int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { int ctrl = i2c_port_to_controller(port); volatile struct i2c_status *p_status = i2c_stsobjs + ctrl; if (out_size == 0 && in_size == 0) return EC_SUCCESS; interrupt_disable(); /* make sure bus is not occupied by the other task */ if (p_status->task_waiting != TASK_ID_INVALID) { interrupt_enable(); return EC_ERROR_BUSY; } /* Assign current task ID */ p_status->task_waiting = task_get_current(); interrupt_enable(); /* Select port for multi-ports i2c controller */ i2c_select_port(port); /* Copy data to controller struct */ p_status->flags = flags; p_status->tx_buf = out; p_status->sz_txbuf = out_size; p_status->rx_buf = in; p_status->sz_rxbuf = in_size; #if I2C_7BITS_ADDR /* Set slave address from 7-bits to 8-bits */ p_status->slave_addr = (slave_addr<<1); #else /* Set slave address (8-bits) */ p_status->slave_addr = slave_addr; #endif /* Reset index & error */ p_status->idx_buf = 0; p_status->err_code = SMB_OK; /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_bus_busy(ctrl) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the controller. */ i2c_unwedge(ctrl); /* recovery i2c controller */ i2c_recovery(ctrl); /* Select port again for recovery */ i2c_select_port(port); } CPUTS("\n"); /* Start master transaction */ i2c_master_transaction(ctrl); /* Reset task ID */ p_status->task_waiting = TASK_ID_INVALID; /* Disable SMB interrupt and New Address Match interrupt source */ i2c_interrupt(ctrl, 0); CPRINTS("-Err:0x%02x\n", p_status->err_code); return (p_status->err_code == SMB_OK) ? EC_SUCCESS : EC_ERROR_UNKNOWN; }
int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { struct i2c_port_data *pd = pdata + port; uint32_t events = 0; if (out_size == 0 && in_size == 0) return EC_SUCCESS; if (pd->i2ccs) { if ((flags & I2C_XFER_SINGLE) == I2C_XFER_SINGLE) flags &= ~I2C_XFER_START; } /* Copy data to port struct */ pd->out = out; pd->out_size = out_size; pd->in = in; pd->in_size = in_size; pd->flags = flags; pd->widx = 0; pd->ridx = 0; pd->err = 0; pd->addr = slave_addr; if (port < I2C_STANDARD_PORT_COUNT) { /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_is_busy(port) || (IT83XX_SMB_HOSTA(port) & HOSTA_ALL_WC_BIT) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the port. */ i2c_unwedge(port); /* reset i2c port */ i2c_reset(port, I2C_RC_NO_IDLE_FOR_START); } } else { /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_is_busy(port) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the port. */ i2c_unwedge(port); /* reset i2c port */ i2c_reset(port, I2C_RC_NO_IDLE_FOR_START); } } pd->task_waiting = task_get_current(); if (pd->flags & I2C_XFER_START) { pd->i2ccs = I2C_CH_NORMAL; /* enable i2c interrupt */ task_clear_pending_irq(i2c_ctrl_regs[port].irq); task_enable_irq(i2c_ctrl_regs[port].irq); } /* Start transaction */ i2c_transaction(port); /* Wait for transfer complete or timeout */ events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, pd->timeout_us); /* disable i2c interrupt */ task_disable_irq(i2c_ctrl_regs[port].irq); pd->task_waiting = TASK_ID_INVALID; /* Handle timeout */ if (!(events & TASK_EVENT_I2C_IDLE)) { pd->err = EC_ERROR_TIMEOUT; /* reset i2c port */ i2c_reset(port, I2C_RC_TIMEOUT); } /* reset i2c channel status */ if (pd->err) pd->i2ccs = I2C_CH_NORMAL; return pd->err; }
int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { int i; int started = (flags & I2C_XFER_START) ? 0 : 1; uint8_t reg_sts; if (out_size == 0 && in_size == 0) return EC_SUCCESS; wait_idle(port); reg_sts = MEC1322_I2C_STATUS(port); if (!started && (((reg_sts & (STS_BER | STS_LAB)) || !(reg_sts & STS_NBB)) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { CPRINTS("I2C%d bad status 0x%02x, SCL=%d, SDA=%d", port, reg_sts, i2c_get_line_levels(port) & I2C_LINE_SCL_HIGH, i2c_get_line_levels(port) & I2C_LINE_SDA_HIGH); /* Attempt to unwedge the port. */ i2c_unwedge(port); /* Bus error, bus busy, or arbitration lost. Reset port. */ reset_port(port); /* * We don't know what edges the slave saw, so sleep long enough * that the slave will see the new start condition below. */ usleep(1000); } if (out) { MEC1322_I2C_DATA(port) = (uint8_t)slave_addr; /* * Clock out the slave address. Send START bit if start flag is * set. */ MEC1322_I2C_CTRL(port) = CTRL_PIN | CTRL_ESO | CTRL_ENI | CTRL_ACK | (started ? 0 : CTRL_STA); if (!started) started = 1; for (i = 0; i < out_size; ++i) { if (wait_byte_done(port)) goto err_i2c_xfer; MEC1322_I2C_DATA(port) = out[i]; } if (wait_byte_done(port)) goto err_i2c_xfer; /* * Send STOP bit if the stop flag is on, and caller * doesn't expect to receive data. */ if ((flags & I2C_XFER_STOP) && in_size == 0) { MEC1322_I2C_CTRL(port) = CTRL_PIN | CTRL_ESO | CTRL_STO | CTRL_ACK; } } if (in_size) { if (out_size) { /* resend start bit when change direction */ MEC1322_I2C_CTRL(port) = CTRL_ESO | CTRL_STA | CTRL_ACK | CTRL_ENI; } MEC1322_I2C_DATA(port) = (uint8_t)slave_addr | 0x01; if (!started) { started = 1; /* Clock out slave address with START bit */ MEC1322_I2C_CTRL(port) = CTRL_PIN | CTRL_ESO | CTRL_STA | CTRL_ACK | CTRL_ENI; } /* On MEC1322, first byte read is dummy read (slave addr) */ in_size++; for (i = 0; i < in_size - 2; ++i) { if (wait_byte_done(port)) goto err_i2c_xfer; fill_in_buf(in, i, MEC1322_I2C_DATA(port)); } if (wait_byte_done(port)) goto err_i2c_xfer; /* * De-assert ACK bit before reading the next to last byte, * so that the last byte is NACK'ed. */ MEC1322_I2C_CTRL(port) = CTRL_ESO | CTRL_ENI; fill_in_buf(in, in_size - 2, MEC1322_I2C_DATA(port)); if (wait_byte_done(port)) goto err_i2c_xfer; /* Send STOP if stop flag is set */ MEC1322_I2C_CTRL(port) = CTRL_PIN | CTRL_ESO | CTRL_ACK | ((flags & I2C_XFER_STOP) ? CTRL_STO : 0); /* Now read the last byte */ fill_in_buf(in, in_size - 1, MEC1322_I2C_DATA(port)); } /* Check for error conditions */ if (MEC1322_I2C_STATUS(port) & (STS_LAB | STS_BER)) return EC_ERROR_UNKNOWN; return EC_SUCCESS; err_i2c_xfer: /* Send STOP and return error */ MEC1322_I2C_CTRL(port) = CTRL_PIN | CTRL_ESO | CTRL_STO | CTRL_ACK; return EC_ERROR_UNKNOWN; }