/* * The state machine needs some refinement. It is only used to track * invalid STOP commands for the moment. */ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { bus->cmd &= ~0xFFFF; bus->cmd |= value & 0xFFFF; if (bus->cmd & I2CD_M_START_CMD) { uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? I2CD_MSTARTR : I2CD_MSTART; aspeed_i2c_set_state(bus, state); if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7), extract32(bus->buf, 0, 1))) { bus->intr_status |= I2CD_INTR_TX_NAK; } else { bus->intr_status |= I2CD_INTR_TX_ACK; } /* START command is also a TX command, as the slave address is * sent on the bus */ bus->cmd &= ~(I2CD_M_START_CMD | I2CD_M_TX_CMD); /* No slave found */ if (!i2c_bus_busy(bus->bus)) { return; } aspeed_i2c_set_state(bus, I2CD_MACTIVE); } if (bus->cmd & I2CD_M_TX_CMD) { aspeed_i2c_set_state(bus, I2CD_MTXD); if (i2c_send(bus->bus, bus->buf)) { bus->intr_status |= (I2CD_INTR_TX_NAK); i2c_end_transfer(bus->bus); } else { bus->intr_status |= I2CD_INTR_TX_ACK; } bus->cmd &= ~I2CD_M_TX_CMD; aspeed_i2c_set_state(bus, I2CD_MACTIVE); } if ((bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) && !(bus->intr_status & I2CD_INTR_RX_DONE)) { aspeed_i2c_handle_rx_cmd(bus); } if (bus->cmd & I2CD_M_STOP_CMD) { if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); bus->intr_status |= I2CD_INTR_ABNORMAL; } else { aspeed_i2c_set_state(bus, I2CD_MSTOP); i2c_end_transfer(bus->bus); bus->intr_status |= I2CD_INTR_NORMAL_STOP; } bus->cmd &= ~I2CD_M_STOP_CMD; aspeed_i2c_set_state(bus, I2CD_IDLE); } }
void smbus_write_byte(i2c_bus *bus, int addr, uint8_t command, uint8_t data) { i2c_start_transfer(bus, addr, 0); i2c_send(bus, command); i2c_send(bus, data); i2c_end_transfer(bus); }
static void i2c_enter_stop(i2c_interface *i2c) { if (i2c->current_addr >= 0) i2c_end_transfer(i2c->bus); i2c->current_addr = -1; i2c->state = STOPPED; }
static int pca954x_event(I2CSlave *i2c, enum i2c_event event) { PCA954XState *s = PCA954X(i2c); int i; s->event = event; for (i = 0; i < s->lanes; ++i) { if (s->active_lanes & (1 << i)) { switch (event) { /* defer START conditions until we have an address */ case I2C_START_SEND: case I2C_START_RECV: break; /* Forward others to sub busses */ case I2C_FINISH: if (!s->control_decoded) { DB_PRINT("stopping active bus %d\n", i); i2c_end_transfer(s->busses[i]); } break; case I2C_NACK: if (!s->control_decoded) { DB_PRINT("nacking active bus %d\n", i); i2c_nack(s->busses[i]); } break; } } } return 0; }
void smbus_write_word(i2c_bus *bus, int addr, uint8_t command, uint16_t data) { i2c_start_transfer(bus, addr, 0); i2c_send(bus, command); i2c_send(bus, data & 0xff); i2c_send(bus, data >> 8); i2c_end_transfer(bus); }
static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) { DPRINTF("STOP\n"); if (i2c->current_addr >= 0) i2c_end_transfer(i2c->bus); i2c->current_addr = -1; i2c->state = STOPPED; }
uint8_t smbus_receive_byte(i2c_bus *bus, int addr) { uint8_t data; i2c_start_transfer(bus, addr, 1); data = i2c_recv(bus); i2c_nack(bus); i2c_end_transfer(bus); return data; }
uint8_t smbus_read_byte(i2c_bus *bus, int addr, uint8_t command) { uint8_t data; i2c_start_transfer(bus, addr, 0); i2c_send(bus, command); i2c_start_transfer(bus, addr, 1); data = i2c_recv(bus); i2c_nack(bus); i2c_end_transfer(bus); return data; }
uint16_t smbus_read_word(i2c_bus *bus, uint8_t addr, uint8_t command) { uint16_t data; i2c_start_transfer(bus, addr, 0); i2c_send(bus, command); i2c_start_transfer(bus, addr, 1); data = i2c_recv(bus); data |= i2c_recv(bus) << 8; i2c_nack(bus); i2c_end_transfer(bus); return data; }
static void aspeed_i2c_reset(DeviceState *dev) { int i; AspeedI2CState *s = ASPEED_I2C(dev); s->intr_status = 0; for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { s->busses[i].intr_ctrl = 0; s->busses[i].intr_status = 0; s->busses[i].cmd = 0; s->busses[i].buf = 0; i2c_end_transfer(s->busses[i].bus); } }
void smbus_write_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data, int len) { int i; if (len > 32) len = 32; i2c_start_transfer(bus, addr, 0); i2c_send(bus, command); i2c_send(bus, len); for (i = 0; i < len; i++) i2c_send(bus, data[i]); i2c_end_transfer(bus); }
static void imx_i2c_reset(DeviceState *dev) { IMXI2CState *s = IMX_I2C(dev); if (s->address != ADDR_RESET) { i2c_end_transfer(s->bus); } s->address = ADDR_RESET; s->iadr = IADR_RESET; s->ifdr = IFDR_RESET; s->i2cr = I2CR_RESET; s->i2sr = I2SR_RESET; s->i2dr_read = I2DR_RESET; s->i2dr_write = I2DR_RESET; }
int smbus_read_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data) { int len; int i; i2c_start_transfer(bus, addr, 0); i2c_send(bus, command); i2c_start_transfer(bus, addr, 1); len = i2c_recv(bus); if (len > 32) len = 0; for (i = 0; i < len; i++) data[i] = i2c_recv(bus); i2c_nack(bus); i2c_end_transfer(bus); return len; }
void smbus_send_byte(i2c_bus *bus, int addr, uint8_t data) { i2c_start_transfer(bus, addr, 0); i2c_send(bus, data); i2c_end_transfer(bus); }
static void imx_i2c_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXI2CState *s = IMX_I2C(opaque); DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n", imx_i2c_get_regname(offset), offset, (int)value); value &= 0xff; switch (offset) { case IADR_ADDR: s->iadr = value & IADR_MASK; /* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */ break; case IFDR_ADDR: s->ifdr = value & IFDR_MASK; break; case I2CR_ADDR: if (imx_i2c_is_enabled(s) && ((value & I2CR_IEN) == 0)) { /* This is a soft reset. IADR is preserved during soft resets */ uint16_t iadr = s->iadr; imx_i2c_reset(DEVICE(s)); s->iadr = iadr; } else { /* normal write */ s->i2cr = value & I2CR_MASK; if (imx_i2c_is_master(s)) { /* set the bus to busy */ s->i2sr |= I2SR_IBB; } else { /* slave mode */ /* bus is not busy anymore */ s->i2sr &= ~I2SR_IBB; /* * if we unset the master mode then it ends the ongoing * transfer if any */ if (s->address != ADDR_RESET) { i2c_end_transfer(s->bus); s->address = ADDR_RESET; } } if (s->i2cr & I2CR_RSTA) { /* Restart */ /* if this is a restart then it ends the ongoing transfer */ if (s->address != ADDR_RESET) { i2c_end_transfer(s->bus); s->address = ADDR_RESET; s->i2cr &= ~I2CR_RSTA; } } } break; case I2SR_ADDR: /* * if the user writes 0 to IIF then lower the interrupt and * reset the bit */ if ((s->i2sr & I2SR_IIF) && !(value & I2SR_IIF)) { s->i2sr &= ~I2SR_IIF; qemu_irq_lower(s->irq); } /* * if the user writes 0 to IAL, reset the bit */ if ((s->i2sr & I2SR_IAL) && !(value & I2SR_IAL)) { s->i2sr &= ~I2SR_IAL; } break; case I2DR_ADDR: /* if the device is not enabled, nothing to do */ if (!imx_i2c_is_enabled(s)) { break; } s->i2dr_write = value & I2DR_MASK; if (imx_i2c_is_master(s)) { /* If this is the first write cycle then it is the slave addr */ if (s->address == ADDR_RESET) { if (i2c_start_transfer(s->bus, extract32(s->i2dr_write, 1, 7), extract32(s->i2dr_write, 0, 1))) { /* if non zero is returned, the adress is not valid */ s->i2sr |= I2SR_RXAK; } else { s->address = s->i2dr_write; s->i2sr &= ~I2SR_RXAK; imx_i2c_raise_interrupt(s); } } else { /* This is a normal data write */ if (i2c_send(s->bus, s->i2dr_write)) { /* if the target return non zero then end the transfer */ s->i2sr |= I2SR_RXAK; s->address = ADDR_RESET; i2c_end_transfer(s->bus); } else { s->i2sr &= ~I2SR_RXAK; imx_i2c_raise_interrupt(s); } } } else { qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", TYPE_IMX_I2C, __func__); } break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset); break; } }
/* Master device commands. */ void smbus_quick_command(I2CBus *bus, uint8_t addr, int read) { i2c_start_transfer(bus, addr, read); i2c_end_transfer(bus); }
AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, uint8_t len, uint8_t *data) { AUXReply ret = AUX_NACK; I2CBus *i2c_bus = aux_get_i2c_bus(bus); size_t i; bool is_write = false; DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address, cmd, len); switch (cmd) { /* * Forward the request on the AUX bus.. */ case WRITE_AUX: case READ_AUX: is_write = cmd == READ_AUX ? false : true; for (i = 0; i < len; i++) { if (!address_space_rw(&bus->aux_addr_space, address++, MEMTXATTRS_UNSPECIFIED, data++, 1, is_write)) { ret = AUX_I2C_ACK; } else { ret = AUX_NACK; break; } } break; /* * Classic I2C transactions.. */ case READ_I2C: case WRITE_I2C: is_write = cmd == READ_I2C ? false : true; if (i2c_bus_busy(i2c_bus)) { i2c_end_transfer(i2c_bus); } if (i2c_start_transfer(i2c_bus, address, is_write)) { ret = AUX_I2C_NACK; break; } ret = AUX_I2C_ACK; while (len > 0) { if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { ret = AUX_I2C_NACK; break; } len--; } i2c_end_transfer(i2c_bus); break; /* * I2C MOT transactions. * * Here we send a start when: * - We didn't start transaction yet. * - We had a READ and we do a WRITE. * - We changed the address. */ case WRITE_I2C_MOT: case READ_I2C_MOT: is_write = cmd == READ_I2C_MOT ? false : true; ret = AUX_I2C_NACK; if (!i2c_bus_busy(i2c_bus)) { /* * No transactions started.. */ if (i2c_start_transfer(i2c_bus, address, is_write)) { break; } } else if ((address != bus->last_i2c_address) || (bus->last_transaction != cmd)) { /* * Transaction started but we need to restart.. */ i2c_end_transfer(i2c_bus); if (i2c_start_transfer(i2c_bus, address, is_write)) { break; } } bus->last_transaction = cmd; bus->last_i2c_address = address; while (len > 0) { if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { i2c_end_transfer(i2c_bus); break; } len--; } if (len == 0) { ret = AUX_I2C_ACK; } break; default: DPRINTF("Not implemented!\n"); return AUX_NACK; } DPRINTF("reply: %u\n", ret); return ret; }
/* Master device commands. */ void smbus_quick_command(i2c_bus *bus, int addr, int read) { i2c_start_transfer(bus, addr, read); i2c_end_transfer(bus); }