summaryrefslogtreecommitdiff
path: root/chip/stm32/i2c-stm32f4.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/i2c-stm32f4.c')
-rw-r--r--chip/stm32/i2c-stm32f4.c1010
1 files changed, 0 insertions, 1010 deletions
diff --git a/chip/stm32/i2c-stm32f4.c b/chip/stm32/i2c-stm32f4.c
deleted file mode 100644
index c1f19704b5..0000000000
--- a/chip/stm32/i2c-stm32f4.c
+++ /dev/null
@@ -1,1010 +0,0 @@
-/* Copyright 2016 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 "hooks.h"
-#include "i2c.h"
-#include "registers.h"
-#include "system.h"
-#include "task.h"
-#include "timer.h"
-#include "util.h"
-
-/* Console output macros */
-#define CPUTS(outstr) cputs(CC_I2C, outstr)
-#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
-
-#define I2C_ERROR_FAILED_START EC_ERROR_INTERNAL_FIRST
-
-/* Transmit timeout in microseconds */
-#define I2C_TX_TIMEOUT_CONTROLLER (10 * MSEC)
-
-#ifdef CONFIG_HOSTCMD_I2C_ADDR_FLAGS
-#if (I2C_PORT_EC == STM32_I2C1_PORT)
-#define IRQ_PERIPHERAL_EV STM32_IRQ_I2C1_EV
-#define IRQ_PERIPHERAL_ER STM32_IRQ_I2C1_ER
-#else
-#define IRQ_PERIPHERAL_EV STM32_IRQ_I2C2_EV
-#define IRQ_PERIPHERAL_ER STM32_IRQ_I2C2_ER
-#endif
-#endif
-
-/* Define I2C blocks available in stm32f4:
- * We have standard ST I2C blocks and a "fast mode plus" I2C block,
- * which do not share the same registers or functionality. So we'll need
- * two sets of functions to handle this for stm32f4. In stm32f446, we
- * only have one FMP block so we'll hardcode its port number.
- */
-#define STM32F4_FMPI2C_PORT 3
-
-static const __unused struct dma_option dma_tx_option[I2C_PORT_COUNT] = {
- {STM32_DMAC_I2C1_TX, (void *)&STM32_I2C_DR(STM32_I2C1_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_I2C1_TX_REQ_CH)},
- {STM32_DMAC_I2C2_TX, (void *)&STM32_I2C_DR(STM32_I2C2_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_I2C2_TX_REQ_CH)},
- {STM32_DMAC_I2C3_TX, (void *)&STM32_I2C_DR(STM32_I2C3_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_I2C3_TX_REQ_CH)},
- {STM32_DMAC_FMPI2C4_TX, (void *)&STM32_FMPI2C_TXDR(STM32_FMPI2C4_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_FMPI2C4_TX_REQ_CH)},
-};
-
-static const struct dma_option dma_rx_option[I2C_PORT_COUNT] = {
- {STM32_DMAC_I2C1_RX, (void *)&STM32_I2C_DR(STM32_I2C1_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_I2C1_RX_REQ_CH)},
- {STM32_DMAC_I2C2_RX, (void *)&STM32_I2C_DR(STM32_I2C2_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_I2C2_RX_REQ_CH)},
- {STM32_DMAC_I2C3_RX, (void *)&STM32_I2C_DR(STM32_I2C3_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_I2C3_RX_REQ_CH)},
- {STM32_DMAC_FMPI2C4_RX, (void *)&STM32_FMPI2C_RXDR(STM32_FMPI2C4_PORT),
- STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
- STM32_DMA_CCR_CHANNEL(STM32_FMPI2C4_RX_REQ_CH)},
-};
-
-/* Callback for ISR to wake task on DMA complete. */
-static inline void _i2c_dma_wake_callback(void *cb_data, int port)
-{
- task_id_t id = (task_id_t)(int)cb_data;
-
- if (id != TASK_ID_INVALID)
- task_set_event(id, TASK_EVENT_I2C_COMPLETION(port));
-}
-
-/* Each callback is hardcoded to an I2C channel. */
-static void _i2c_dma_wake_callback_0(void *cb_data)
-{
- _i2c_dma_wake_callback(cb_data, 0);
-}
-
-static void _i2c_dma_wake_callback_1(void *cb_data)
-{
- _i2c_dma_wake_callback(cb_data, 1);
-}
-
-static void _i2c_dma_wake_callback_2(void *cb_data)
-{
- _i2c_dma_wake_callback(cb_data, 2);
-}
-
-static void _i2c_dma_wake_callback_3(void *cb_data)
-{
- _i2c_dma_wake_callback(cb_data, 3);
-}
-
-/* void (*callback)(void *) */
-static void (*i2c_callbacks[I2C_PORT_COUNT])(void *) = {
- _i2c_dma_wake_callback_0,
- _i2c_dma_wake_callback_1,
- _i2c_dma_wake_callback_2,
- _i2c_dma_wake_callback_3,
-};
-
-/* Enable the I2C interrupt callback for this port. */
-void i2c_dma_enable_tc_interrupt(enum dma_channel stream, int port)
-{
- dma_enable_tc_interrupt_callback(stream, i2c_callbacks[port],
- (void *)(int)task_get_current());
-}
-
-/**
- * Wait for SR1 register to contain the specified mask of 0 or 1.
- *
- * @param port I2C port
- * @param mask mask of bits of interest
- * @param val desired value of bits of interest
- * @param poll uS poll frequency
- *
- * @return EC_SUCCESS, EC_ERROR_TIMEOUT if timed out waiting, or
- * EC_ERROR_UNKNOWN if an error bit appeared in the status register.
- */
-#define SET 0xffffffff
-#define UNSET 0
-static int wait_sr1_poll(int port, int mask, int val, int poll)
-{
- uint64_t timeout = get_time().val + I2C_TX_TIMEOUT_CONTROLLER;
-
- while (get_time().val < timeout) {
- int sr1 = STM32_I2C_SR1(port);
-
- /* Check for errors */
- if (sr1 & (STM32_I2C_SR1_ARLO | STM32_I2C_SR1_BERR |
- STM32_I2C_SR1_AF)) {
- return EC_ERROR_UNKNOWN;
- }
-
- /* Check for desired mask */
- if ((sr1 & mask) == (val & mask))
- return EC_SUCCESS;
-
- /* I2C is slow, so let other things run while we wait */
- usleep(poll);
- }
-
- CPRINTS("I2C timeout: p:%d m:%x", port, mask);
- return EC_ERROR_TIMEOUT;
-}
-
-/* Wait for SR1 register to contain the specified mask of ones */
-static int wait_sr1(int port, int mask)
-{
- return wait_sr1_poll(port, mask, SET, 100);
-}
-
-
-/**
- * Send a start condition and peripheral address on the specified port.
- *
- * @param port I2C port
- * @param addr_8bit I2C address, with LSB set for receive-mode
- *
- * @return Non-zero if error.
- */
-static int send_start(const int port, const uint16_t addr_8bit)
-{
- int rv;
-
- /* Send start bit */
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_START;
- rv = wait_sr1_poll(port, STM32_I2C_SR1_SB, SET, 1);
- if (rv)
- return I2C_ERROR_FAILED_START;
-
- /* Write peripheral address */
- STM32_I2C_DR(port) = addr_8bit;
- rv = wait_sr1_poll(port, STM32_I2C_SR1_ADDR, SET, 1);
- if (rv)
- return rv;
-
- /* Read SR2 to clear ADDR bit */
- rv = STM32_I2C_SR2(port);
-
- return EC_SUCCESS;
-}
-
-/**
- * Find the i2c port structure associated with the port.
- *
- * @return i2c_port_t * associated with this port number.
- */
-static const struct i2c_port_t *find_port(int port)
-{
- const struct i2c_port_t *p = i2c_ports;
- int i;
-
- for (i = 0; i < i2c_ports_used; i++, p++) {
- if (p->port == port)
- return p;
- }
- CPRINTS("I2C port %d invalid! Crashing now.", port);
- return NULL;
-}
-
-/**
- * Wait for ISR register to contain the specified mask.
- *
- * @param port I2C port
- * @param mask mask of bits of interest
- * @param val desired value of bits of interest
- * @param poll uS poll frequency
- *
- * @return EC_SUCCESS, EC_ERROR_TIMEOUT if timed out waiting, or
- * EC_ERROR_UNKNOWN if an error bit appeared in the status register.
- */
-static int wait_fmpi2c_isr_poll(int port, int mask, int val, int poll)
-{
- uint64_t timeout = get_time().val + I2C_TX_TIMEOUT_CONTROLLER;
-
- while (get_time().val < timeout) {
- int isr = STM32_FMPI2C_ISR(port);
-
- /* Check for errors */
- if (isr & (FMPI2C_ISR_ARLO | FMPI2C_ISR_BERR |
- FMPI2C_ISR_NACKF)) {
- return EC_ERROR_UNKNOWN;
- }
-
- /* Check for desired mask */
- if ((isr & mask) == (val & mask))
- return EC_SUCCESS;
-
- /* I2C is slow, so let other things run while we wait */
- usleep(poll);
- }
-
- CPRINTS("FMPI2C timeout p:%d, m:0x%08x", port, mask);
- return EC_ERROR_TIMEOUT;
-}
-
-/* Wait for ISR register to contain the specified mask of ones */
-static int wait_fmpi2c_isr(int port, int mask)
-{
- return wait_fmpi2c_isr_poll(port, mask, SET, 100);
-}
-
-/**
- * Send a start condition and peripheral address on the specified port.
- *
- * @param port I2C port
- * @param addr_8bit I2C address
- * @param size bytes to transfer
- * @param is_read read, or write?
- *
- * @return Non-zero if error.
- */
-static int send_fmpi2c_start(const int port, const uint16_t addr_8bit,
- int size, int is_read)
-{
- uint32_t reg;
-
- /* Send start bit */
- reg = STM32_FMPI2C_CR2(port);
- reg &= ~(FMPI2C_CR2_SADD_MASK | FMPI2C_CR2_SIZE_MASK |
- FMPI2C_CR2_RELOAD | FMPI2C_CR2_AUTOEND |
- FMPI2C_CR2_RD_WRN | FMPI2C_CR2_START | FMPI2C_CR2_STOP);
- reg |= FMPI2C_CR2_START | FMPI2C_CR2_AUTOEND |
- addr_8bit | FMPI2C_CR2_SIZE(size) |
- (is_read ? FMPI2C_CR2_RD_WRN : 0);
- STM32_FMPI2C_CR2(port) = reg;
-
- return EC_SUCCESS;
-}
-
-/**
- * Set i2c clock rate..
- *
- * @param p I2C port struct
- */
-static void i2c_set_freq_port(const struct i2c_port_t *p)
-{
- int port = p->port;
- int freq = clock_get_freq();
-
- if (p->port == STM32F4_FMPI2C_PORT) {
- int prescalar;
- int actual;
- uint32_t reg;
-
- /* FMP I2C clock set. */
- STM32_FMPI2C_CR1(port) &= ~FMPI2C_CR1_PE;
- prescalar = (freq / (p->kbps * 1000 *
- (0x12 + 1 + 0xe + 1 + 1))) - 1;
- actual = freq / ((prescalar + 1) * (0x12 + 1 + 0xe + 1 + 1));
-
- reg = FMPI2C_TIMINGR_SCLL(0x12) |
- FMPI2C_TIMINGR_SCLH(0xe) |
- FMPI2C_TIMINGR_PRESC(prescalar);
- STM32_FMPI2C_TIMINGR(port) = reg;
-
- CPRINTS("port %d target %d, pre %d, act %d, reg 0x%08x",
- port, p->kbps, prescalar, actual, reg);
-
- STM32_FMPI2C_CR1(port) |= FMPI2C_CR1_PE;
- udelay(10);
- } else {
- /* Force peripheral reset and disable port */
- STM32_I2C_CR1(port) = STM32_I2C_CR1_SWRST;
- STM32_I2C_CR1(port) = 0;
-
- /* Set clock frequency */
- if (p->kbps > 100) {
- STM32_I2C_CCR(port) = freq / (2 * MSEC * p->kbps);
- } else {
- STM32_I2C_CCR(port) = STM32_I2C_CCR_FM
- | STM32_I2C_CCR_DUTY
- | (freq / (16 + 9 * MSEC * p->kbps));
- }
- STM32_I2C_CR2(port) = freq / SECOND;
- STM32_I2C_TRISE(port) = freq / SECOND + 1;
-
- /* Enable port */
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE;
- }
-}
-
-/**
- * Initialize on the specified I2C port.
- *
- * @param p the I2c port
- */
-static void i2c_init_port(const struct i2c_port_t *p)
-{
- int port = p->port;
-
- /* Configure GPIOs, clocks */
- gpio_config_module(MODULE_I2C, 1);
- clock_enable_module(MODULE_I2C, 1);
-
- if (p->port == STM32F4_FMPI2C_PORT) {
- /* FMP I2C block */
- /* Set timing (?) */
- STM32_FMPI2C_TIMINGR(port) = TIMINGR_THE_RIGHT_VALUE;
- udelay(10);
- /* Device enable */
- STM32_FMPI2C_CR1(port) |= FMPI2C_CR1_PE;
- /* Need to wait 3 APB cycles */
- udelay(10);
- /* Device only. */
- STM32_FMPI2C_OAR1(port) = 0;
- STM32_FMPI2C_CR2(port) |= FMPI2C_CR2_AUTOEND;
- } else {
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_SWRST;
- STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_SWRST;
- udelay(10);
- }
-
- /* Set up initial bus frequencies */
- i2c_set_freq_port(p);
-}
-
-/*****************************************************************************/
-/* Interface */
-
-/**
- * Clear status regs on the specified I2C port.
- *
- * @param port the I2c port
- */
-static void fmpi2c_clear_regs(int port)
-{
- /* Clear status */
- STM32_FMPI2C_ICR(port) = 0xffffffff;
-
- /* Clear start, stop, NACK, etc. bits to get us in a known state */
- STM32_FMPI2C_CR2(port) &= ~(FMPI2C_CR2_START | FMPI2C_CR2_STOP |
- FMPI2C_CR2_RD_WRN | FMPI2C_CR2_NACK |
- FMPI2C_CR2_AUTOEND |
- FMPI2C_CR2_SADD_MASK | FMPI2C_CR2_SIZE_MASK);
-}
-
-/**
- * Perform an i2c transaction
- *
- * @param port i2c port to use
- * @param addr_8bit the i2c address
- * @param out source buffer for data
- * @param out_bytes bytes of data to write
- * @param in destination buffer for data
- * @param in_bytes bytes of data to read
- * @param flags user cached I2C state
- *
- * @return EC_SUCCESS on success.
- */
-static int chip_fmpi2c_xfer(const int port, const uint16_t addr_8bit,
- 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);
- ASSERT(!started);
-
- if (STM32_FMPI2C_ISR(port) & FMPI2C_ISR_BUSY) {
- CPRINTS("fmpi2c port %d busy", port);
- return EC_ERROR_BUSY;
- }
-
- fmpi2c_clear_regs(port);
-
- /* No out bytes and no in bytes means just check for active */
- if (out_bytes || !in_bytes) {
- rv = send_fmpi2c_start(
- port, addr_8bit, out_bytes, FMPI2C_WRITE);
- if (rv)
- goto xfer_exit;
-
- /* Write data, if any */
- for (i = 0; i < out_bytes; i++) {
- rv = wait_fmpi2c_isr(port, FMPI2C_ISR_TXIS);
- if (rv)
- goto xfer_exit;
-
- /* Write next data byte */
- STM32_FMPI2C_TXDR(port) = out[i];
- }
-
- /* Wait for transaction STOP. */
- wait_fmpi2c_isr(port, FMPI2C_ISR_STOPF);
- }
-
- if (in_bytes) {
- int rv_start;
- const struct dma_option *dma = dma_rx_option + port;
-
- dma_start_rx(dma, in_bytes, in);
- i2c_dma_enable_tc_interrupt(dma->channel, port);
-
- rv_start = send_fmpi2c_start(
- port, addr_8bit, in_bytes, FMPI2C_READ);
- if (rv_start)
- goto xfer_exit;
-
- rv = wait_fmpi2c_isr(port, FMPI2C_ISR_RXNE);
- if (rv)
- goto xfer_exit;
- STM32_FMPI2C_CR1(port) |= FMPI2C_CR1_RXDMAEN;
-
- rv = task_wait_event_mask(
- TASK_EVENT_I2C_COMPLETION(port),
- DMA_TRANSFER_TIMEOUT_US);
- if (rv & TASK_EVENT_I2C_COMPLETION(port))
- rv = EC_SUCCESS;
- else
- rv = EC_ERROR_TIMEOUT;
-
- dma_disable(dma->channel);
- dma_disable_tc_interrupt(dma->channel);
-
- /* Validate i2c is STOPped */
- if (!rv)
- rv = wait_fmpi2c_isr(port, FMPI2C_ISR_STOPF);
-
- STM32_FMPI2C_CR1(port) &= ~FMPI2C_CR1_RXDMAEN;
- }
-
- xfer_exit:
- /* On error, queue a stop condition */
- if (rv) {
- flags |= I2C_XFER_STOP;
- STM32_FMPI2C_CR2(port) |= FMPI2C_CR2_STOP;
-
- /*
- * 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;
-
- CPRINTS("chip_fmpi2c_xfer start error; "
- "unwedging and resetting i2c %d", port);
-
- p = find_port(port);
- i2c_unwedge(port);
- i2c_init_port(p);
- }
- }
-
- /* 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_FMPI2C_ISR(port) & FMPI2C_ISR_BUSY))
- break;
- usleep(10);
- }
-
- /*
- * Allow bus to idle for at least one 100KHz clock = 10 us.
- * This allows peripherals on the bus to detect bus-idle before
- * the next start condition.
- */
- STM32_FMPI2C_CR1(port) &= ~FMPI2C_CR1_PE;
- usleep(10);
- STM32_FMPI2C_CR1(port) |= FMPI2C_CR1_PE;
- }
-
- return rv;
-}
-
-
-/**
- * Clear status regs on the specified I2C port.
- *
- * @param port the I2c port
- */
-static void i2c_clear_regs(int port)
-{
- /*
- * 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);
-}
-
-/*****************************************************************************
- * Exported functions declared in i2c.h
- */
-
-/* Perform an i2c transaction. */
-int chip_i2c_xfer(const int port, const uint16_t addr_flags,
- const uint8_t *out, int out_bytes,
- uint8_t *in, int in_bytes, int flags)
-{
- int addr_8bit = I2C_STRIP_FLAGS(addr_flags) << 1;
- int started = (flags & I2C_XFER_START) ? 0 : 1;
- int rv = EC_SUCCESS;
- int i;
- const struct i2c_port_t *p = find_port(port);
-
- ASSERT(out || !out_bytes);
- ASSERT(in || !in_bytes);
- ASSERT(!started);
-
- if (p->port == STM32F4_FMPI2C_PORT) {
- return chip_fmpi2c_xfer(port, addr_8bit,
- out, out_bytes,
- in, in_bytes, flags);
- }
-
- i2c_clear_regs(port);
-
- /* No out bytes and no in bytes means just check for active */
- if (out_bytes || !in_bytes) {
- rv = send_start(port, addr_8bit);
- 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];
-
- rv = wait_sr1(port, STM32_I2C_SR1_BTF);
- if (rv)
- goto xfer_exit;
- }
-
- /* 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) {
- int rv_start;
-
- const struct dma_option *dma = dma_rx_option + port;
-
- STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_POS;
- dma_start_rx(dma, in_bytes, in);
- i2c_dma_enable_tc_interrupt(dma->channel, port);
-
- /* 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;
-
- STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_STOP;
-
- STM32_I2C_CR2(port) |= STM32_I2C_CR2_LAST;
- STM32_I2C_CR2(port) |= STM32_I2C_CR2_DMAEN;
-
- rv_start = send_start(port, addr_8bit | 0x01);
-
- if ((in_bytes == 1) && (flags & I2C_XFER_STOP))
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP;
-
- if (!rv_start) {
- rv = task_wait_event_mask(
- TASK_EVENT_I2C_COMPLETION(port),
- DMA_TRANSFER_TIMEOUT_US);
- if (rv & TASK_EVENT_I2C_COMPLETION(port))
- rv = EC_SUCCESS;
- else
- rv = EC_ERROR_TIMEOUT;
- }
-
- dma_disable(dma->channel);
- dma_disable_tc_interrupt(dma->channel);
- STM32_I2C_CR2(port) &= ~STM32_I2C_CR2_DMAEN;
- /* Disable ack */
- STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_ACK;
-
- if (rv_start)
- rv = rv_start;
-
- /* Send stop. */
- STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_ACK;
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP;
- STM32_I2C_CR2(port) &= ~STM32_I2C_CR2_LAST;
- STM32_I2C_CR2(port) &= ~STM32_I2C_CR2_DMAEN;
- }
-
- xfer_exit:
- /* On error, queue a stop condition */
- if (rv) {
- flags |= I2C_XFER_STOP;
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP;
-
- /*
- * 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;
-
- CPRINTS("chip_i2c_xfer start error; "
- "unwedging and resetting i2c %d", port);
-
- p = find_port(port);
- i2c_unwedge(port);
- i2c_init_port(p);
- }
- }
-
- /* 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;
- usleep(10);
- }
-
- /*
- * Allow bus to idle for at least one 100KHz clock = 10 us.
- * This allows peripherals on the bus to detect bus-idle before
- * the next start condition.
- */
- usleep(10);
- }
-
- return rv;
-}
-
-int i2c_raw_get_scl(int port)
-{
- enum gpio_signal g;
-
- if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS)
- return gpio_get_level(g);
-
- /* If no SCL pin defined for this port, then return 1 to appear idle. */
- return 1;
-}
-
-int i2c_raw_get_sda(int port)
-{
- enum gpio_signal g;
-
- if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS)
- return gpio_get_level(g);
-
- /* If no SDA pin defined for this port, then return 1 to appear idle. */
- return 1;
-}
-
-int i2c_get_line_levels(int port)
-{
- return (i2c_raw_get_sda(port) ? I2C_LINE_SDA_HIGH : 0) |
- (i2c_raw_get_scl(port) ? I2C_LINE_SCL_HIGH : 0);
-}
-
-/*****************************************************************************/
-/* Hooks */
-
-#ifdef CONFIG_I2C_CONTROLLER
-/* Handle CPU clock changing frequency */
-static void i2c_freq_change(void)
-{
- const struct i2c_port_t *p = i2c_ports;
- int i;
-
- for (i = 0; i < i2c_ports_used; i++, p++)
- i2c_set_freq_port(p);
-}
-
-/* Handle an upcoming frequency change. */
-static void i2c_pre_freq_change_hook(void)
-{
- const struct i2c_port_t *p = i2c_ports;
- int i;
-
- /* Lock I2C ports so freq change can't interrupt an I2C transaction */
- for (i = 0; i < i2c_ports_used; i++, p++)
- i2c_lock(p->port, 1);
-}
-DECLARE_HOOK(HOOK_PRE_FREQ_CHANGE, i2c_pre_freq_change_hook, HOOK_PRIO_DEFAULT);
-
-/* Handle a frequency change */
-static void i2c_freq_change_hook(void)
-{
- const struct i2c_port_t *p = i2c_ports;
- int i;
-
- i2c_freq_change();
-
- /* Unlock I2C ports we locked in pre-freq change hook */
- for (i = 0; i < i2c_ports_used; i++, p++)
- i2c_lock(p->port, 0);
-}
-DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_change_hook, HOOK_PRIO_DEFAULT);
-#endif
-
-/*****************************************************************************/
-/* Peripheral */
-#ifdef CONFIG_HOSTCMD_I2C_ADDR_FLAGS
-/* Host command peripheral */
-/*
- * Buffer for received host command packets (including prefix byte on request,
- * and result/size on response). After any protocol-specific headers, the
- * buffers must be 32-bit aligned.
- */
-static uint8_t host_buffer_padded[I2C_MAX_HOST_PACKET_SIZE + 4 +
- CONFIG_I2C_EXTRA_PACKET_SIZE] __aligned(4);
-static uint8_t * const host_buffer = host_buffer_padded + 2;
-static uint8_t params_copy[I2C_MAX_HOST_PACKET_SIZE] __aligned(4);
-static int host_i2c_resp_port;
-static int tx_pending;
-static int tx_index, tx_end;
-static struct host_packet i2c_packet;
-
-static void i2c_send_response_packet(struct host_packet *pkt)
-{
- int size = pkt->response_size;
- uint8_t *out = host_buffer;
-
- /* Ignore host command in-progress */
- if (pkt->driver_result == EC_RES_IN_PROGRESS)
- return;
-
- /* Write result and size to first two bytes. */
- *out++ = pkt->driver_result;
- *out++ = size;
-
- /* host_buffer data range */
- tx_index = 0;
- tx_end = size + 2;
-
- /*
- * Set the transmitter to be in 'not full' state to keep sending
- * '0xec' in the event loop. Because of this, the controller i2c
- * doesn't need to snoop the response stream to abort transaction.
- */
- STM32_I2C_CR2(host_i2c_resp_port) |= STM32_I2C_CR2_ITBUFEN;
-}
-
-/* Process the command in the i2c host buffer */
-static void i2c_process_command(void)
-{
- char *buff = host_buffer;
-
- /*
- * TODO(crosbug.com/p/29241): Combine this functionality with the
- * i2c_process_command function in chip/stm32/i2c-stm32f.c to make one
- * host command i2c process function which handles all protocol
- * versions.
- */
- i2c_packet.send_response = i2c_send_response_packet;
-
- i2c_packet.request = (const void *)(&buff[1]);
- i2c_packet.request_temp = params_copy;
- i2c_packet.request_max = sizeof(params_copy);
- /* Don't know the request size so pass in the entire buffer */
- i2c_packet.request_size = I2C_MAX_HOST_PACKET_SIZE;
-
- /*
- * Stuff response at buff[2] to leave the first two bytes of
- * buffer available for the result and size to send over i2c. Note
- * that this 2-byte offset and the 2-byte offset from host_buffer
- * add up to make the response buffer 32-bit aligned.
- */
- i2c_packet.response = (void *)(&buff[2]);
- i2c_packet.response_max = I2C_MAX_HOST_PACKET_SIZE;
- i2c_packet.response_size = 0;
-
- if (*buff >= EC_COMMAND_PROTOCOL_3) {
- i2c_packet.driver_result = EC_RES_SUCCESS;
- } else {
- /* Only host command protocol 3 is supported. */
- i2c_packet.driver_result = EC_RES_INVALID_HEADER;
- }
- host_packet_receive(&i2c_packet);
-}
-
-#ifdef CONFIG_BOARD_I2C_ADDR_FLAGS
-static void i2c_send_board_response(int len)
-{
- /* host_buffer data range, beyond this length, will return 0xec */
- tx_index = 0;
- tx_end = len;
-
- /* enable transmit interrupt and use irq to send data back */
- STM32_I2C_CR2(host_i2c_resp_port) |= STM32_I2C_CR2_ITBUFEN;
-}
-
-static void i2c_process_board_command(int read, int addr, int len)
-{
- board_i2c_process(read, addr, len, &host_buffer[0],
- i2c_send_board_response);
-}
-#endif
-
-static void i2c_event_handler(int port)
-{
- volatile uint32_t i2c_cr1;
- volatile uint32_t i2c_sr2;
- volatile uint32_t i2c_sr1;
- static int rx_pending, buf_idx;
- static uint16_t addr_8bit;
-
- volatile uint32_t unused __attribute__((unused));
-
- i2c_cr1 = STM32_I2C_CR1(port);
- i2c_sr2 = STM32_I2C_SR2(port);
- i2c_sr1 = STM32_I2C_SR1(port);
-
- /*
- * Check for error conditions. Note, arbitration loss and bus error
- * are the only two errors we can get as a peripheral allowing clock
- * stretching and in non-SMBus mode.
- */
- if (i2c_sr1 & (STM32_I2C_SR1_ARLO | STM32_I2C_SR1_BERR)) {
- rx_pending = 0;
- tx_pending = 0;
- /* Disable buffer interrupt */
- STM32_I2C_CR2(port) &= ~STM32_I2C_CR2_ITBUFEN;
- /* Clear error status bits */
- STM32_I2C_SR1(port) &= ~(STM32_I2C_SR1_ARLO |
- STM32_I2C_SR1_BERR);
- }
-
- /* Transfer matched our peripheral address */
- if (i2c_sr1 & STM32_I2C_SR1_ADDR) {
- addr_8bit = ((i2c_sr2 & STM32_I2C_SR2_DUALF) ?
- STM32_I2C_OAR2(port) : STM32_I2C_OAR1(port)) & 0xfe;
- if (i2c_sr2 & STM32_I2C_SR2_TRA) {
- /* Transmitter peripheral */
- i2c_sr1 |= STM32_I2C_SR1_TXE;
-#ifdef CONFIG_BOARD_I2C_ADDR_FLAGS
- if (!rx_pending && !tx_pending) {
- tx_pending = 1;
- i2c_process_board_command(1, addr_8bit, 0);
- }
-#endif
- } else {
- /* Receiver peripheral */
- buf_idx = 0;
- rx_pending = 1;
- }
-
- /* Enable buffer interrupt to start receive/response */
- STM32_I2C_CR2(port) |= STM32_I2C_CR2_ITBUFEN;
- /* Clear ADDR bit */
- unused = STM32_I2C_SR1(port);
- unused = STM32_I2C_SR2(port);
- /* Inhibit stop mode when addressed until STOPF flag is set */
- disable_sleep(SLEEP_MASK_I2C_PERIPHERAL);
- }
-
- /* I2C in peripheral transmitter */
- if (i2c_sr2 & STM32_I2C_SR2_TRA) {
- if (i2c_sr1 & (STM32_I2C_SR1_BTF | STM32_I2C_SR1_TXE)) {
- if (tx_pending) {
- if (tx_index < tx_end) {
- STM32_I2C_DR(port) =
- host_buffer[tx_index++];
- } else {
- STM32_I2C_DR(port) = 0xec;
- tx_index = 0;
- tx_end = 0;
- tx_pending = 0;
- }
- } else if (rx_pending) {
- host_i2c_resp_port = port;
- /* Disable buffer interrupt */
- STM32_I2C_CR2(port) &= ~STM32_I2C_CR2_ITBUFEN;
-#ifdef CONFIG_BOARD_I2C_ADDR_FLAGS
- if ((addr_8bit >> 1) ==
- I2C_STRIP_FLAGS(
- CONFIG_BOARD_I2C_ADDR_FLAGS))
- i2c_process_board_command(1, addr_8bit,
- buf_idx);
- else
-#endif
- i2c_process_command();
- /* Reset host buffer */
- rx_pending = 0;
- tx_pending = 1;
- } else {
- STM32_I2C_DR(port) = 0xec;
- }
- }
- } else { /* I2C in peripheral receiver */
- if (i2c_sr1 & (STM32_I2C_SR1_BTF | STM32_I2C_SR1_RXNE))
- host_buffer[buf_idx++] = STM32_I2C_DR(port);
- }
-
- /* STOPF or AF */
- if (i2c_sr1 & (STM32_I2C_SR1_STOPF | STM32_I2C_SR1_AF)) {
- /* Disable buffer interrupt */
- STM32_I2C_CR2(port) &= ~STM32_I2C_CR2_ITBUFEN;
-
-#ifdef CONFIG_BOARD_I2C_ADDR_FLAGS
- if (rx_pending &&
- (addr_8b >> 1) ==
- I2C_STRIP_FLAGS(CONFIG_BOARD_I2C_ADDR_FLAGS))
- i2c_process_board_command(0, addr_8bit, buf_idx);
-#endif
- rx_pending = 0;
- tx_pending = 0;
-
- /* Clear AF */
- STM32_I2C_SR1(port) &= ~STM32_I2C_SR1_AF;
- /* Clear STOPF: read SR1 and write CR1 */
- unused = STM32_I2C_SR1(port);
- STM32_I2C_CR1(port) = i2c_cr1 | STM32_I2C_CR1_PE;
-
- /* No longer inhibit deep sleep after stop condition */
- enable_sleep(SLEEP_MASK_I2C_PERIPHERAL);
- }
-
- /* Enable again */
- if (!(i2c_cr1 & STM32_I2C_CR1_PE))
- STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE;
-}
-void i2c_event_interrupt(void) { i2c_event_handler(I2C_PORT_EC); }
-DECLARE_IRQ(IRQ_PERIPHERAL_EV, i2c_event_interrupt, 2);
-DECLARE_IRQ(IRQ_PERIPHERAL_ER, i2c_event_interrupt, 2);
-#endif
-
-
-/* Init all available i2c ports */
-void i2c_init(void)
-{
- const struct i2c_port_t *p = i2c_ports;
- int i;
-
- for (i = 0; i < i2c_ports_used; i++, p++)
- i2c_init_port(p);
-
-
-#ifdef CONFIG_HOSTCMD_I2C_ADDR_FLAGS
- /* Enable ACK */
- STM32_I2C_CR1(I2C_PORT_EC) |= STM32_I2C_CR1_ACK;
- /* Enable interrupts */
- STM32_I2C_CR2(I2C_PORT_EC) |= STM32_I2C_CR2_ITEVTEN
- | STM32_I2C_CR2_ITERREN;
- /* Setup host command peripheral */
- STM32_I2C_OAR1(I2C_PORT_EC) = STM32_I2C_OAR1_B14
- | (I2C_STRIP_ADDR(CONFIG_HOSTCMD_I2C_ADDR_FLAGS) << 1);
-#ifdef CONFIG_BOARD_I2C_ADDR_FLAGS
- STM32_I2C_OAR2(I2C_PORT_EC) = STM32_I2C_OAR2_ENDUAL
- | (I2C_STRIP_FLAGS(CONFIG_BOARD_I2C_ADDR_FLAGS) << 1);
-#endif
- task_enable_irq(IRQ_PERIPHERAL_EV);
- task_enable_irq(IRQ_PERIPHERAL_ER);
-#endif
-}