@ -10,11 +10,19 @@
# include "qemu/osdep.h"
# include "qemu/osdep.h"
# include "hw/i2c/i2c.h"
# include "hw/i2c/i2c.h"
typedef struct I2CNode I2CNode ;
struct I2CNode {
I2CSlave * elt ;
QLIST_ENTRY ( I2CNode ) next ;
} ;
struct I2CBus
struct I2CBus
{
{
BusState qbus ;
BusState qbus ;
I2CSlave * current_dev ;
QLIST_HEAD ( , I2CNode ) current_devs ;
uint8_t saved_address ;
uint8_t saved_address ;
bool broadcast ;
} ;
} ;
static Property i2c_props [ ] = {
static Property i2c_props [ ] = {
@ -35,17 +43,12 @@ static void i2c_bus_pre_save(void *opaque)
{
{
I2CBus * bus = opaque ;
I2CBus * bus = opaque ;
bus - > saved_address = bus - > current_dev ? bus - > current_dev - > address : - 1 ;
bus - > saved_address = - 1 ;
}
if ( ! QLIST_EMPTY ( & bus - > current_devs ) ) {
if ( ! bus - > broadcast ) {
static int i2c_bus_post_load ( void * opaque , int version_id )
bus - > saved_address = QLIST_FIRST ( & bus - > current_devs ) - > elt - > address ;
{
}
I2CBus * bus = opaque ;
}
/* The bus is loaded before attached devices, so load and save the
current device id . Devices will check themselves as loaded . */
bus - > current_dev = NULL ;
return 0 ;
}
}
static const VMStateDescription vmstate_i2c_bus = {
static const VMStateDescription vmstate_i2c_bus = {
@ -53,9 +56,9 @@ static const VMStateDescription vmstate_i2c_bus = {
. version_id = 1 ,
. version_id = 1 ,
. minimum_version_id = 1 ,
. minimum_version_id = 1 ,
. pre_save = i2c_bus_pre_save ,
. pre_save = i2c_bus_pre_save ,
. post_load = i2c_bus_post_load ,
. fields = ( VMStateField [ ] ) {
. fields = ( VMStateField [ ] ) {
VMSTATE_UINT8 ( saved_address , I2CBus ) ,
VMSTATE_UINT8 ( saved_address , I2CBus ) ,
VMSTATE_BOOL ( broadcast , I2CBus ) ,
VMSTATE_END_OF_LIST ( )
VMSTATE_END_OF_LIST ( )
}
}
} ;
} ;
@ -66,6 +69,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name)
I2CBus * bus ;
I2CBus * bus ;
bus = I2C_BUS ( qbus_create ( TYPE_I2C_BUS , parent , name ) ) ;
bus = I2C_BUS ( qbus_create ( TYPE_I2C_BUS , parent , name ) ) ;
QLIST_INIT ( & bus - > current_devs ) ;
vmstate_register ( NULL , - 1 , & vmstate_i2c_bus , bus ) ;
vmstate_register ( NULL , - 1 , & vmstate_i2c_bus , bus ) ;
return bus ;
return bus ;
}
}
@ -78,7 +82,7 @@ void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
/* Return nonzero if bus is busy. */
/* Return nonzero if bus is busy. */
int i2c_bus_busy ( I2CBus * bus )
int i2c_bus_busy ( I2CBus * bus )
{
{
return bus - > current_dev ! = NULL ;
return ! QLIST_EMPTY ( & bus - > current_devs ) ;
}
}
/* Returns non-zero if the address is not valid. */
/* Returns non-zero if the address is not valid. */
@ -86,95 +90,111 @@ int i2c_bus_busy(I2CBus *bus)
int i2c_start_transfer ( I2CBus * bus , uint8_t address , int recv )
int i2c_start_transfer ( I2CBus * bus , uint8_t address , int recv )
{
{
BusChild * kid ;
BusChild * kid ;
I2CSlave * slave = NULL ;
I2CSlaveClass * sc ;
I2CSlaveClass * sc ;
I2CNode * node ;
if ( address = = 0x00 ) {
/*
* This is a broadcast , the current_devs will be all the devices of the
* bus .
*/
bus - > broadcast = true ;
}
QTAILQ_FOREACH ( kid , & bus - > qbus . children , sibling ) {
QTAILQ_FOREACH ( kid , & bus - > qbus . children , sibling ) {
DeviceState * qdev = kid - > child ;
DeviceState * qdev = kid - > child ;
I2CSlave * candidate = I2C_SLAVE ( qdev ) ;
I2CSlave * candidate = I2C_SLAVE ( qdev ) ;
if ( candidate - > address = = address ) {
if ( ( candidate - > address = = address ) | | ( bus - > broadcast ) ) {
slave = candidate ;
node = g_malloc ( sizeof ( struct I2CNode ) ) ;
break ;
node - > elt = candidate ;
QLIST_INSERT_HEAD ( & bus - > current_devs , node , next ) ;
if ( ! bus - > broadcast ) {
break ;
}
}
}
}
}
if ( ! slave ) {
if ( QLIST_EMPTY ( & bus - > current_devs ) ) {
return 1 ;
return 1 ;
}
}
sc = I2C_SLAVE_GET_CLASS ( slave ) ;
QLIST_FOREACH ( node , & bus - > current_devs , next ) {
/* If the bus is already busy, assume this is a repeated
sc = I2C_SLAVE_GET_CLASS ( node - > elt ) ;
start condition . */
/* If the bus is already busy, assume this is a repeated
bus - > current_dev = slave ;
start condition . */
if ( sc - > event ) {
if ( sc - > event ) {
sc - > event ( slave , recv ? I2C_START_RECV : I2C_START_SEND ) ;
sc - > event ( node - > elt , recv ? I2C_START_RECV : I2C_START_SEND ) ;
}
}
}
return 0 ;
return 0 ;
}
}
void i2c_end_transfer ( I2CBus * bus )
void i2c_end_transfer ( I2CBus * bus )
{
{
I2CSlave * dev = bus - > current_dev ;
I2CSlaveClass * sc ;
I2CSlaveClass * sc ;
I2CNode * node , * next ;
if ( ! dev ) {
if ( QLIST_EMPTY ( & bus - > current_devs ) ) {
return ;
return ;
}
}
sc = I2C_SLAVE_GET_CLASS ( dev ) ;
QLIST_FOREACH_SAFE ( node , & bus - > current_devs , next , next ) {
if ( sc - > event ) {
sc = I2C_SLAVE_GET_CLASS ( node - > elt ) ;
sc - > event ( dev , I2C_FINISH ) ;
if ( sc - > event ) {
sc - > event ( node - > elt , I2C_FINISH ) ;
}
QLIST_REMOVE ( node , next ) ;
g_free ( node ) ;
}
}
bus - > broadcast = false ;
bus - > current_dev = NULL ;
}
}
int i2c_send ( I2CBus * bus , uint8_t data )
int i2c_send ( I2CBus * bus , uint8_t data )
{
{
I2CSlave * dev = bus - > current_dev ;
I2CSlaveClass * sc ;
I2CSlaveClass * sc ;
I2CNode * node ;
if ( ! dev ) {
int ret = 0 ;
return - 1 ;
}
QLIST_FOREACH ( node , & bus - > current_devs , next ) {
sc = I2C_SLAVE_GET_CLASS ( node - > elt ) ;
sc = I2C_SLAVE_GET_CLASS ( dev ) ;
if ( sc - > send ) {
if ( sc - > send ) {
ret = ret | | sc - > send ( node - > elt , data ) ;
return sc - > send ( dev , data ) ;
} else {
ret = - 1 ;
}
}
}
return ret ? - 1 : 0 ;
return - 1 ;
}
}
int i2c_recv ( I2CBus * bus )
int i2c_recv ( I2CBus * bus )
{
{
I2CSlave * dev = bus - > current_dev ;
I2CSlaveClass * sc ;
I2CSlaveClass * sc ;
if ( ! dev ) {
if ( ( QLIST_EMPTY ( & bus - > current_devs ) ) | | ( bus - > broadcast ) ) {
return - 1 ;
return - 1 ;
}
}
sc = I2C_SLAVE_GET_CLASS ( dev ) ;
sc = I2C_SLAVE_GET_CLASS ( QLIST_FIRST ( & bus - > current_ devs ) - > elt ) ;
if ( sc - > recv ) {
if ( sc - > recv ) {
return sc - > recv ( dev ) ;
return sc - > recv ( QLIST_FIRST ( & bus - > current_ devs ) - > elt ) ;
}
}
return - 1 ;
return - 1 ;
}
}
void i2c_nack ( I2CBus * bus )
void i2c_nack ( I2CBus * bus )
{
{
I2CSlave * dev = bus - > current_dev ;
I2CSlaveClass * sc ;
I2CSlaveClass * sc ;
I2CNode * node ;
if ( ! dev ) {
if ( QLIST_EMPTY ( & bus - > current_devs ) ) {
return ;
return ;
}
}
sc = I2C_SLAVE_GET_CLASS ( dev ) ;
QLIST_FOREACH ( node , & bus - > current_devs , next ) {
if ( sc - > event ) {
sc = I2C_SLAVE_GET_CLASS ( node - > elt ) ;
sc - > event ( dev , I2C_NACK ) ;
if ( sc - > event ) {
sc - > event ( node - > elt , I2C_NACK ) ;
}
}
}
}
}
@ -182,9 +202,13 @@ static int i2c_slave_post_load(void *opaque, int version_id)
{
{
I2CSlave * dev = opaque ;
I2CSlave * dev = opaque ;
I2CBus * bus ;
I2CBus * bus ;
I2CNode * node ;
bus = I2C_BUS ( qdev_get_parent_bus ( DEVICE ( dev ) ) ) ;
bus = I2C_BUS ( qdev_get_parent_bus ( DEVICE ( dev ) ) ) ;
if ( bus - > saved_address = = dev - > address ) {
if ( ( bus - > saved_address = = dev - > address ) | | ( bus - > broadcast ) ) {
bus - > current_dev = dev ;
node = g_malloc ( sizeof ( struct I2CNode ) ) ;
node - > elt = dev ;
QLIST_INSERT_HEAD ( & bus - > current_devs , node , next ) ;
}
}
return 0 ;
return 0 ;
}
}