/* * Writes a string to I2C and checks acknowledge * @param addr address of the slave receiver * @param buf the buffer containing the string * @param len buffer length in bytes * @return 0 on success, error code otherwise */ i2cError_t i2cHwWrite(uint8_t busId, uint8_t addr, const void *buf, uint8_t len) { size_t i; int ret = I2C_OK; #define I2C_WRITE_INIT(letterid) \ UC##letterid##CTL1 |= UCSWRST; /* Enable SW reset */ \ UC##letterid##I2CSA = addr; /* Set slave address */ \ UC##letterid##CTL1 &= ~UCSWRST; /* SW reset, resume operation */ \ UC##letterid##CTL1 |= UCTR + UCTXSTT; /* Transmit mode */ \ #define I2C_WRITE_BYTE1(letterid, id, byte) do { \ /* Send data */ \ UC##letterid##TXBUF = byte; \ /* Wait for either transmission clearance or error */ \ while (1) { \ if (UC##letterid##STAT & UCNACKIFG) { \ /* No ack */ \ ret = I2C_ACK_ERROR; \ goto end; \ } \ else if (UC##id##IFG & UC##letterid##TXIFG) { \ break; \ } \ } \ } while (0) // TODO: will not work for B0? #define I2C_WRITE_BYTE(letterid, id, byte) do { \ while (!(UC1IFG & UCB1TXIFG) && !(UCB1STAT & UCNACKIFG)); \ UC##letterid##TXBUF = byte; \ } while (0) // initalize Tx mode #ifdef UCB0CTL0_ if (busId == 0) { I2C_WRITE_INIT(B0); } else { I2C_WRITE_INIT(B1); } #else I2C_WRITE_INIT(B0); #endif // Send all bytes sequentially for (i = 0; i < len; i++) { #ifdef UCB0CTL0_ if (busId == 0) { I2C_WRITE_BYTE(B0, 0, ((const char *)buf)[i]); } else { I2C_WRITE_BYTE(B1, 1, ((const char *)buf)[i]); } #else I2C_WRITE_BYTE(B0, 0, ((const char *)buf)[i]); #endif } end: return ret; }
inline void i2c_handle_sda_irq(int controller) { volatile struct i2c_status *p_status = i2c_stsobjs + controller; /* 1 Issue Start is successful ie. write address byte */ if (p_status->oper_state == SMB_MASTER_START || p_status->oper_state == SMB_REPEAT_START) { uint8_t addr = p_status->slave_addr; /* Prepare address byte */ if (p_status->sz_txbuf == 0) {/* Receive mode */ p_status->oper_state = SMB_READ_OPER; /* * Receiving one byte only - set nack just * before writing address byte */ if (p_status->sz_rxbuf == 1) I2C_NACK(controller); /* Write the address to the bus R bit*/ I2C_WRITE_BYTE(controller, (addr | 0x1)); CPRINTS("-ARR-0x%02x", addr); } else {/* Transmit mode */ p_status->oper_state = SMB_WRITE_OPER; /* Write the address to the bus W bit*/ I2C_WRITE_BYTE(controller, addr); CPRINTS("-ARW-0x%02x", addr); } /* Completed handling START condition */ return; } /* 2 Handle master write operation */ else if (p_status->oper_state == SMB_WRITE_OPER) { /* all bytes have been written, in a pure write operation */ if (p_status->idx_buf == p_status->sz_txbuf) { /* no more message */ if (p_status->sz_rxbuf == 0) { /* need to STOP or not */ if (p_status->flags & I2C_XFER_STOP) { /* Issue a STOP condition on the bus */ I2C_STOP(controller); CPUTS("-SP"); /* Clear SDAST by writing dummy byte */ I2C_WRITE_BYTE(controller, 0xFF); } /* Set error code */ p_status->err_code = SMB_OK; /* Set SMB status if we need stall bus */ p_status->oper_state = (p_status->flags & I2C_XFER_STOP) ? SMB_IDLE : SMB_WRITE_SUSPEND; /* * Disable interrupt for i2c master stall SCL * and forbid SDAST generate interrupt * until common layer start other transactions */ if (p_status->oper_state == SMB_WRITE_SUSPEND) i2c_interrupt(controller, 0); /* Notify upper layer */ task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); CPUTS("-END"); } /* need to restart & send slave address immediately */ else { uint8_t addr_byte = p_status->slave_addr; /* * Prepare address byte * and start to receive bytes */ p_status->oper_state = SMB_READ_OPER; /* Reset index of buffer */ p_status->idx_buf = 0; /* * Generate (Repeated) Start * upon next write to SDA */ I2C_START(controller); CPUTS("-RST"); /* * Receiving one byte only - set nack just * before writing address byte */ if (p_status->sz_rxbuf == 1) { I2C_NACK(controller); CPUTS("-GNA"); } /* Write the address to the bus R bit*/ I2C_WRITE_BYTE(controller, (addr_byte | 0x1)); CPUTS("-ARR"); } } /* write next byte (not last byte and not slave address */ else { I2C_WRITE_BYTE(controller, p_status->tx_buf[p_status->idx_buf++]); CPRINTS("-W(%02x)", p_status->tx_buf[p_status->idx_buf-1]); } } /* 3 Handle master read operation (read or after a write operation) */ else if (p_status->oper_state == SMB_READ_OPER) { uint8_t data; /* last byte is about to be read - end of transaction */ if (p_status->idx_buf == (p_status->sz_rxbuf - 1)) { /* need to STOP or not */ if (p_status->flags & I2C_XFER_STOP) { /* Stop should set before reading last byte */ I2C_STOP(controller); CPUTS("-SP"); } } /* Check if byte-before-last is about to be read */ else if (p_status->idx_buf == (p_status->sz_rxbuf - 2)) { /* * Set nack before reading byte-before-last, * so that nack will be generated after receive * of last byte */ if (p_status->flags & I2C_XFER_STOP) { I2C_NACK(controller); CPUTS("-GNA"); } } /* Read last byte but flag don't include I2C_XFER_STOP */ if (p_status->idx_buf == p_status->sz_rxbuf-1) { /* * Disable interrupt before i2c master read SDA reg * (stall SCL) and forbid SDAST generate interrupt * until common layer start other transactions */ if (!(p_status->flags & I2C_XFER_STOP)) i2c_interrupt(controller, 0); } /* Read data for SMBSDA */ I2C_READ_BYTE(controller, data); CPRINTS("-R(%02x)", data); /* Read to buffer */ p_status->rx_buf[p_status->idx_buf++] = data; /* last byte is read - end of transaction */ if (p_status->idx_buf == p_status->sz_rxbuf) { /* Set current status */ p_status->oper_state = (p_status->flags & I2C_XFER_STOP) ? SMB_IDLE : SMB_READ_SUSPEND; /* Set error code */ p_status->err_code = SMB_OK; /* Notify upper layer of missing data */ task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); CPUTS("-END"); } } }
enum smb_error i2c_master_transaction(int controller) { /* Set i2c mode to object */ int events = 0; volatile struct i2c_status *p_status = i2c_stsobjs + controller; /* Assign current SMB status of controller */ if (p_status->oper_state == SMB_IDLE) { /* New transaction */ p_status->oper_state = SMB_MASTER_START; } else if (p_status->oper_state == SMB_WRITE_SUSPEND) { if (p_status->sz_txbuf == 0) { /* Read bytes from next transaction */ p_status->oper_state = SMB_REPEAT_START; CPUTS("R"); } else { /* Continue to write the other bytes */ p_status->oper_state = SMB_WRITE_OPER; I2C_WRITE_BYTE(controller, p_status->tx_buf[p_status->idx_buf++]); CPRINTS("-W(%02x)", p_status->tx_buf[p_status->idx_buf-1]); } } else if (p_status->oper_state == SMB_READ_SUSPEND) { /* Need to read the other bytes from next transaction */ uint8_t data; uint8_t timeout = 10; /* unit: us */ p_status->oper_state = SMB_READ_OPER; /* wait for SDAST issue */ while (timeout > 0) { if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_SDAST)) break; if (--timeout > 0) usleep(10); } if (timeout == 0) return EC_ERROR_TIMEOUT; /* * Read first byte from SMBSDA in case SDAST interrupt occurs * immediately before task_wait_event_mask() func */ I2C_READ_BYTE(controller, data); CPRINTS("-R(%02x)", data); /* Read to buffer */ p_status->rx_buf[p_status->idx_buf++] = data; } /* Generate a START condition */ if (p_status->oper_state == SMB_MASTER_START || p_status->oper_state == SMB_REPEAT_START) { I2C_START(controller); CPUTS("ST"); } /* Enable SMB interrupt and New Address Match interrupt source */ i2c_interrupt(controller, 1); /* Wait for transfer complete or timeout */ events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, p_status->timeout_us); /* Handle bus timeout */ if ((events & TASK_EVENT_I2C_IDLE) == 0) { /* Recovery I2C controller */ i2c_recovery(controller); p_status->err_code = SMB_TIMEOUT_ERROR; } /* * In slave write operation, NACK is OK, otherwise it is a problem */ else if (p_status->err_code == SMB_BUS_ERROR || p_status->err_code == SMB_MASTER_NO_ADDRESS_MATCH){ i2c_recovery(controller); } /* Wait till STOP condition is generated */ if (p_status->err_code == SMB_OK && i2c_wait_stop_completed(controller, I2C_MIN_TIMEOUT) != EC_SUCCESS) { cprints(CC_I2C, "STOP fail! scl %02x is held by slave device!", controller); p_status->err_code = SMB_TIMEOUT_ERROR; } return p_status->err_code; }