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; }
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 i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, uint8_t *in, int in_bytes, int flags) { int started = (flags & I2C_XFER_START) ? 0 : 1; int rv = EC_SUCCESS; int i; ASSERT(out || !out_bytes); ASSERT(in || !in_bytes); dump_i2c_reg(port, "xfer start"); /* * Clear status * * TODO(crosbug.com/p/29314): should check for any leftover error * status, and reset the port if present. */ STM32_I2C_SR1(port) = 0; /* Clear start, stop, POS, ACK bits to get us in a known state */ STM32_I2C_CR1(port) &= ~(STM32_I2C_CR1_START | STM32_I2C_CR1_STOP | STM32_I2C_CR1_POS | STM32_I2C_CR1_ACK); /* No out bytes and no in bytes means just check for active */ if (out_bytes || !in_bytes) { if (!started) { rv = send_start(port, slave_addr); if (rv) goto xfer_exit; } /* Write data, if any */ for (i = 0; i < out_bytes; i++) { /* Write next data byte */ STM32_I2C_DR(port) = out[i]; dump_i2c_reg(port, "wrote data"); rv = wait_sr1(port, STM32_I2C_SR1_BTF); if (rv) goto xfer_exit; } /* Need repeated start condition before reading */ started = 0; /* If no input bytes, queue stop condition */ if (!in_bytes && (flags & I2C_XFER_STOP)) STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; } if (in_bytes) { /* Setup ACK/POS before sending start as per user manual */ if (in_bytes == 2) STM32_I2C_CR1(port) |= STM32_I2C_CR1_POS; else if (in_bytes != 1) STM32_I2C_CR1(port) |= STM32_I2C_CR1_ACK; if (!started) { rv = send_start(port, slave_addr | 0x01); if (rv) goto xfer_exit; } if (in_bytes == 1) { /* Set stop immediately after ADDR cleared */ if (flags & I2C_XFER_STOP) STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; rv = wait_sr1(port, STM32_I2C_SR1_RXNE); if (rv) goto xfer_exit; in[0] = STM32_I2C_DR(port); } else if (in_bytes == 2) { /* Wait till the shift register is full */ rv = wait_sr1(port, STM32_I2C_SR1_BTF); if (rv) goto xfer_exit; if (flags & I2C_XFER_STOP) STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; in[0] = STM32_I2C_DR(port); in[1] = STM32_I2C_DR(port); } else { /* Read all but last three */ for (i = 0; i < in_bytes - 3; i++) { /* Wait for receive buffer not empty */ rv = wait_sr1(port, STM32_I2C_SR1_RXNE); if (rv) goto xfer_exit; dump_i2c_reg(port, "read data"); in[i] = STM32_I2C_DR(port); dump_i2c_reg(port, "post read data"); } /* Wait for BTF (data N-2 in DR, N-1 in shift) */ rv = wait_sr1(port, STM32_I2C_SR1_BTF); if (rv) goto xfer_exit; /* No more acking */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_ACK; in[i++] = STM32_I2C_DR(port); /* Wait for BTF (data N-1 in DR, N in shift) */ rv = wait_sr1(port, STM32_I2C_SR1_BTF); if (rv) goto xfer_exit; /* If this is the last byte, queue stop condition */ if (flags & I2C_XFER_STOP) STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; /* Read the last two bytes */ in[i++] = STM32_I2C_DR(port); in[i++] = STM32_I2C_DR(port); } } xfer_exit: /* On error, queue a stop condition */ if (rv) { flags |= I2C_XFER_STOP; STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; dump_i2c_reg(port, "stop after error"); /* * If failed at sending start, try resetting the port * to unwedge the bus. */ if (rv == I2C_ERROR_FAILED_START) { const struct i2c_port_t *p = i2c_ports; CPRINTS("i2c_xfer start error; " "unwedging and resetting i2c %d", port); i2c_unwedge(port); for (i = 0; i < i2c_ports_used; i++, p++) { if (p->port == port) { i2c_init_port(p); break; } } } } /* If a stop condition is queued, wait for it to take effect */ if (flags & I2C_XFER_STOP) { /* Wait up to 100 us for bus idle */ for (i = 0; i < 10; i++) { if (!(STM32_I2C_SR2(port) & STM32_I2C_SR2_BUSY)) break; udelay(10); } /* * Allow bus to idle for at least one 100KHz clock = 10 us. * This allows slaves on the bus to detect bus-idle before * the next start condition. */ udelay(10); } return rv; }