blob: 2e049757e4d36b6b82369afd8e892efe603f0604 [file] [log] [blame]
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "chipset.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "dma.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "i2c.h"
#include "i2c_arbitration.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_I2C, outstr)
#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
/* Maximum transfer of a SMBUS block transfer */
#define SMBUS_MAX_BLOCK 32
/* 8-bit I2C slave address */
#define I2C_ADDRESS 0x3c
/* I2C bus frequency */
#define I2C_FREQ 100000 /* Hz */
/* I2C bit period in microseconds */
#define I2C_PERIOD_US (SECOND / I2C_FREQ)
/* Clock divider for I2C controller */
#define I2C_CCR (CPU_CLOCK / (2 * I2C_FREQ))
/*
* Transmit timeout in microseconds
*
* In theory we shouldn't have a timeout here (at least when we're in slave
* mode). The slave is supposed to wait forever for the master to read bytes.
* ...but we're going to keep the timeout to make sure we're robust. It may in
* fact be needed if the host resets itself mid-read.
*/
#define I2C_TX_TIMEOUT_SLAVE (100 * MSEC)
#define I2C_TX_TIMEOUT_MASTER (10 * MSEC)
/*
* We delay 5us in bitbang mode. That gives us 5us low and 5us high or
* a frequency of 100kHz.
*
* Note that the code takes a little time to run so we don't actually get
* 100kHz, but that's OK.
*/
#define I2C_BITBANG_DELAY_US 5
#define I2C1 STM32_I2C1_PORT
#define I2C2 STM32_I2C2_PORT
/* Select the DMA channels matching the board configuration */
#define DMAC_SLAVE_TX \
((I2C_PORT_SLAVE) ? STM32_DMAC_I2C2_TX : STM32_DMAC_I2C1_TX)
#define DMAC_SLAVE_RX \
((I2C_PORT_SLAVE) ? STM32_DMAC_I2C2_RX : STM32_DMAC_I2C1_RX)
#define DMAC_MASTER_TX \
((I2C_PORT_MASTER) ? STM32_DMAC_I2C2_TX : STM32_DMAC_I2C1_TX)
#define DMAC_MASTER_RX \
((I2C_PORT_MASTER) ? STM32_DMAC_I2C2_RX : STM32_DMAC_I2C1_RX)
enum {
/*
* A stop condition should take 2 clocks, but the process may need more
* time to notice if it is preempted, so we poll repeatedly for 8
* clocks, before backing off and only check once every
* STOP_SENT_RETRY_US for up to TIMEOUT_STOP_SENT clocks before giving
* up.
*/
SLOW_STOP_SENT_US = I2C_PERIOD_US * 8,
TIMEOUT_STOP_SENT_US = I2C_PERIOD_US * 200,
STOP_SENT_RETRY_US = 150,
};
static const struct dma_option dma_tx_option[I2C_PORT_COUNT] = {
{STM32_DMAC_I2C1_TX, (void *)&STM32_I2C_DR(I2C1),
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_16_BIT},
{STM32_DMAC_I2C2_TX, (void *)&STM32_I2C_DR(I2C2),
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_16_BIT},
};
static const struct dma_option dma_rx_option[I2C_PORT_COUNT] = {
{STM32_DMAC_I2C1_RX, (void *)&STM32_I2C_DR(I2C1),
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_16_BIT},
{STM32_DMAC_I2C2_RX, (void *)&STM32_I2C_DR(I2C2),
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_16_BIT},
};
static uint16_t i2c_sr1[I2C_PORT_COUNT];
/* Buffer for host commands (including version, error code and checksum) */
static uint8_t host_buffer[EC_PROTO2_MAX_REQUEST_SIZE];
static struct host_cmd_handler_args host_cmd_args;
static uint8_t i2c_old_response; /* Send an old-style response */
/* Flag indicating if a command is currently in the buffer */
static uint8_t rx_pending;
static inline void disable_i2c_interrupt(int port)
{
STM32_I2C_CR2(port) &= ~(3 << 8);
}
static inline void enable_i2c_interrupt(int port)
{
STM32_I2C_CR2(port) |= 3 << 8;
}
static inline void enable_ack(int port)
{
STM32_I2C_CR1(port) |= (1 << 10);
}
static inline void disable_ack(int port)
{
STM32_I2C_CR1(port) &= ~(1 << 10);
}
static void i2c_init_port(unsigned int port);
static int i2c_write_raw_slave(int port, void *buf, int len)
{
stm32_dma_chan_t *chan;
int rv;
/* we don't want to race with TxE interrupt event */
disable_i2c_interrupt(port);
/* Configuring DMA1 channel DMAC_SLAVE_TX */
enable_ack(port);
chan = dma_get_channel(DMAC_SLAVE_TX);
dma_prepare_tx(dma_tx_option + port, len, buf);
/* Start the DMA */
dma_go(chan);
/* Configuring i2c to use DMA */
STM32_I2C_CR2(port) |= (1 << 11);
if (in_interrupt_context()) {
/* Poll for the transmission complete flag */
dma_wait(DMAC_SLAVE_TX);
dma_clear_isr(DMAC_SLAVE_TX);
} else {
/* Wait for the transmission complete Interrupt */
dma_enable_tc_interrupt(DMAC_SLAVE_TX);
rv = task_wait_event(DMA_TRANSFER_TIMEOUT_US);
dma_disable_tc_interrupt(DMAC_SLAVE_TX);
if (!(rv & TASK_EVENT_WAKE)) {
CPRINTF("[%T Slave timeout, resetting i2c]\n");
i2c_init_port(port);
}
}
dma_disable(DMAC_SLAVE_TX);
STM32_I2C_CR2(port) &= ~(1 << 11);
enable_i2c_interrupt(port);
return len;
}
static void i2c_send_response(struct host_cmd_handler_args *args)
{
const uint8_t *data = args->response;
int size = args->response_size;
uint8_t *out = host_buffer;
int sum = 0, i;
*out++ = args->result;
if (!i2c_old_response) {
*out++ = size;
sum = args->result + size;
}
for (i = 0; i < size; i++, data++, out++) {
if (data != out)
*out = *data;
sum += *data;
}
*out++ = sum & 0xff;
/* send the answer to the AP */
i2c_write_raw_slave(I2C2, host_buffer, out - host_buffer);
}
/* Process the command in the i2c host buffer */
static void i2c_process_command(void)
{
struct host_cmd_handler_args *args = &host_cmd_args;
char *buff = host_buffer;
args->command = *buff;
args->result = EC_RES_SUCCESS;
if (args->command >= EC_CMD_VERSION0) {
int csum, i;
/* Read version and data size */
args->version = args->command - EC_CMD_VERSION0;
args->command = buff[1];
args->params_size = buff[2];
/* Verify checksum */
for (csum = i = 0; i < args->params_size + 3; i++)
csum += buff[i];
if ((uint8_t)csum != buff[i])
args->result = EC_RES_INVALID_CHECKSUM;
buff += 3;
i2c_old_response = 0;
} else {
/*
* Old style (version 1) command.
*
* TODO(crosbug.com/p/23765): Nothing sends these anymore,
* since this was superseded by version 2 before snow launched.
* This code should be safe to remove.
*/
args->version = 0;
args->params_size = EC_PROTO2_MAX_PARAM_SIZE; /* unknown */
buff++;
i2c_old_response = 1;
}
/* we have an available command : execute it */
args->send_response = i2c_send_response;
args->params = buff;
/* skip room for error code, arglen */
args->response = host_buffer + 2;
args->response_max = EC_PROTO2_MAX_PARAM_SIZE;
args->response_size = 0;
host_command_received(args);
}
static void i2c_event_handler(int port)
{
/* save and clear status */
i2c_sr1[port] = STM32_I2C_SR1(port);
STM32_I2C_SR1(port) = 0;
/* Confirm that you are not in master mode */
if (STM32_I2C_SR2(port) & (1 << 0)) {
CPRINTF("I2C slave ISR triggered in master mode, ignoring.\n");
return;
}
/* transfer matched our slave address */
if (i2c_sr1[port] & (1 << 1)) {
/* If it's a receiver slave */
if (!(STM32_I2C_SR2(port) & (1 << 2))) {
dma_start_rx(dma_rx_option + port, sizeof(host_buffer),
host_buffer);
STM32_I2C_CR2(port) |= (1 << 11);
rx_pending = 1;
}
/* cleared by reading SR1 followed by reading SR2 */
STM32_I2C_SR1(port);
STM32_I2C_SR2(port);
} else if (i2c_sr1[port] & (1 << 4)) {
/* If it's a receiver slave */
if (!(STM32_I2C_SR2(port) & (1 << 2))) {
/* Disable, and clear the DMA transfer complete flag */
dma_disable(DMAC_SLAVE_RX);
dma_clear_isr(DMAC_SLAVE_RX);
/* Turn off i2c's DMA flag */
STM32_I2C_CR2(port) &= ~(1 << 11);
}
/* clear STOPF bit by reading SR1 and then writing CR1 */
STM32_I2C_SR1(port);
STM32_I2C_CR1(port) = STM32_I2C_CR1(port);
}
/* TxE event */
if (i2c_sr1[port] & (1 << 7)) {
if (port == I2C2) { /* AP is waiting for EC response */
if (rx_pending) {
i2c_process_command();
/* reset host buffer after end of transfer */
rx_pending = 0;
} else {
/* spurious read : return dummy value */
STM32_I2C_DR(port) = 0xec;
}
}
}
}
static void i2c2_event_interrupt(void) { i2c_event_handler(I2C2); }
DECLARE_IRQ(STM32_IRQ_I2C2_EV, i2c2_event_interrupt, 3);
static void i2c_error_handler(int port)
{
i2c_sr1[port] = STM32_I2C_SR1(port);
if (i2c_sr1[port] & 1 << 10) {
/* ACK failed (NACK); expected when AP reads final byte.
* Software must clear AF bit. */
} else {
CPRINTF("%s: I2C_SR1(%d): 0x%04x\n",
__func__, port, i2c_sr1[port]);
CPRINTF("%s: I2C_SR2(%d): 0x%04x\n",
__func__, port, STM32_I2C_SR2(port));
}
STM32_I2C_SR1(port) &= ~0xdf00;
}
static void i2c2_error_interrupt(void) { i2c_error_handler(I2C2); }
DECLARE_IRQ(STM32_IRQ_I2C2_ER, i2c2_error_interrupt, 2);
/* board-specific setup for post-I2C module init */
void __board_i2c_post_init(int port)
{
}
void board_i2c_post_init(int port)
__attribute__((weak, alias("__board_i2c_post_init")));
/*
* Unwedge the i2c bus for the given port.
*
* Some devices on our i2c busses keep power even if we get a reset. That
* means that they could be partway through a transaction and could be
* driving the bus in a way that makes it hard for us to talk on the bus.
* ...or they might listen to the next transaction and interpret it in a
* weird way.
*
* Note that devices could be in one of several states:
* - If a device got interrupted in a write transaction it will be watching
* for additional data to finish its write. It will probably be looking to
* ack the data (drive the data line low) after it gets everything. Ideally
* we'd like to abort right away so we don't write bogus data.
* - If a device got interrupted while responding to a register read, it will
* be watching for clocks and will drive data out when it sees clocks. At
* the moment it might be trying to send out a 1 (so both clock and data
* may be high) or it might be trying to send out a 0 (so it's driving data
* low). Ideally we want to finish reading the current byte and then nak to
* abort everything.
*
* We attempt to unwedge the bus by doing:
* - If possible, send a pseudo-"stop" bit. We can only do this if nobody
* else is driving the clock or data lines, since that's the only way we
* have enough control. The idea here is to abort any writes that might
* be in progress. Note that a real "stop" bit would actually be a "low to
* high transition of SDA while SCL is high". ...but both must be high for
* us to be in control of the bus. Thus we _first_ drive SDA low so we can
* transition it high. This first transition looks like a start bit. In any
* case, the hope here is that it will look enough like an error condition
* that slaves will abort.
* - If we failed to send the pseudo-stop bit, try one clock and try again.
* I've seen a reset happen while the device was waiting for us to clock out
* its ack of the address. That should be the only time that the other side
* is driving things in the case of a write, so only 1 clock is enough.
* - Try to clock 9 times, if we can. This should finish reading out any data
* and then should nak.
* - Send one last pseudo-stop bit, just for good measure.
*
* @param port The i2c port to unwedge.
*/
static void unwedge_i2c_bus(int port)
{
enum gpio_signal sda, scl;
int i;
ASSERT(port == I2C1 || port == I2C2);
/*
* TODO(crosbug.com/p/23802): This requires defining GPIOs for both
* ports even if the board only supports one port.
*/
if (port == I2C1) {
sda = GPIO_I2C1_SDA;
scl = GPIO_I2C1_SCL;
} else {
sda = GPIO_I2C2_SDA;
scl = GPIO_I2C2_SCL;
}
/*
* Reconfigure ports as general purpose open-drain outputs, initted
* to high.
*/
gpio_set_flags(scl, GPIO_ODR_HIGH);
gpio_set_flags(sda, GPIO_ODR_HIGH);
/* Try to send out pseudo-stop bit. See function description */
if (gpio_get_level(scl) && gpio_get_level(sda)) {
gpio_set_level(sda, 0);
udelay(I2C_BITBANG_DELAY_US);
gpio_set_level(sda, 1);
udelay(I2C_BITBANG_DELAY_US);
} else {
/* One more clock in case it was trying to ack its address */
gpio_set_level(scl, 0);
udelay(I2C_BITBANG_DELAY_US);
gpio_set_level(scl, 1);
udelay(I2C_BITBANG_DELAY_US);
if (gpio_get_level(scl) && gpio_get_level(sda)) {
gpio_set_level(sda, 0);
udelay(I2C_BITBANG_DELAY_US);
gpio_set_level(sda, 1);
udelay(I2C_BITBANG_DELAY_US);
}
}
/*
* Now clock 9 to read pending data; one of these will be a NAK.
*
* Don't bother even checking if scl is high--we can't do anything about
* it anyway.
*/
for (i = 0; i < 9; i++) {
gpio_set_level(scl, 0);
udelay(I2C_BITBANG_DELAY_US);
gpio_set_level(scl, 1);
udelay(I2C_BITBANG_DELAY_US);
}
/* One last try at a pseudo-stop bit */
if (gpio_get_level(scl) && gpio_get_level(sda)) {
gpio_set_level(sda, 0);
udelay(I2C_BITBANG_DELAY_US);
gpio_set_level(sda, 1);
udelay(I2C_BITBANG_DELAY_US);
}
/*
* Set things back to quiescent.
*
* We rely on board_i2c_post_init() to actually reconfigure pins to
* be special function.
*/
gpio_set_level(scl, 1);
gpio_set_level(sda, 1);
}
static void i2c_init_port(unsigned int port)
{
const int i2c_clock_bit[] = {21, 22};
ASSERT(port == I2C1 || port == I2C2);
ASSERT(port < 2);
if (!(STM32_RCC_APB1ENR & (1 << i2c_clock_bit[port]))) {
/* Only unwedge the bus if the clock is off */
if (i2c_claim(port) == EC_SUCCESS) {
unwedge_i2c_bus(port);
i2c_release(port);
}
/* enable I2C2 clock */
STM32_RCC_APB1ENR |= 1 << i2c_clock_bit[port];
}
/* force reset of the i2c peripheral */
STM32_I2C_CR1(port) = 0x8000;
STM32_I2C_CR1(port) = 0x0000;
/* set clock configuration : standard mode (100kHz) */
STM32_I2C_CCR(port) = I2C_CCR;
/* set slave address */
if (port == I2C2)
STM32_I2C_OAR1(port) = I2C_ADDRESS;
/* configuration : I2C mode / Periphal enabled, ACK enabled */
STM32_I2C_CR1(port) = (1 << 10) | (1 << 0);
/* error and event interrupts enabled / input clock is 16Mhz */
STM32_I2C_CR2(port) = (1 << 9) | (1 << 8) | 0x10;
/* clear status */
STM32_I2C_SR1(port) = 0;
board_i2c_post_init(port);
}
static void i2c_init(void)
{
/*
* TODO(crosbug.com/p/23763): Add config options to determine which
* channels to init.
*/
i2c_init_port(I2C1);
i2c_init_port(I2C2);
/* Enable event and error interrupts */
task_enable_irq(STM32_IRQ_I2C2_EV);
task_enable_irq(STM32_IRQ_I2C2_ER);
}
DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT);
/*****************************************************************************/
/* STM32 Host I2C */
#define SR1_SB (1 << 0) /* Start bit sent */
#define SR1_ADDR (1 << 1) /* Address sent */
#define SR1_BTF (1 << 2) /* Byte transfered */
#define SR1_ADD10 (1 << 3) /* 10bit address sent */
#define SR1_STOPF (1 << 4) /* Stop detected */
#define SR1_RxNE (1 << 6) /* Data reg not empty */
#define SR1_TxE (1 << 7) /* Data reg empty */
#define SR1_BERR (1 << 8) /* Buss error */
#define SR1_ARLO (1 << 9) /* Arbitration lost */
#define SR1_AF (1 << 10) /* Ack failure */
#define SR1_OVR (1 << 11) /* Overrun/underrun */
#define SR1_PECERR (1 << 12) /* PEC err in reception */
#define SR1_TIMEOUT (1 << 14) /* Timeout : 25ms */
#define CR2_DMAEN (1 << 11) /* DMA enable */
#define CR2_LAST (1 << 12) /* Next EOT is last EOT */
static inline void dump_i2c_reg(int port)
{
#ifdef CONFIG_I2C_DEBUG
CPRINTF("CR1 : %016b\n", STM32_I2C_CR1(port));
CPRINTF("CR2 : %016b\n", STM32_I2C_CR2(port));
CPRINTF("SR2 : %016b\n", STM32_I2C_SR2(port));
CPRINTF("SR1 : %016b\n", STM32_I2C_SR1(port));
CPRINTF("OAR1 : %016b\n", STM32_I2C_OAR1(port));
CPRINTF("OAR2 : %016b\n", STM32_I2C_OAR2(port));
CPRINTF("DR : %016b\n", STM32_I2C_DR(port));
CPRINTF("CCR : %016b\n", STM32_I2C_CCR(port));
CPRINTF("TRISE: %016b\n", STM32_I2C_TRISE(port));
#endif /* CONFIG_I2C_DEBUG */
}
enum wait_t {
WAIT_NONE,
WAIT_MASTER_START,
WAIT_ADDR_READY,
WAIT_XMIT_TXE,
WAIT_XMIT_FINAL_TXE,
WAIT_XMIT_BTF,
WAIT_XMIT_STOP,
WAIT_RX_NE,
WAIT_RX_NE_FINAL,
WAIT_RX_NE_STOP,
WAIT_RX_NE_STOP_SIZE2,
};
/**
* Wait for a specific i2c event
*
* This function waits until the bit(s) corresponding to mask in
* the specified port's I2C SR1 register is/are set. It may
* return a timeout or success.
*
* @param port Port to wait on
* @param mask A mask specifying which bits in SR1 to wait to be set
* @param wait A wait code to be returned with the timeout error code if that
* occurs, to help with debugging.
* @return EC_SUCCESS, or EC_ERROR_TIMEOUT with the wait code OR'd onto the
* bits 8-16 to indicate what it timed out waiting for.
*/
static int wait_status(int port, uint32_t mask, enum wait_t wait)
{
uint32_t r;
timestamp_t t1, t2;
t1 = t2 = get_time();
r = STM32_I2C_SR1(port);
while (mask ? ((r & mask) != mask) : r) {
t2 = get_time();
if (t2.val - t1.val > I2C_TX_TIMEOUT_MASTER)
return EC_ERROR_TIMEOUT | (wait << 8);
else if (t2.val - t1.val > 150)
usleep(100);
r = STM32_I2C_SR1(port);
}
return EC_SUCCESS;
}
static inline uint32_t read_clear_status(int port)
{
uint32_t sr1, sr2;
sr1 = STM32_I2C_SR1(port);
sr2 = STM32_I2C_SR2(port);
return (sr2 << 16) | (sr1 & 0xffff);
}
static int master_start(int port, int slave_addr)
{
int rv;
/* Change to master send mode, reset stop bit, send start bit */
STM32_I2C_CR1(port) = (STM32_I2C_CR1(port) & ~(1 << 9)) | (1 << 8);
/* Wait for start bit sent event */
rv = wait_status(port, SR1_SB, WAIT_MASTER_START);
if (rv)
return rv;
/* Send address */
STM32_I2C_DR(port) = slave_addr;
/* Wait for addr ready */
rv = wait_status(port, SR1_ADDR, WAIT_ADDR_READY);
if (rv)
return rv;
read_clear_status(port);
return EC_SUCCESS;
}
static void master_stop(int port)
{
STM32_I2C_CR1(port) |= (1 << 9);
}
static int wait_until_stop_sent(int port)
{
timestamp_t deadline;
timestamp_t slow_cutoff;
uint8_t is_slow;
deadline = slow_cutoff = get_time();
deadline.val += TIMEOUT_STOP_SENT_US;
slow_cutoff.val += SLOW_STOP_SENT_US;
while (STM32_I2C_CR1(port) & (1 << 9)) {
if (timestamp_expired(deadline, NULL)) {
ccprintf("Stop event deadline passed:\ttask=%d"
"\tCR1=%016b\n",
(int)task_get_current(), STM32_I2C_CR1(port));
return EC_ERROR_TIMEOUT;
}
if (is_slow) {
/* If we haven't gotten a fast response, sleep */
usleep(STOP_SENT_RETRY_US);
} else {
/* Check to see if this request is taking a while */
if (timestamp_expired(slow_cutoff, NULL)) {
ccprintf("Stop event taking a while: task=%d",
(int)task_get_current());
is_slow = 1;
}
}
}
return EC_SUCCESS;
}
static void handle_i2c_error(int port, int rv)
{
timestamp_t t1, t2;
uint32_t r;
/* We have not used the bus, just exit */
if (rv == EC_ERROR_BUSY)
return;
/* EC_ERROR_TIMEOUT may have a code specifying where the timeout was */
if ((rv & 0xff) == EC_ERROR_TIMEOUT) {
#ifdef CONFIG_I2C_DEBUG
CPRINTF("Wait_status() timeout type: %d\n", (rv >> 8));
#endif
rv = EC_ERROR_TIMEOUT;
}
if (rv)
dump_i2c_reg(port);
/* Clear rc_w0 bits */
STM32_I2C_SR1(port) = 0;
/* Clear seq read status bits */
r = STM32_I2C_SR1(port);
r = STM32_I2C_SR2(port);
/* Clear busy state */
t1 = get_time();
if (rv == EC_ERROR_TIMEOUT && (STM32_I2C_CR1(port) & (1 << 8))) {
/*
* If it failed while just trying to send the start bit then
* something is wrong with the internal state of the i2c,
* (Probably a stray pulse on the line got it out of sync with
* the actual bytes) so reset it.
*/
CPRINTF("Unable to send START, resetting i2c.\n");
i2c_init_port(port);
goto cr_cleanup;
} else if (rv == EC_ERROR_TIMEOUT && !(r & 2)) {
/*
* If the BUSY bit is faulty, send a stop bit just to be sure.
* It seems that this can be happen very briefly while sending
* a 1. We've not actually seen this, but just to be safe.
*/
CPRINTF("Bad BUSY bit detected.\n");
master_stop(port);
}
/* Try to send stop bits until the bus becomes idle */
while (r & 2) {
t2 = get_time();
if (t2.val - t1.val > I2C_TX_TIMEOUT_MASTER) {
dump_i2c_reg(port);
/* Reset the i2c periph to get it back to slave mode */
i2c_init_port(port);
goto cr_cleanup;
}
/* Send stop */
master_stop(port);
usleep(1000);
r = STM32_I2C_SR2(port);
}
cr_cleanup:
/*
* Reset control register to the default state :
* I2C mode / Periphal enabled, ACK enabled
*/
STM32_I2C_CR1(port) = (1 << 10) | (1 << 0);
}
static int i2c_master_transmit(int port, int slave_addr, const uint8_t *data,
int size, int stop)
{
int rv, rv_start;
disable_ack(port);
/* Configure DMA channel for TX to host */
dma_prepare_tx(dma_tx_option + port, size, data);
dma_enable_tc_interrupt(DMAC_MASTER_TX);
/* Start the DMA */
dma_go(dma_get_channel(DMAC_MASTER_TX));
/* Configuring i2c2 to use DMA */
STM32_I2C_CR2(port) |= CR2_DMAEN;
/* Initialise i2c communication by sending START and ADDR */
rv_start = master_start(port, slave_addr);
/* If it started, wait for the transmission complete Interrupt */
if (!rv_start)
rv = task_wait_event(DMA_TRANSFER_TIMEOUT_US);
dma_disable(DMAC_MASTER_TX);
dma_disable_tc_interrupt(DMAC_MASTER_TX);
STM32_I2C_CR2(port) &= ~CR2_DMAEN;
if (rv_start)
return rv_start;
if (!(rv & TASK_EVENT_WAKE))
return EC_ERROR_TIMEOUT;
rv = wait_status(port, SR1_BTF, WAIT_XMIT_BTF);
if (rv)
return rv;
if (stop) {
master_stop(port);
return wait_status(port, 0, WAIT_XMIT_STOP);
}
return EC_SUCCESS;
}
static int i2c_master_receive(int port, int slave_addr, uint8_t *data,
int size)
{
int rv, rv_start;
if (data == NULL || size < 1)
return EC_ERROR_INVAL;
/* Master receive only supports DMA for payloads > 1 byte */
if (size > 1) {
enable_ack(port);
dma_start_rx(dma_rx_option + port, size, data);
dma_enable_tc_interrupt(DMAC_MASTER_RX);
STM32_I2C_CR2(port) |= CR2_DMAEN;
STM32_I2C_CR2(port) |= CR2_LAST;
rv_start = master_start(port, slave_addr | 1);
if (!rv_start)
rv = task_wait_event(DMA_TRANSFER_TIMEOUT_US);
dma_disable(DMAC_MASTER_RX);
dma_disable_tc_interrupt(DMAC_MASTER_RX);
STM32_I2C_CR2(port) &= ~CR2_DMAEN;
disable_ack(port);
if (rv_start)
return rv_start;
if (!(rv & TASK_EVENT_WAKE))
return EC_ERROR_TIMEOUT;
master_stop(port);
} else {
disable_ack(port);
rv = master_start(port, slave_addr | 1);
if (rv)
return rv;
master_stop(port);
rv = wait_status(port, SR1_RxNE, WAIT_RX_NE_STOP_SIZE2);
if (rv)
return rv;
data[0] = STM32_I2C_DR(port);
}
return wait_until_stop_sent(port);
}
int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
uint8_t *in, int in_bytes, int flags)
{
int rv;
/* TODO(crosbug.com/p/23569): support start/stop flags */
ASSERT(out || !out_bytes);
ASSERT(in || !in_bytes);
if (i2c_claim(port))
return EC_ERROR_BUSY;
disable_i2c_interrupt(port);
rv = i2c_master_transmit(port, slave_addr, out, out_bytes,
in_bytes ? 0 : 1);
if (!rv && in_bytes)
rv = i2c_master_receive(port, slave_addr, in, in_bytes);
handle_i2c_error(port, rv);
enable_i2c_interrupt(port);
i2c_release(port);
return rv;
}
int i2c_get_line_levels(int port)
{
enum gpio_signal sda, scl;
ASSERT(port == I2C1 || port == I2C2);
if (port == I2C1) {
sda = GPIO_I2C1_SDA;
scl = GPIO_I2C1_SCL;
} else {
sda = GPIO_I2C2_SDA;
scl = GPIO_I2C2_SCL;
}
return (gpio_get_level(sda) ? I2C_LINE_SDA_HIGH : 0) |
(gpio_get_level(scl) ? I2C_LINE_SCL_HIGH : 0);
}
int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data,
int len)
{
int rv;
uint8_t reg, block_length;
/*
* TODO(crosbug.com/p/23569): when i2c_xfer() supports start/stop bits,
* merge this with the LM4 implementation and move to i2c_common.c.
*/
if ((len <= 0) || (len > SMBUS_MAX_BLOCK))
return EC_ERROR_INVAL;
i2c_lock(port, 1);
/* Read the counted string into the output buffer */
reg = offset;
rv = i2c_xfer(port, slave_addr, &reg, 1, data, len, I2C_XFER_SINGLE);
if (rv == EC_SUCCESS) {
/* Block length is the first byte of the returned buffer */
block_length = MIN(data[0], len - 1);
/* Move data down, then null-terminate it */
memmove(data, data + 1, block_length);
data[block_length] = 0;
}
i2c_lock(port, 0);
return rv;
}