summaryrefslogtreecommitdiff
path: root/chip/npcx/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/npcx/i2c.c')
-rw-r--r--chip/npcx/i2c.c1224
1 files changed, 0 insertions, 1224 deletions
diff --git a/chip/npcx/i2c.c b/chip/npcx/i2c.c
deleted file mode 100644
index 26935f778f..0000000000
--- a/chip/npcx/i2c.c
+++ /dev/null
@@ -1,1224 +0,0 @@
-/* Copyright 2014 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.
- */
-
-/* I2C port module for Chrome EC */
-
-#include "clock.h"
-#include "clock_chip.h"
-#include "common.h"
-#include "console.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "i2c_chip.h"
-#include "registers.h"
-#include "task.h"
-#include "timer.h"
-#include "util.h"
-
-#if !(DEBUG_I2C)
-#define CPUTS(...)
-#define CPRINTS(...)
-#define CPRINTF(...)
-#else
-#define CPUTS(outstr) cputs(CC_I2C, outstr)
-#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
-#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
-#endif
-
-/* Timeout for device should be available after reset (SMBus spec. unit:ms) */
-#define I2C_MAX_TIMEOUT 35
-/*
- * Timeout for SCL held to low by peripheral device. (SMBus spec. unit:ms).
- * Some I2C devices may violate this timing and clock stretch for longer.
- * TODO: Consider increasing this timeout.
- */
-#define I2C_MIN_TIMEOUT 25
-
-/*
- * I2C module that supports FIFO mode has 32 bytes Tx FIFO and
- * 32 bytes Rx FIFO.
- */
-#define NPCX_I2C_FIFO_MAX_SIZE 32
-
-/* Macro functions of I2C */
-#define I2C_START(ctrl) SET_BIT(NPCX_SMBCTL1(ctrl), NPCX_SMBCTL1_START)
-#define I2C_STOP(ctrl) SET_BIT(NPCX_SMBCTL1(ctrl), NPCX_SMBCTL1_STOP)
-#define I2C_NACK(ctrl) SET_BIT(NPCX_SMBCTL1(ctrl), NPCX_SMBCTL1_ACK)
-/* I2C module automatically stall bus after sending peripheral address */
-#define I2C_STALL(ctrl) SET_BIT(NPCX_SMBCTL1(ctrl), NPCX_SMBCTL1_STASTRE)
-#define I2C_WRITE_BYTE(ctrl, data) (NPCX_SMBSDA(ctrl) = data)
-#define I2C_READ_BYTE(ctrl, data) (data = NPCX_SMBSDA(ctrl))
-#define I2C_TX_FIFO_OCCUPIED(ctrl) (NPCX_SMBTXF_STS(ctrl) & 0x3F)
-#define I2C_TX_FIFO_AVAILABLE(ctrl) \
- (NPCX_I2C_FIFO_MAX_SIZE - I2C_TX_FIFO_OCCUPIED(ctrl))
-
-#define I2C_RX_FIFO_OCCUPIED(ctrl) (NPCX_SMBRXF_STS(ctrl) & 0x3F)
-#define I2C_RX_FIFO_AVAILABLE(ctrl) \
- (NPCX_I2C_FIFO_MAX_SIZE - I2C_RX_FIFO_OCCUPIED(ctrl))
-/* Drive the SCL signal to low */
-#define I2C_SCL_STALL(ctrl) \
- (NPCX_SMBCTL3(ctrl) = \
- (NPCX_SMBCTL3(ctrl) & ~BIT(NPCX_SMBCTL3_SCL_LVL)) | \
- BIT(NPCX_SMBCTL3_SDA_LVL))
-/*
- * Release the SCL signal to be pulled up to high level.
- * Note: The SCL might be still driven low either by I2C module or external
- * devices connected to ths bus.
- */
-#define I2C_SCL_FREE(ctrl) \
- (NPCX_SMBCTL3(ctrl) |= BIT(NPCX_SMBCTL3_SCL_LVL) | \
- BIT(NPCX_SMBCTL3_SDA_LVL))
-
-/* Error values that functions can return */
-enum smb_error {
- SMB_OK = 0, /* No error */
- SMB_CH_OCCUPIED, /* Channel is already occupied */
- SMB_MEM_POOL_INIT_ERROR, /* Memory pool initialization error */
- SMB_BUS_FREQ_ERROR, /* SMbus freq was not valid */
- SMB_INVLAID_REGVALUE, /* Invalid SMbus register value */
- SMB_UNEXIST_CH_ERROR, /* Channel does not exist */
- SMB_NO_SUPPORT_PTL, /* Not support SMBus Protocol */
- SMB_BUS_ERROR, /* Encounter bus error */
- SMB_NO_ADDRESS_MATCH, /* No peripheral address match */
- /* (Controller Mode) */
- SMB_READ_DATA_ERROR, /* Read data for SDA error */
- SMB_READ_OVERFLOW_ERROR, /* Read data over than we predict */
- SMB_TIMEOUT_ERROR, /* Timeout expired */
- SMB_MODULE_ISBUSY, /* Module is occupied by other device */
- SMB_BUS_BUSY, /* SMBus is occupied by other device */
-};
-
-/*
- * Internal SMBus Interface driver states values, which reflect events
- * which occurred on the bus
- */
-enum smb_oper_state_t {
- SMB_IDLE,
- SMB_CONTROLLER_START,
- SMB_WRITE_OPER,
- SMB_READ_OPER,
- SMB_FAKE_READ_OPER,
- SMB_REPEAT_START,
- SMB_WRITE_SUSPEND,
- SMB_READ_SUSPEND,
-};
-
-/* I2C controller state data */
-struct i2c_status {
- int flags; /* Flags (I2C_XFER_*) */
- const uint8_t *tx_buf; /* Entry pointer of transmit buffer */
- uint8_t *rx_buf; /* Entry pointer of receive buffer */
- uint16_t sz_txbuf; /* Size of Tx buffer in bytes */
- uint16_t sz_rxbuf; /* Size of rx buffer in bytes */
- uint16_t idx_buf; /* Current index of Tx/Rx buffer */
- uint16_t addr_flags;/* Target address */
- enum smb_oper_state_t oper_state;/* Smbus operation state */
- enum smb_error err_code; /* Error code */
- int task_waiting; /* Task waiting on controller */
- uint32_t timeout_us;/* Transaction timeout */
- uint16_t kbps; /* Speed */
-};
-/* I2C controller state data array */
-static struct i2c_status i2c_stsobjs[I2C_CONTROLLER_COUNT];
-
-/* I2C timing setting */
-struct i2c_timing {
- uint8_t clock; /* I2C source clock. (Unit: MHz)*/
- uint8_t HLDT; /* I2C hold-time. (Unit: clocks) */
- uint8_t k1; /* k1 = SCL low-time (Unit: clocks) */
- uint8_t k2; /* k2 = SCL high-time (Unit: clocks) */
-};
-
-/* I2C timing setting array of 400K & 1M Hz */
-static const struct i2c_timing i2c_400k_timings[] = {
- {20, 7, 32, 22},
- {15, 7, 24, 18},};
-const unsigned int i2c_400k_timing_used = ARRAY_SIZE(i2c_400k_timings);
-
-static const struct i2c_timing i2c_1m_timings[] = {
- {20, 7, 16, 10},
- {15, 7, 14, 10},};
-const unsigned int i2c_1m_timing_used = ARRAY_SIZE(i2c_1m_timings);
-
-/* IRQ for each port */
-const uint32_t i2c_irqs[I2C_CONTROLLER_COUNT] = {
- NPCX_IRQ_SMB1, NPCX_IRQ_SMB2, NPCX_IRQ_SMB3, NPCX_IRQ_SMB4,
-#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7
- NPCX_IRQ_SMB5, NPCX_IRQ_SMB6, NPCX_IRQ_SMB7, NPCX_IRQ_SMB8,
-#endif
-};
-BUILD_ASSERT(ARRAY_SIZE(i2c_irqs) == I2C_CONTROLLER_COUNT);
-
-static void i2c_init_bus(int controller)
-{
- /* Enable FIFO mode */
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT))
- SET_BIT(NPCX_SMBFIF_CTL(controller), NPCX_SMBFIF_CTL_FIFO_EN);
-
- /* Enable module - before configuring CTL1 */
- SET_BIT(NPCX_SMBCTL2(controller), NPCX_SMBCTL2_ENABLE);
-
- /* Enable SMB interrupt and New Address Match interrupt source */
- SET_BIT(NPCX_SMBCTL1(controller), NPCX_SMBCTL1_NMINTE);
- SET_BIT(NPCX_SMBCTL1(controller), NPCX_SMBCTL1_INTEN);
-}
-
-int i2c_bus_busy(int controller)
-{
- return IS_BIT_SET(NPCX_SMBCST(controller), NPCX_SMBCST_BB) ? 1 : 0;
-}
-
-static int i2c_wait_stop_completed(int controller, int timeout)
-{
- if (timeout <= 0)
- return EC_ERROR_INVAL;
-
- /* Wait till STOP condition is generated. ie. I2C bus is idle. */
- while (timeout > 0) {
- if (!IS_BIT_SET(NPCX_SMBCTL1(controller), NPCX_SMBCTL1_STOP))
- break;
- if (--timeout > 0)
- msleep(1);
- }
-
- if (timeout)
- return EC_SUCCESS;
- else
- return EC_ERROR_TIMEOUT;
-}
-
-static void i2c_abort_data(int controller)
-{
- /* Clear NEGACK, STASTR and BER bits */
- SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_BER);
- SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_STASTR);
- SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_NEGACK);
-
- /* Wait till STOP condition is generated */
- if (i2c_wait_stop_completed(controller, I2C_MAX_TIMEOUT)
- != EC_SUCCESS) {
- cprintf(CC_I2C, "Abort i2c %02x fail!\n", controller);
- /* Clear BB (BUS BUSY) bit */
- SET_BIT(NPCX_SMBCST(controller), NPCX_SMBCST_BB);
- return;
- }
-
- /* Clear BB (BUS BUSY) bit */
- SET_BIT(NPCX_SMBCST(controller), NPCX_SMBCST_BB);
-}
-
-static int i2c_reset(int controller)
-{
- uint16_t timeout = I2C_MAX_TIMEOUT;
-
- /* Disable the SMB module */
- CLEAR_BIT(NPCX_SMBCTL2(controller), NPCX_SMBCTL2_ENABLE);
-
- while (--timeout) {
- /* WAIT FOR SCL & SDA IS HIGH */
- if (IS_BIT_SET(NPCX_SMBCTL3(controller), NPCX_SMBCTL3_SCL_LVL)
- && IS_BIT_SET(NPCX_SMBCTL3(controller), NPCX_SMBCTL3_SDA_LVL))
- break;
- msleep(1);
- }
-
- if (timeout == 0) {
- cprintf(CC_I2C, "Reset i2c %02x fail!\n", controller);
- return 0;
- }
-
- /* Init the SMB module again */
- i2c_init_bus(controller);
- return 1;
-}
-
-static void i2c_select_bank(int controller, int bank)
-{
- if (bank)
- SET_BIT(NPCX_SMBCTL3(controller), NPCX_SMBCTL3_BNK_SEL);
- else
- CLEAR_BIT(NPCX_SMBCTL3(controller), NPCX_SMBCTL3_BNK_SEL);
-}
-
-static void i2c_stall_bus(int controller, int stall)
-{
- i2c_select_bank(controller, 0);
- /*
- * Enable the writing to SCL_LVL and SDA_LVL bit in
- * SMBnCTL3 register. Then, firmware can set SCL_LVL to 0 to
- * stall the bus when needed. Note: this register should be
- * accessed when bank = 0.
- */
- SET_BIT(NPCX_SMBCTL4(controller), NPCX_SMBCTL4_LVL_WE);
- if (stall)
- I2C_SCL_STALL(controller);
- else
- I2C_SCL_FREE(controller);
- /*
- * Disable the writing to SCL_LVL and SDA_LVL bit in
- * SMBnCTL3 register. It will prevent form changing the level of
- * SCL/SDA when touching other bits in SMBnCTL3 register.
- */
- CLEAR_BIT(NPCX_SMBCTL4(controller), NPCX_SMBCTL4_LVL_WE);
- i2c_select_bank(controller, 1);
-}
-
-static void i2c_recovery(int controller, volatile struct i2c_status *p_status)
-{
- cprintf(CC_I2C,
- "i2c %d recovery! error code is %d, current state is %d\n",
- controller, p_status->err_code, p_status->oper_state);
-
- /* Make sure the bus is not stalled before exit. */
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT))
- i2c_stall_bus(controller, 0);
-
- /* Abort data, wait for STOP condition completed. */
- i2c_abort_data(controller);
-
- /* Reset i2c controller by re-enable i2c controller*/
- if (!i2c_reset(controller))
- return;
-
- /* Restore to idle status */
- p_status->oper_state = SMB_IDLE;
-}
-
-/*
- * This function can be called in either single-byte mode or FIFO mode.
- * In single-byte mode - it always write 1 byte to SMBSDA register at one time.
- * In FIFO mode - write as many as available bytes in FIFO at one time.
- */
-static void i2c_fifo_write_data(int controller)
-{
- int len, fifo_avail, i;
-
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- len = 1;
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT)) {
- len = p_status->sz_txbuf - p_status->idx_buf;
- fifo_avail = I2C_TX_FIFO_AVAILABLE(controller);
- len = MIN(len, fifo_avail);
- }
- for (i = 0; i < len; i++) {
- I2C_WRITE_BYTE(controller,
- p_status->tx_buf[p_status->idx_buf++]);
- CPRINTF("%02x ",
- p_status->tx_buf[p_status->idx_buf - 1]);
- }
- CPRINTF("\n");
-}
-
-enum smb_error i2c_controller_transaction(int controller)
-{
- /* Set i2c mode to object */
- int events = 0;
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- /* Switch to bank 1 to access I2C FIO registers */
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT))
- i2c_select_bank(controller, 1);
-
- /* Assign current SMB status of controller */
- if (p_status->oper_state == SMB_IDLE) {
- /* New transaction */
- p_status->oper_state = SMB_CONTROLLER_START;
- /* Clear FIFO and status bit */
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT)) {
- NPCX_SMBFIF_CTS(controller) =
- BIT(NPCX_SMBFIF_CTS_RXF_TXE) |
- BIT(NPCX_SMBFIF_CTS_CLR_FIFO);
- }
- } 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;
- CPRINTS("-W");
- /*
- * This function can be called in either single-byte
- * mode or FIFO mode.
- */
- i2c_fifo_write_data(controller);
- }
- } else if (p_status->oper_state == SMB_READ_SUSPEND) {
- if (!IS_ENABLED(NPCX_I2C_FIFO_SUPPORT)) {
- /*
- * Do extra read if read length is 1 and I2C_XFER_STOP
- * is set simultaneously.
- */
- if (p_status->sz_rxbuf == 1 &&
- (p_status->flags & I2C_XFER_STOP)) {
- /*
- * Since SCL is released after reading last
- * byte from previous transaction, adding a
- * extra byte for next transaction which let
- * ec sets NACK bit in time is necessary.
- * Or i2c controller cannot generate STOP
- * when the last byte is ACK during receiving.
- */
- p_status->sz_rxbuf++;
- p_status->oper_state = SMB_FAKE_READ_OPER;
- } else
- /*
- * Need to read the other bytes from
- * next transaction
- */
- p_status->oper_state = SMB_READ_OPER;
- }
- } else
- cprintf(CC_I2C, "Unexpected i2c state machine! %d\n",
- p_status->oper_state);
-
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT)) {
- if (p_status->sz_rxbuf > 0) {
- if (p_status->sz_rxbuf > NPCX_I2C_FIFO_MAX_SIZE) {
- /* Set RX threshold = FIFO_MAX_SIZE */
- SET_FIELD(NPCX_SMBRXF_CTL(controller),
- NPCX_SMBRXF_CTL_RX_THR,
- NPCX_I2C_FIFO_MAX_SIZE);
- } else {
- /*
- * set RX threshold = remaining data bytes
- * (it should be <= FIFO_MAX_SIZE)
- */
- SET_FIELD(NPCX_SMBRXF_CTL(controller),
- NPCX_SMBRXF_CTL_RX_THR,
- p_status->sz_rxbuf);
- /*
- * Set LAST bit generate the NACK at the
- * last byte of the data group in FIFO
- */
- if (p_status->flags & I2C_XFER_STOP) {
- SET_BIT(NPCX_SMBRXF_CTL(controller),
- NPCX_SMBRXF_CTL_LAST);
- }
- }
-
- /* Free the stalled SCL signal */
- if (p_status->oper_state == SMB_READ_SUSPEND) {
- p_status->oper_state = SMB_READ_OPER;
- i2c_stall_bus(controller, 0);
- }
- }
- }
-
- /* Generate a START condition */
- if (p_status->oper_state == SMB_CONTROLLER_START ||
- p_status->oper_state == SMB_REPEAT_START) {
- I2C_START(controller);
- CPUTS("ST");
- }
-
- /* Enable event and error interrupts */
- task_enable_irq(i2c_irqs[controller]);
-
- /* Wait for transfer complete or timeout */
- events = task_wait_event_mask(TASK_EVENT_I2C_IDLE,
- p_status->timeout_us);
-
- /* Disable event and error interrupts */
- task_disable_irq(i2c_irqs[controller]);
-
- /*
- * Accessing FIFO register is only needed during transaction.
- * Switch back to bank 0 at the end of transaction
- */
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT))
- i2c_select_bank(controller, 0);
-
- /*
- * If Stall-After-Start mode is still enabled since NACK or BUS error
- * occurs, disable it.
- */
- if (IS_BIT_SET(NPCX_SMBCTL1(controller), NPCX_SMBCTL1_STASTRE))
- CLEAR_BIT(NPCX_SMBCTL1(controller), NPCX_SMBCTL1_STASTRE);
-
- /* Handle bus timeout */
- if ((events & TASK_EVENT_I2C_IDLE) == 0) {
- p_status->err_code = SMB_TIMEOUT_ERROR;
- /* Recovery I2C controller */
- i2c_recovery(controller, p_status);
- }
- /* Recovery bus if we encounter bus error */
- else if (p_status->err_code == SMB_BUS_ERROR)
- i2c_recovery(controller, p_status);
-
- /* Wait till STOP condition is generated for normal transaction */
- if (p_status->err_code == SMB_OK && i2c_wait_stop_completed(controller,
- I2C_MIN_TIMEOUT) != EC_SUCCESS) {
- cprintf(CC_I2C,
- "STOP fail! scl %02x is held by slave device!\n",
- controller);
- p_status->err_code = SMB_TIMEOUT_ERROR;
- }
-
- return p_status->err_code;
-}
-
-/* Issue stop condition if necessary and end transaction */
-void i2c_done(int controller)
-{
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- /* 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 RXF_TXE bit (RX FIFO full/TX FIFO empty) */
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT))
- NPCX_SMBFIF_CTS(controller) =
- BIT(NPCX_SMBFIF_CTS_RXF_TXE);
-
- /* Clear SDAST by writing mock 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 controller stall SCL
- * and forbid SDAST generate interrupt
- * until common layer start other transactions
- */
- if (p_status->oper_state == SMB_WRITE_SUSPEND)
- task_disable_irq(i2c_irqs[controller]);
-
- /* Notify upper layer */
- task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE);
- CPUTS("-END");
-}
-
-static void i2c_handle_receive(int controller)
-{
- uint8_t data;
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- /* 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");
- } else {
- /*
- * Disable interrupt before i2c controller read SDA
- * reg (stall SCL) and forbid SDAST generate
- * interrupt until starting other transactions
- */
- task_disable_irq(i2c_irqs[controller]);
- }
- }
- /* 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 data for SMBSDA */
- I2C_READ_BYTE(controller, data);
- CPRINTS("-R(%02x)", data);
-
- /* Read to buf. Skip last byte if meet SMB_FAKE_READ_OPER */
- if (p_status->oper_state == SMB_FAKE_READ_OPER &&
- p_status->idx_buf == (p_status->sz_rxbuf - 1))
- p_status->idx_buf++;
- else
- 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);
- CPUTS("-END");
- }
-}
-
-static void i2c_fifo_read_data(int controller, uint8_t bytes_in_fifo)
-{
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- while (bytes_in_fifo--) {
- uint8_t data;
-
- data = NPCX_SMBSDA(controller);
- p_status->rx_buf[p_status->idx_buf++] = data;
- CPRINTF("%02x ", data);
- }
- CPRINTF("\n");
-}
-
-static void i2c_fifo_handle_receive(int controller)
-{
- uint8_t bytes_in_fifo, remaining_bytes;
-
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- /*
- * Clear RX_THST bit (RX-FIFO Threshold Status).
- * It is set when RX_BYTES = RX_THR after being RX_BYTES < RX_THR
- */
- SET_BIT(NPCX_SMBRXF_STS(controller), NPCX_SMBRXF_STS_RX_THST);
- SET_BIT(NPCX_SMBFIF_CTS(controller), NPCX_SMBFIF_CTS_RXF_TXE);
-
- bytes_in_fifo = I2C_RX_FIFO_OCCUPIED(controller);
- remaining_bytes = p_status->sz_rxbuf - p_status->idx_buf;
- if (remaining_bytes - bytes_in_fifo <= 0) {
- /*
- * Last byte is about to be read - end of transaction.
- * Stop should be set before reading last byte.
- */
- if (p_status->flags & I2C_XFER_STOP) {
- I2C_STOP(controller);
- CPUTS("-FSP");
- } else {
- task_disable_irq(i2c_irqs[controller]);
- /*
- * The I2C bus will be freed from stalled and continue
- * to recevie data when reading data from FIFO.
- * Pull SCL signal down to stall the bus manually.
- * SCL signal will be freed when it gets a new I2C
- * transaction call from common layer.
- */
- i2c_stall_bus(controller, 1);
- }
-
- CPRINTS("-LFR");
- i2c_fifo_read_data(controller, remaining_bytes);
- } else {
- CPRINTS("-FR");
- /*
- * The I2C bus will be freed from stalled and continue to
- * recevie data when reading data from FIFO.
- * This may caue driver cannot set the new Rx threshold in time.
- * Manually stall SCL signal until the new Rx threshold is set.
- */
- i2c_stall_bus(controller, 1);
- i2c_fifo_read_data(controller, bytes_in_fifo);
- remaining_bytes = p_status->sz_rxbuf - p_status->idx_buf;
- if (remaining_bytes > 0) {
- if (remaining_bytes > NPCX_I2C_FIFO_MAX_SIZE) {
- SET_FIELD(NPCX_SMBRXF_CTL(controller),
- NPCX_SMBRXF_CTL_RX_THR,
- NPCX_I2C_FIFO_MAX_SIZE);
- } else {
- SET_FIELD(NPCX_SMBRXF_CTL(controller),
- NPCX_SMBRXF_CTL_RX_THR,
- remaining_bytes);
- if (p_status->flags & I2C_XFER_STOP) {
- SET_BIT(NPCX_SMBRXF_CTL(controller),
- NPCX_SMBRXF_CTL_LAST);
- CPRINTS("-FGNA");
- }
- }
-
- }
- i2c_stall_bus(controller, 0);
-
- }
- /* 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);
- CPUTS("-END");
- }
-
-}
-
-static void i2c_handle_sda_irq(int controller)
-{
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
- uint8_t addr_8bit = I2C_STRIP_FLAGS(p_status->addr_flags) << 1;
-
- /* 1 Issue Start is successful ie. write address byte */
- if (p_status->oper_state == SMB_CONTROLLER_START
- || p_status->oper_state == SMB_REPEAT_START) {
- /* Prepare address byte */
- if (p_status->sz_txbuf == 0) {/* Receive mode */
- p_status->oper_state = SMB_READ_OPER;
- /*
- * Receiving one or zero bytes - stall bus after
- * START condition. If there's no peripheral
- * devices on bus, FW needn't to set ACK bit.
- */
- if (p_status->sz_rxbuf < 2)
- I2C_STALL(controller);
-
- /* Write the address to the bus R bit*/
- I2C_WRITE_BYTE(controller, (addr_8bit | 0x1));
- CPRINTS("-ARR-0x%02x", addr_8bit);
- } else {/* Transmit mode */
- p_status->oper_state = SMB_WRITE_OPER;
- /* Write the address to the bus W bit*/
- I2C_WRITE_BYTE(controller, addr_8bit);
- CPRINTS("-ARW-0x%02x", addr_8bit);
- }
- /* Completed handling START condition */
- return;
- }
- /* 2 Handle controller 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)
- i2c_done(controller);
- /*
- * need to restart & send peripheral address
- * immediately
- */
- else {
- /*
- * 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.
- * Set NACK (ACK bit in the SMBnCTL1 register)
- * only in the single-byte mode.
- * In FIFO mode, NACK is set via LAST bit
- * in the SMBnTXF_CTL register.
- */
- if (p_status->sz_rxbuf == 1 &&
- (p_status->flags & I2C_XFER_STOP) &&
- !IS_ENABLED(NPCX_I2C_FIFO_SUPPORT)) {
- I2C_NACK(controller);
- CPUTS("-GNA");
- }
- /* Write the address to the bus R bit*/
- I2C_WRITE_BYTE(controller,
- (addr_8bit | 0x1));
- CPUTS("-ARR");
- }
- }
- /*
- * write next byte (not last byte and not peripheral
- * address)
- */
- else {
- /*
- * This function can be called in either single-byte
- * mode or FIFO mode.
- */
- CPRINTS("-W");
- i2c_fifo_write_data(controller);
- }
- }
- /*
- * 3 Handle controller read operation (read or after a write
- * operation)
- */
- else if (p_status->oper_state == SMB_READ_OPER ||
- p_status->oper_state == SMB_FAKE_READ_OPER) {
- if (IS_ENABLED(NPCX_I2C_FIFO_SUPPORT))
- i2c_fifo_handle_receive(controller);
- else
- i2c_handle_receive(controller);
- }
-}
-
-static void i2c_controller_int_handler(int controller)
-{
- volatile struct i2c_status *p_status = i2c_stsobjs + controller;
-
- /* Condition 1 : A Bus Error has been identified */
- if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_BER)) {
- uint8_t __attribute__((unused)) data;
- /* Generate a STOP condition */
- I2C_STOP(controller);
- CPUTS("-SP");
- /* Clear BER Bit */
- SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_BER);
- /* Make sure peripheral doesn't hold bus by reading */
- I2C_READ_BYTE(controller, data);
-
- /* Set error code */
- p_status->err_code = SMB_BUS_ERROR;
- /* Notify upper layer */
- p_status->oper_state = SMB_IDLE;
- task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE);
- CPUTS("-BER");
-
- /*
- * Disable smb's interrupts to forbid ec to enter ISR again
- * before executing error recovery.
- */
- task_disable_irq(i2c_irqs[controller]);
-
- /* return for executing error recovery immediately */
- return;
- }
-
- /* Condition 2: A negative acknowledge has occurred */
- if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_NEGACK)) {
- /* Generate a STOP condition */
- I2C_STOP(controller);
- CPUTS("-SP");
- /* Clear NEGACK Bit */
- SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_NEGACK);
- /* Set error code */
- p_status->err_code = SMB_NO_ADDRESS_MATCH;
- /* Notify upper layer */
- p_status->oper_state = SMB_IDLE;
- task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE);
- CPUTS("-NA");
- }
-
- /* Condition 3: A Stall after START has occurred for READ-BYTE */
- if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_STASTR)) {
- CPUTS("-STL");
-
- /* Disable Stall-After-Start mode first */
- CLEAR_BIT(NPCX_SMBCTL1(controller), NPCX_SMBCTL1_STASTRE);
-
- /*
- * Generate stop condition and return success status since
- * ACK received on zero-byte transaction.
- */
- if (p_status->sz_rxbuf == 0)
- i2c_done(controller);
- /*
- * Otherwise we have a one-byte transaction, so NACK after
- * receiving next byte, if requested.
- * Set NACK (ACK bit in the SMBnCTL1 register) only in the
- * single-byte mode.
- * In FIFO mode, NACK is set via LAST bit in the SMBnTXF_CTL
- * register.
- */
- else if ((p_status->flags & I2C_XFER_STOP) &&
- !IS_ENABLED(NPCX_I2C_FIFO_SUPPORT)) {
- I2C_NACK(controller);
- }
-
- /* Clear STASTR to release SCL after setting NACK/STOP bits */
- SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_STASTR);
- }
-
- /* Condition 4: SDA status is set - transmit or receive */
- if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_SDAST)) {
- i2c_handle_sda_irq(controller);
-#if DEBUG_I2C
- /* SDAST still issued with unexpected state machine */
- if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_SDAST) &&
- p_status->oper_state != SMB_WRITE_SUSPEND) {
- cprints(CC_I2C, "i2c %d unknown state %d, error %d\n",
- controller, p_status->oper_state, p_status->err_code);
- }
-#endif
- }
-}
-
-/**
- * Handle an interrupt on the specified controller.
- *
- * @param controller I2C controller generating interrupt
- */
-void handle_interrupt(int controller)
-{
- i2c_controller_int_handler(controller);
-}
-
-void i2c0_interrupt(void) { handle_interrupt(0); }
-void i2c1_interrupt(void) { handle_interrupt(1); }
-void i2c2_interrupt(void) { handle_interrupt(2); }
-void i2c3_interrupt(void) { handle_interrupt(3); }
-#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7
-void i2c4_interrupt(void) { handle_interrupt(4); }
-void i2c5_interrupt(void) { handle_interrupt(5); }
-void i2c6_interrupt(void) { handle_interrupt(6); }
-void i2c7_interrupt(void) { handle_interrupt(7); }
-#endif
-
-DECLARE_IRQ(NPCX_IRQ_SMB1, i2c0_interrupt, 4);
-DECLARE_IRQ(NPCX_IRQ_SMB2, i2c1_interrupt, 4);
-DECLARE_IRQ(NPCX_IRQ_SMB3, i2c2_interrupt, 4);
-DECLARE_IRQ(NPCX_IRQ_SMB4, i2c3_interrupt, 4);
-#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7
-DECLARE_IRQ(NPCX_IRQ_SMB5, i2c4_interrupt, 4);
-DECLARE_IRQ(NPCX_IRQ_SMB6, i2c5_interrupt, 4);
-DECLARE_IRQ(NPCX_IRQ_SMB7, i2c6_interrupt, 4);
-DECLARE_IRQ(NPCX_IRQ_SMB8, i2c7_interrupt, 4);
-#endif
-
-/*****************************************************************************/
-/* IC specific low-level driver */
-
-void i2c_set_timeout(int port, uint32_t timeout)
-{
- int ctrl = i2c_port_to_controller(port);
-
- /* Return if i2c_port_to_controller() returned an error */
- if (ctrl < 0)
- return;
-
- /* Param is port, but timeout is stored by-controller. */
- i2c_stsobjs[ctrl].timeout_us =
- timeout ? timeout : I2C_TIMEOUT_DEFAULT_US;
-}
-
-int chip_i2c_xfer(const int port,
- const uint16_t addr_flags,
- const uint8_t *out, int out_size,
- uint8_t *in, int in_size, int flags)
-{
- volatile struct i2c_status *p_status;
- int ctrl = i2c_port_to_controller(port);
-
- /* Return error if i2c_port_to_controller() returned an error */
- if (ctrl < 0)
- return EC_ERROR_INVAL;
-
- /* Skip unnecessary transaction */
- if (out_size == 0 && in_size == 0)
- return EC_SUCCESS;
-
- p_status = i2c_stsobjs + ctrl;
-
- /* Assign current task ID */
- p_status->task_waiting = task_get_current();
-
- /* 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;
- p_status->addr_flags = addr_flags;
-
- /* 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) &&
- /* Ignore busy bus for repeated start */
- p_status->oper_state != SMB_WRITE_SUSPEND &&
- (i2c_bus_busy(ctrl)
- || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) {
- int ret;
-
- /* Attempt to unwedge the i2c port */
- ret = i2c_unwedge(port);
- if (ret)
- return ret;
- p_status->err_code = SMB_BUS_BUSY;
- /* recover i2c controller */
- i2c_recovery(ctrl, p_status);
- /* Select port again for recovery */
- i2c_select_port(port);
- }
-
- CPUTS("\n");
-
- /* Start controller transaction */
- i2c_controller_transaction(ctrl);
-
- /* Reset task ID */
- p_status->task_waiting = TASK_ID_INVALID;
-
- CPRINTS("-Err:0x%02x", p_status->err_code);
-
- return (p_status->err_code == SMB_OK) ? EC_SUCCESS : EC_ERROR_UNKNOWN;
-}
-
-/**
- * Return raw I/O line levels (I2C_LINE_*) for a port when port is in alternate
- * function mode.
- *
- * @param port Port to check
- * @return State of SCL/SDA bit 0/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);
-}
-
-int i2c_raw_get_scl(int port)
-{
- enum gpio_signal g;
-
- /*
- * Check do we support this port of i2c and return gpio number of scl.
- * Please notice we cannot read voltage level from GPIO in M4 EC
- */
- if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS) {
- if (i2c_is_raw_mode(port))
- return gpio_get_level(g);
- else
- return IS_BIT_SET(NPCX_SMBCTL3(
- i2c_port_to_controller(port)), NPCX_SMBCTL3_SCL_LVL);
- }
-
- /* 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;
-
- /*
- * Check do we support this port of i2c and return gpio number of scl.
- * Please notice we cannot read voltage level from GPIO in M4 EC
- */
- if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS) {
- if (i2c_is_raw_mode(port))
- return gpio_get_level(g);
- else
- return IS_BIT_SET(NPCX_SMBCTL3(
- i2c_port_to_controller(port)), NPCX_SMBCTL3_SDA_LVL);
- }
-
-
- /* If no SDA pin defined for this port, then return 1 to appear idle */
- return 1;
-}
-
-/*****************************************************************************/
-
-static void i2c_port_set_freq(const int ctrl, const int bus_freq_kbps)
-{
- int freq, j;
- int scl_freq;
- const struct i2c_timing *pTiming;
- int i2c_timing_used;
-
-#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7
- /*
- * SMB0/1/4/5/6/7 use APB3 clock
- * SMB2/3 use APB2 clock
- */
- freq = (ctrl < 2 || ctrl > 3) ?
- clock_get_apb3_freq() : clock_get_apb2_freq();
-#else /* CHIP_FAMILY_NPCX5 */
- /*
- * SMB0/1 use core clock
- * SMB2/3 use APB2 clock
- */
- freq = (ctrl < 2) ? clock_get_freq() : clock_get_apb2_freq();
-#endif
-
- if (bus_freq_kbps == i2c_stsobjs[ctrl].kbps)
- return;
-
- /*
- * Set SCL frequency by formula:
- * tSCL = 4 * SCLFRQ * tCLK
- * fSCL = fCLK / (4*SCLFRQ)
- * SCLFRQ = ceil(fCLK/(4*fSCL))
- */
- scl_freq = DIV_ROUND_UP(freq, bus_freq_kbps*4000); /* Unit in bps */
-
- /* Normal mode if I2C freq is under 100kHz */
- if (bus_freq_kbps <= 100) {
- i2c_stsobjs[ctrl].kbps = bus_freq_kbps;
- /* Set divider value of SCL */
- SET_FIELD(NPCX_SMBCTL2(ctrl), NPCX_SMBCTL2_SCLFRQ7_FIELD,
- (scl_freq & 0x7F));
- SET_FIELD(NPCX_SMBCTL3(ctrl), NPCX_SMBCTL3_SCLFRQ2_FIELD,
- (scl_freq >> 7));
- return;
- }
-
- /* use Fast Mode */
- SET_BIT(NPCX_SMBCTL3(ctrl), NPCX_SMBCTL3_400K);
- /*
- * Set SCLH(L)T and hold-time directly for best I2C
- * timing condition for all source clocks. Please refer
- * Section 7.5.9 "SMBus Timing - Fast Mode" for detail.
- */
- if (bus_freq_kbps == 400) {
- pTiming = i2c_400k_timings;
- i2c_timing_used = i2c_400k_timing_used;
- } else if (bus_freq_kbps == 1000) {
- pTiming = i2c_1m_timings;
- i2c_timing_used = i2c_1m_timing_used;
- } else {
- i2c_stsobjs[ctrl].kbps = bus_freq_kbps;
- /* Set value from formula */
- NPCX_SMBSCLLT(ctrl) = scl_freq;
- NPCX_SMBSCLHT(ctrl) = scl_freq;
- cprints(CC_I2C,
- "Warning: I2C %d: Use 400kHz or 1MHz for better timing",
- ctrl);
- return;
- }
-
- for (j = 0; j < i2c_timing_used; j++, pTiming++) {
- if (pTiming->clock == (freq/SECOND)) {
- i2c_stsobjs[ctrl].kbps = bus_freq_kbps;
- /* Set SCLH(L)T and hold-time */
- NPCX_SMBSCLLT(ctrl) = pTiming->k1/2;
- NPCX_SMBSCLHT(ctrl) = pTiming->k2/2;
- SET_FIELD(NPCX_SMBCTL4(ctrl),
- NPCX_SMBCTL4_HLDT_FIELD, pTiming->HLDT);
- break;
- }
- }
- if (j == i2c_timing_used)
- cprints(CC_I2C, "Error: I2C %d: src clk %d not supported",
- ctrl, freq / SECOND);
-}
-
-/* Hooks */
-
-static void i2c_freq_changed(void)
-{
- int i;
-
- for (i = 0; i < I2C_CONTROLLER_COUNT; ++i) {
- /* No bus speed configured */
- i2c_stsobjs[i].kbps = 0;
- }
-
- for (i = 0; i < i2c_ports_used; i++) {
- const struct i2c_port_t *p;
- int ctrl;
-
- p = &i2c_ports[i];
- ctrl = i2c_port_to_controller(p->port);
- if (ctrl < 0)
- continue;
- i2c_port_set_freq(ctrl, p->kbps);
- }
-}
-
-DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT);
-
-enum i2c_freq chip_i2c_get_freq(int chip_i2c_port)
-{
- int ctrl;
- int kbps;
-
- ctrl = i2c_port_to_controller(chip_i2c_port);
- if (ctrl < 0)
- return I2C_FREQ_COUNT;
-
- kbps = i2c_stsobjs[ctrl].kbps;
-
- if (kbps > 400)
- return I2C_FREQ_1000KHZ;
- if (kbps > 100)
- return I2C_FREQ_400KHZ;
-
- if (kbps == 100)
- return I2C_FREQ_100KHZ;
-
- return I2C_FREQ_COUNT;
-}
-
-int chip_i2c_set_freq(int chip_i2c_port, enum i2c_freq freq)
-{
- int ctrl;
- int bus_freq_kbps;
-
- ctrl = i2c_port_to_controller(chip_i2c_port);
- if (ctrl < 0)
- return EC_ERROR_INVAL;
-
- switch (freq) {
- case I2C_FREQ_100KHZ:
- bus_freq_kbps = 100;
- break;
- case I2C_FREQ_400KHZ:
- bus_freq_kbps = 400;
- break;
- case I2C_FREQ_1000KHZ:
- bus_freq_kbps = 1000;
- break;
- default:
- return EC_ERROR_INVAL;
- }
-
- i2c_port_set_freq(ctrl, bus_freq_kbps);
- return EC_SUCCESS;
-}
-
-void i2c_init(void)
-{
- int i;
-
- /* Configure pins from GPIOs to I2Cs */
- gpio_config_module(MODULE_I2C, 1);
-
- /* Enable clock for I2C peripheral */
- clock_enable_peripheral(CGC_OFFSET_I2C, CGC_I2C_MASK,
- CGC_MODE_RUN | CGC_MODE_SLEEP);
-#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7
- clock_enable_peripheral(CGC_OFFSET_I2C2, CGC_I2C_MASK2,
- CGC_MODE_RUN | CGC_MODE_SLEEP);
-#endif
-
- /* Set I2C freq */
- i2c_freq_changed();
- /*
- * initialize smb status and register
- */
- for (i = 0; i < i2c_ports_used; i++) {
- volatile struct i2c_status *p_status;
- int port = i2c_ports[i].port;
- int ctrl = i2c_port_to_controller(port);
-
- /* ignore the port if i2c_port_to_controller() failed */
- if (ctrl < 0)
- continue;
-
- p_status = i2c_stsobjs + ctrl;
-
- /* status init */
- p_status->oper_state = SMB_IDLE;
-
- /* Reset task ID */
- p_status->task_waiting = TASK_ID_INVALID;
-
- /* Use default timeout. */
- i2c_set_timeout(port, 0);
-
- /* Init the SMB module */
- i2c_init_bus(ctrl);
- }
-}