/* I2C bus common (driver-independent) primitives implementation.
*
* Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia
* Author: Victor V. Vengerov <vvv@oktet.ru>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
*
* http://www.rtems.com/license/LICENSE.
*
* @(#) $Id$
*/
#include <bsp.h>
#include <i2c.h>
/* i2c_transfer_sema_done_func --
* This function called from I2C driver layer to signal that I2C
* transfer is finished. This function resumes of task execution which
* has invoked blocking I2C primitive.
*
* PARAMETERS:
* arg - done function argument; it is RTEMS semaphore ID.
*/
static void
i2c_transfer_sema_done_func(uint32_t arg)
{
rtems_id sema = (rtems_id)arg;
rtems_semaphore_release(sema);
}
/* i2c_transfer_poll_done_func --
* This function called from I2C driver layer to signal that I2C
* transfer is finished. This function set the flag polled by waiting
* function.
*
* PARAMETERS:
* arg - done function argument; address of poll_done_flag
*/
static void
i2c_transfer_poll_done_func(uint32_t arg)
{
rtems_boolean *poll_done_flag = (rtems_boolean *)arg;
*poll_done_flag = 1;
}
/* i2c_transfer_wait_sema --
* Initiate I2C bus transfer and block on temporary created semaphore
* until this transfer will be finished.
*
* PARAMETERS:
* bus - I2C bus number
* msg - pointer to transfer messages array
* nmsg - number of messages in transfer
*
* RETURNS:
* RTEMS_SUCCESSFUL, if tranfer finished successfully,
* or RTEMS status code if semaphore operations has failed.
*/
static i2c_message_status
i2c_transfer_wait_sema(i2c_bus_number bus, i2c_message *msg, int nmsg)
{
rtems_status_code sc;
rtems_id sema;
sc = rtems_semaphore_create(
rtems_build_name('I', '2', 'C', 'S'),
0,
RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
0,
&sema
);
if (sc != RTEMS_SUCCESSFUL)
return I2C_RESOURCE_NOT_AVAILABLE;
sc = i2c_transfer(bus, nmsg, msg, i2c_transfer_sema_done_func, sema);
if (sc != RTEMS_SUCCESSFUL)
{
rtems_semaphore_delete(sema);
return sc;
}
rtems_semaphore_obtain(sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
sc = rtems_semaphore_delete(sema);
return sc;
}
/* i2c_transfer_wait_poll --
* Initiate I2C bus transfer and wait by poll transaction done flag until
* this transfer will be finished.
*
* PARAMETERS:
* bus - I2C bus number
* msg - pointer to transfer messages array
* nmsg - number of messages in transfer
*
* RETURNS:
* RTEMS_SUCCESSFUL
*/
static rtems_status_code
i2c_transfer_wait_poll(i2c_bus_number bus, i2c_message *msg, int nmsg)
{
volatile rtems_boolean poll_done_flag;
rtems_status_code sc;
poll_done_flag = 0;
sc = i2c_transfer(bus, nmsg, msg, i2c_transfer_poll_done_func,
(uint32_t)&poll_done_flag);
if (sc != RTEMS_SUCCESSFUL)
return sc;
while (poll_done_flag == 0)
{
i2c_poll(bus);
}
return RTEMS_SUCCESSFUL;
}
/* i2c_transfer_wait --
* Initiate I2C bus transfer and block until this transfer will be
* finished. This function wait the semaphore if system in
* SYSTEM_STATE_UP state, or poll done flag in other states.
*
* PARAMETERS:
* bus - I2C bus number
* msg - pointer to transfer messages array
* nmsg - number of messages in transfer
*
* RETURNS:
* I2C_SUCCESSFUL, if tranfer finished successfully,
* I2C_RESOURCE_NOT_AVAILABLE, if semaphore operations has failed,
* value of status field of first error-finished message in transfer,
* if something wrong.
*/
i2c_message_status
i2c_transfer_wait(i2c_bus_number bus, i2c_message *msg, int nmsg)
{
rtems_status_code sc;
int i;
if (_System_state_Is_up(_System_state_Get()))
{
sc = i2c_transfer_wait_sema(bus, msg, nmsg);
}
else
{
sc = i2c_transfer_wait_poll(bus, msg, nmsg);
}
if (sc != RTEMS_SUCCESSFUL)
return I2C_RESOURCE_NOT_AVAILABLE;
for (i = 0; i < nmsg; i++)
{
if (msg[i].status != I2C_SUCCESSFUL)
{
return msg[i].status;
}
}
return I2C_SUCCESSFUL;
}
/* i2c_write --
* Send single message over specified I2C bus to addressed device and
* wait while transfer is finished.
*
* PARAMETERS:
* bus - I2C bus number
* addr - address of I2C device
* buf - data to be sent to device
* size - data buffer size
*
* RETURNS:
* transfer status
*/
i2c_message_status
i2c_write(i2c_bus_number bus, i2c_address addr, void *buf, int size)
{
i2c_message msg;
msg.addr = addr;
msg.flags = I2C_MSG_WR;
if (addr > 0xff)
msg.flags |= I2C_MSG_ADDR_10;
msg.status = 0;
msg.len = size;
msg.buf = buf;
return i2c_transfer_wait(bus, &msg, 1);
}
/* i2c_wrbyte --
* Send single one-byte long message over specified I2C bus to
* addressed device and wait while transfer is finished.
*
* PARAMETERS:
* bus - I2C bus number
* addr - address of I2C device
* cmd - byte message to be sent to device
*
* RETURNS:
* transfer status
*/
i2c_message_status
i2c_wrbyte(i2c_bus_number bus, i2c_address addr, uint8_t cmd)
{
i2c_message msg;
uint8_t data = cmd;
msg.addr = addr;
msg.flags = I2C_MSG_WR;
if (addr > 0xff)
msg.flags |= I2C_MSG_ADDR_10;
msg.status = 0;
msg.len = sizeof(data);
msg.buf = &data;
return i2c_transfer_wait(bus, &msg, 1);
}
/* i2c_read --
* receive single message over specified I2C bus from addressed device.
* This call will wait while transfer is finished.
*
* PARAMETERS:
* bus - I2C bus number
* addr - address of I2C device
* buf - buffer for received message
* size - receive buffer size
*
* RETURNS:
* transfer status
*/
i2c_message_status
i2c_read(i2c_bus_number bus, i2c_address addr, void *buf, int size)
{
i2c_message msg;
msg.addr = addr;
msg.flags = 0;
if (addr > 0xff)
msg.flags |= I2C_MSG_ADDR_10;
msg.status = 0;
msg.len = size;
msg.buf = buf;
return i2c_transfer_wait(bus, &msg, 1);
}
/* i2c_wrrd --
* Send message over I2C bus to specified device and receive message
* from the same device during single transfer.
*
* PARAMETERS:
* bus - I2C bus number
* addr - address of I2C device
* bufw - data to be sent to device
* sizew - send data buffer size
* bufr - buffer for received message
* sizer - receive buffer size
*
* RETURNS:
* transfer status
*/
i2c_message_status
i2c_wrrd(i2c_bus_number bus, i2c_address addr, void *bufw, int sizew,
void *bufr, int sizer)
{
i2c_message msg[2];
msg[0].addr = addr;
msg[0].flags = I2C_MSG_WR | I2C_MSG_ERRSKIP;
if (addr > 0xff)
msg[0].flags |= I2C_MSG_ADDR_10;
msg[0].status = 0;
msg[0].len = sizew;
msg[0].buf = bufw;
msg[1].addr = addr;
msg[1].flags = 0;
if (addr > 0xff)
msg[1].flags |= I2C_MSG_ADDR_10;
msg[1].status = 0;
msg[1].len = sizer;
msg[1].buf = bufr;
return i2c_transfer_wait(bus, msg, 2);
}
/* i2c_wbrd --
* Send one-byte message over I2C bus to specified device and receive
* message from the same device during single transfer.
*
* PARAMETERS:
* bus - I2C bus number
* addr - address of I2C device
* cmd - one-byte message to be sent over I2C bus
* bufr - buffer for received message
* sizer - receive buffer size
*
* RETURNS:
* transfer status
*/
i2c_message_status
i2c_wbrd(i2c_bus_number bus, i2c_address addr, uint8_t cmd,
void *bufr, int sizer)
{
i2c_message msg[2];
uint8_t bufw = cmd;
msg[0].addr = addr;
msg[0].flags = I2C_MSG_WR | I2C_MSG_ERRSKIP;
if (addr > 0xff)
msg[0].flags |= I2C_MSG_ADDR_10;
msg[0].status = 0;
msg[0].len = sizeof(bufw);
msg[0].buf = &bufw;
msg[1].addr = addr;
msg[1].flags = I2C_MSG_ERRSKIP;
if (addr > 0xff)
msg[1].flags |= I2C_MSG_ADDR_10;
msg[1].status = 0;
msg[1].len = sizer;
msg[1].buf = bufr;
return i2c_transfer_wait(bus, msg, 2);
}