i2c: Allow I2C devices to NAK start events
Add a return value to the event handler. Some I2C devices will NAK if they have no data, so allow them to do this. This required the following changes: Go through all the event handlers and change them to return int and return 0. Modify i2c_start_transfer to terminate the transaction on a NAK. Modify smbus handing to not assert if a NAK occurs on a second operation, and terminate the transaction and return -1 instead. Add some information on semantics to I2CSlaveClass. Signed-off-by: Corey Minyard <cminyard@mvista.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
ffe22bf510
commit
d307c28ca9
@ -1258,7 +1258,7 @@ static void pxa2xx_i2c_update(PXA2xxI2CState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* These are only stubs now. */
|
/* These are only stubs now. */
|
||||||
static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
|
PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
|
||||||
PXA2xxI2CState *s = slave->host;
|
PXA2xxI2CState *s = slave->host;
|
||||||
@ -1280,6 +1280,8 @@ static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pxa2xx_i2c_update(s);
|
pxa2xx_i2c_update(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_i2c_rx(I2CSlave *i2c)
|
static int pxa2xx_i2c_rx(I2CSlave *i2c)
|
||||||
|
@ -172,7 +172,7 @@ static int tosa_dac_send(I2CSlave *i2c, uint8_t data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
|
static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
TosaDACState *s = TOSA_DAC(i2c);
|
TosaDACState *s = TOSA_DAC(i2c);
|
||||||
|
|
||||||
@ -194,6 +194,8 @@ static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tosa_dac_recv(I2CSlave *s)
|
static int tosa_dac_recv(I2CSlave *s)
|
||||||
|
@ -220,7 +220,7 @@ static int aer915_send(I2CSlave *i2c, uint8_t data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aer915_event(I2CSlave *i2c, enum i2c_event event)
|
static int aer915_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
AER915State *s = AER915(i2c);
|
AER915State *s = AER915(i2c);
|
||||||
|
|
||||||
@ -238,6 +238,8 @@ static void aer915_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int aer915_recv(I2CSlave *slave)
|
static int aer915_recv(I2CSlave *slave)
|
||||||
|
@ -303,7 +303,7 @@ static void wm8750_reset(I2CSlave *i2c)
|
|||||||
s->i2c_len = 0;
|
s->i2c_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
|
static int wm8750_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
WM8750State *s = WM8750(i2c);
|
WM8750State *s = WM8750(i2c);
|
||||||
|
|
||||||
@ -321,6 +321,8 @@ static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WM8750_LINVOL 0x00
|
#define WM8750_LINVOL 0x00
|
||||||
|
@ -179,7 +179,7 @@ static int ssd0303_send(I2CSlave *i2c, uint8_t data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssd0303_event(I2CSlave *i2c, enum i2c_event event)
|
static int ssd0303_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
ssd0303_state *s = SSD0303(i2c);
|
ssd0303_state *s = SSD0303(i2c);
|
||||||
|
|
||||||
@ -193,6 +193,8 @@ static void ssd0303_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
/* Nothing to do. */
|
/* Nothing to do. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssd0303_update_display(void *opaque)
|
static void ssd0303_update_display(void *opaque)
|
||||||
|
@ -129,7 +129,7 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void max7310_event(I2CSlave *i2c, enum i2c_event event)
|
static int max7310_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
MAX7310State *s = MAX7310(i2c);
|
MAX7310State *s = MAX7310(i2c);
|
||||||
s->len = 0;
|
s->len = 0;
|
||||||
@ -147,6 +147,8 @@ static void max7310_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_max7310 = {
|
static const VMStateDescription vmstate_max7310 = {
|
||||||
|
@ -88,18 +88,26 @@ int i2c_bus_busy(I2CBus *bus)
|
|||||||
return !QLIST_EMPTY(&bus->current_devs);
|
return !QLIST_EMPTY(&bus->current_devs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns non-zero if the address is not valid. If this is called
|
|
||||||
* again without an intervening i2c_end_transfer(), like in the SMBus
|
|
||||||
* case where the operation is switched from write to read, this
|
|
||||||
* function will not rescan the bus and thus cannot fail.
|
|
||||||
*/
|
|
||||||
/* TODO: Make this handle multiple masters. */
|
/* TODO: Make this handle multiple masters. */
|
||||||
|
/*
|
||||||
|
* Start or continue an i2c transaction. When this is called for the
|
||||||
|
* first time or after an i2c_end_transfer(), if it returns an error
|
||||||
|
* the bus transaction is terminated (or really never started). If
|
||||||
|
* this is called after another i2c_start_transfer() without an
|
||||||
|
* intervening i2c_end_transfer(), and it returns an error, the
|
||||||
|
* transaction will not be terminated. The caller must do it.
|
||||||
|
*
|
||||||
|
* This corresponds with the way real hardware works. The SMBus
|
||||||
|
* protocol uses a start transfer to switch from write to read mode
|
||||||
|
* without releasing the bus. If that fails, the bus is still
|
||||||
|
* in a transaction.
|
||||||
|
*/
|
||||||
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;
|
||||||
I2CSlaveClass *sc;
|
I2CSlaveClass *sc;
|
||||||
I2CNode *node;
|
I2CNode *node;
|
||||||
|
bool bus_scanned = false;
|
||||||
|
|
||||||
if (address == I2C_BROADCAST) {
|
if (address == I2C_BROADCAST) {
|
||||||
/*
|
/*
|
||||||
@ -130,6 +138,7 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bus_scanned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QLIST_EMPTY(&bus->current_devs)) {
|
if (QLIST_EMPTY(&bus->current_devs)) {
|
||||||
@ -137,11 +146,21 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(node, &bus->current_devs, next) {
|
QLIST_FOREACH(node, &bus->current_devs, next) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
sc = I2C_SLAVE_GET_CLASS(node->elt);
|
sc = I2C_SLAVE_GET_CLASS(node->elt);
|
||||||
/* If the bus is already busy, assume this is a repeated
|
/* If the bus is already busy, assume this is a repeated
|
||||||
start condition. */
|
start condition. */
|
||||||
|
|
||||||
if (sc->event) {
|
if (sc->event) {
|
||||||
sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND);
|
rv = sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND);
|
||||||
|
if (rv && !bus->broadcast) {
|
||||||
|
if (bus_scanned) {
|
||||||
|
/* First call, terminate the transfer. */
|
||||||
|
i2c_end_transfer(bus);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -230,13 +230,15 @@ static void i2c_ddc_reset(DeviceState *ds)
|
|||||||
s->reg = 0;
|
s->reg = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
|
static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
I2CDDCState *s = I2CDDC(i2c);
|
I2CDDCState *s = I2CDDC(i2c);
|
||||||
|
|
||||||
if (event == I2C_START_SEND) {
|
if (event == I2C_START_SEND) {
|
||||||
s->firstbyte = true;
|
s->firstbyte = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_ddc_rx(I2CSlave *i2c)
|
static int i2c_ddc_rx(I2CSlave *i2c)
|
||||||
|
@ -67,7 +67,7 @@ static void smbus_do_write(SMBusDevice *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smbus_i2c_event(I2CSlave *s, enum i2c_event event)
|
static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
|
||||||
{
|
{
|
||||||
SMBusDevice *dev = SMBUS_DEVICE(s);
|
SMBusDevice *dev = SMBUS_DEVICE(s);
|
||||||
|
|
||||||
@ -148,6 +148,8 @@ static void smbus_i2c_event(I2CSlave *s, enum i2c_event event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smbus_i2c_recv(I2CSlave *s)
|
static int smbus_i2c_recv(I2CSlave *s)
|
||||||
@ -249,7 +251,8 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
|
|||||||
}
|
}
|
||||||
i2c_send(bus, command);
|
i2c_send(bus, command);
|
||||||
if (i2c_start_transfer(bus, addr, 1)) {
|
if (i2c_start_transfer(bus, addr, 1)) {
|
||||||
assert(0);
|
i2c_end_transfer(bus);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
data = i2c_recv(bus);
|
data = i2c_recv(bus);
|
||||||
i2c_nack(bus);
|
i2c_nack(bus);
|
||||||
@ -276,7 +279,8 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
|
|||||||
}
|
}
|
||||||
i2c_send(bus, command);
|
i2c_send(bus, command);
|
||||||
if (i2c_start_transfer(bus, addr, 1)) {
|
if (i2c_start_transfer(bus, addr, 1)) {
|
||||||
assert(0);
|
i2c_end_transfer(bus);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
data = i2c_recv(bus);
|
data = i2c_recv(bus);
|
||||||
data |= i2c_recv(bus) << 8;
|
data |= i2c_recv(bus) << 8;
|
||||||
@ -307,7 +311,8 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data)
|
|||||||
}
|
}
|
||||||
i2c_send(bus, command);
|
i2c_send(bus, command);
|
||||||
if (i2c_start_transfer(bus, addr, 1)) {
|
if (i2c_start_transfer(bus, addr, 1)) {
|
||||||
assert(0);
|
i2c_end_transfer(bus);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
len = i2c_recv(bus);
|
len = i2c_recv(bus);
|
||||||
if (len > 32) {
|
if (len > 32) {
|
||||||
|
@ -383,7 +383,7 @@ static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
LM823KbdState *s = LM8323(i2c);
|
LM823KbdState *s = LM8323(i2c);
|
||||||
|
|
||||||
@ -397,6 +397,8 @@ static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lm_i2c_rx(I2CSlave *i2c)
|
static int lm_i2c_rx(I2CSlave *i2c)
|
||||||
|
@ -176,7 +176,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmp105_event(I2CSlave *i2c, enum i2c_event event)
|
static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
TMP105State *s = TMP105(i2c);
|
TMP105State *s = TMP105(i2c);
|
||||||
|
|
||||||
@ -185,6 +185,7 @@ static void tmp105_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->len = 0;
|
s->len = 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tmp105_post_load(void *opaque, int version_id)
|
static int tmp105_post_load(void *opaque, int version_id)
|
||||||
|
@ -94,7 +94,7 @@ static void inc_regptr(DS1338State *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
|
static int ds1338_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
DS1338State *s = DS1338(i2c);
|
DS1338State *s = DS1338(i2c);
|
||||||
|
|
||||||
@ -113,6 +113,8 @@ static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds1338_recv(I2CSlave *i2c)
|
static int ds1338_recv(I2CSlave *i2c)
|
||||||
|
@ -713,12 +713,14 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void menelaus_event(I2CSlave *i2c, enum i2c_event event)
|
static int menelaus_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
MenelausState *s = TWL92230(i2c);
|
MenelausState *s = TWL92230(i2c);
|
||||||
|
|
||||||
if (event == I2C_START_SEND)
|
if (event == I2C_START_SEND)
|
||||||
s->firstbyte = 1;
|
s->firstbyte = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int menelaus_tx(I2CSlave *i2c, uint8_t data)
|
static int menelaus_tx(I2CSlave *i2c, uint8_t data)
|
||||||
|
@ -32,14 +32,22 @@ typedef struct I2CSlaveClass
|
|||||||
/* Callbacks provided by the device. */
|
/* Callbacks provided by the device. */
|
||||||
int (*init)(I2CSlave *dev);
|
int (*init)(I2CSlave *dev);
|
||||||
|
|
||||||
/* Master to slave. */
|
/* Master to slave. Returns non-zero for a NAK, 0 for success. */
|
||||||
int (*send)(I2CSlave *s, uint8_t data);
|
int (*send)(I2CSlave *s, uint8_t data);
|
||||||
|
|
||||||
/* Slave to master. */
|
/*
|
||||||
|
* Slave to master. This cannot fail, the device should always
|
||||||
|
* return something here. Negative values probably result in 0xff
|
||||||
|
* and a possible log from the driver, and shouldn't be used.
|
||||||
|
*/
|
||||||
int (*recv)(I2CSlave *s);
|
int (*recv)(I2CSlave *s);
|
||||||
|
|
||||||
/* Notify the slave of a bus state change. */
|
/*
|
||||||
void (*event)(I2CSlave *s, enum i2c_event event);
|
* Notify the slave of a bus state change. For start event,
|
||||||
|
* returns non-zero to NAK an operation. For other events the
|
||||||
|
* return code is not used and should be zero.
|
||||||
|
*/
|
||||||
|
int (*event)(I2CSlave *s, enum i2c_event event);
|
||||||
} I2CSlaveClass;
|
} I2CSlaveClass;
|
||||||
|
|
||||||
struct I2CSlave
|
struct I2CSlave
|
||||||
|
Loading…
Reference in New Issue
Block a user