diff options
Diffstat (limited to 'chip/mchp/i2c.c')
-rw-r--r-- | chip/mchp/i2c.c | 1096 |
1 files changed, 0 insertions, 1096 deletions
diff --git a/chip/mchp/i2c.c b/chip/mchp/i2c.c deleted file mode 100644 index 9891f4d41e..0000000000 --- a/chip/mchp/i2c.c +++ /dev/null @@ -1,1096 +0,0 @@ -/* Copyright 2017 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 MCHP MEC */ - -#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 "tfdp_chip.h" -#include "timer.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_I2C, outstr) -#define CPRINTF(format, args...) cprintf(CC_I2C, format, ##args) -#define CPRINTS(format, args...) cprints(CC_I2C, format, ##args) - -/* - * MCHP I2C BAUD clock source is 16 MHz. - */ -#define I2C_CLOCK 16000000UL -#define MCHP_I2C_SUPPORTED_BUS_CLOCKS 6 - -/* SMBus Timing values for 1MHz Speed */ -#define SPEED_1MHZ_BUS_CLOCK 0x0509ul -#define SPEED_1MHZ_DATA_TIMING 0x06060601ul -#define SPEED_1MHZ_DATA_TIMING_2 0x06ul -#define SPEED_1MHZ_IDLE_SCALING 0x01000050ul -#define SPEED_1MHZ_TIMEOUT_SCALING 0x149CC2C7ul -/* SMBus Timing values for 400kHz speed */ -#define SPEED_400KHZ_BUS_CLOCK 0x0F17ul -#define SPEED_400KHZ_DATA_TIMING 0x040A0F01ul -#define SPEED_400KHZ_DATA_TIMING_2 0x0Aul -#define SPEED_400KHZ_IDLE_SCALING 0x01000050ul -#define SPEED_400KHZ_TIMEOUT_SCALING 0x149CC2C7ul -/* SMBus Timing values for 100kHz speed */ -#define SPEED_100KHZ_BUS_CLOCK 0x4F4Ful -#define SPEED_100KHZ_DATA_TIMING 0x0C4D4306ul -#define SPEED_100KHZ_DATA_TIMING_2 0x4Dul -#define SPEED_100KHZ_IDLE_SCALING 0x01FC01EDul -#define SPEED_100KHZ_TIMEOUT_SCALING 0x4B9CC2C7ul -/* Bus clock dividers for 333, 80, and 40 kHz */ -#define SPEED_333KHZ_BUS_CLOCK 0x0F1Ful -#define SPEED_80KHZ_BUS_CLOCK 0x6363ul -#define SPEED_40KHZ_BUS_CLOCK 0xC7C7ul - -/* Status */ -#define STS_NBB BIT(0) /* Bus busy */ -#define STS_LAB BIT(1) /* Arbitration lost */ -#define STS_LRB BIT(3) /* Last received bit */ -#define STS_BER BIT(4) /* Bus error */ -#define STS_PIN BIT(7) /* Pending interrupt */ -/* Control */ -#define CTRL_ACK BIT(0) /* Acknowledge */ -#define CTRL_STO BIT(1) /* STOP */ -#define CTRL_STA BIT(2) /* START */ -#define CTRL_ENI BIT(3) /* Enable interrupt */ -#define CTRL_ESO BIT(6) /* Enable serial output */ -#define CTRL_PIN BIT(7) /* Pending interrupt not */ -/* Completion */ -#define COMP_DTEN BIT(2) /* enable device timeouts */ -#define COMP_MCEN BIT(3) /* enable ctrl. cumulative timeouts */ -#define COMP_SCEN BIT(4) /* enable periph. cumulative timeouts */ -#define COMP_BIDEN BIT(5) /* enable Bus idle timeouts */ -#define COMP_IDLE BIT(29) /* i2c bus is idle */ -#define COMP_RW_BITS_MASK 0x3C /* R/W bits mask */ -/* Configuration */ -#define CFG_PORT_MASK (0x0F) /* port selection field */ -#define CFG_TCEN BIT(4) /* Enable HW bus timeouts */ -#define CFG_FEN BIT(8) /* enable input filtering */ -#define CFG_RESET BIT(9) /* reset controller */ -#define CFG_ENABLE BIT(10) /* enable controller */ -#define CFG_GC_DIS BIT(14) /* disable general call address */ -#define CFG_ENIDI BIT(29) /* Enable I2C idle interrupt */ -/* Enable network layer controller done interrupt */ -#define CFG_ENMI BIT(30) -/* Enable network layer peripheral done interrupt */ -#define CFG_ENSI BIT(31) -/* Controller Command */ -#define MCMD_MRUN BIT(0) -#define MCMD_MPROCEED BIT(1) -#define MCMD_START0 BIT(8) -#define MCMD_STARTN BIT(9) -#define MCMD_STOP BIT(10) -#define MCMD_READM BIT(12) -#define MCMD_WCNT_BITPOS (16) -#define MCMD_WCNT_MASK0 (0xFF) -#define MCMD_WCNT_MASK (0xFF << 16) -#define MCMD_RCNT_BITPOS (24) -#define MCMD_RCNT_MASK0 (0xFF) -#define MCMD_RCNT_MASK (0xFF << 24) - -/* Maximum transfer of a SMBUS block transfer */ -#define SMBUS_MAX_BLOCK_SIZE 32 -/* - * Amount of time to blocking wait for i2c bus to finish. After this - * blocking timeout, if the bus is still not finished, then allow other - * tasks to run. - * Note: this is just long enough for a 400kHz bus to finish transmitting - * one byte assuming the bus isn't being held. - */ -#define I2C_WAIT_BLOCKING_TIMEOUT_US 25 - -enum i2c_transaction_state { - /* Stop condition was sent in previous transaction */ - I2C_TRANSACTION_STOPPED, - /* Stop condition was not sent in previous transaction */ - I2C_TRANSACTION_OPEN, -}; - -/* I2C controller state data - * NOTE: I2C_CONTROLLER_COUNT is defined at board level. - */ -static struct { - /* Transaction timeout, or 0 to use default. */ - uint32_t timeout_us; - /* Task waiting on port, or TASK_ID_INVALID if none. */ - /* - * MCHP Remove volatile. - * ISR only reads. - * Non-ISR only writes when interrupt is disabled. - */ - task_id_t task_waiting; - enum i2c_transaction_state transaction_state; - /* transaction context */ - int out_size; - const uint8_t *outp; - int in_size; - uint8_t *inp; - int xflags; - uint32_t i2c_complete; /* ISR write */ - uint32_t flags; - uint8_t port; - uint8_t periph_addr_8bit; - uint8_t ctrl; - uint8_t hwsts; - uint8_t hwsts2; - uint8_t hwsts3; /* ISR write */ - uint8_t hwsts4; - uint8_t lines; -} cdata[I2C_CONTROLLER_COUNT]; - -static const uint16_t i2c_ctrl_nvic_id[] = { - MCHP_IRQ_I2C_0, MCHP_IRQ_I2C_1, MCHP_IRQ_I2C_2, MCHP_IRQ_I2C_3, -#if defined(CHIP_FAMILY_MEC172X) - MCHP_IRQ_I2C_4 -#elif defined(CHIP_FAMILY_MEC152X) - MCHP_IRQ_I2C_4, MCHP_IRQ_I2C_5, MCHP_IRQ_I2C_6, MCHP_IRQ_I2C_7 -#endif -}; -BUILD_ASSERT(ARRAY_SIZE(i2c_ctrl_nvic_id) == MCHP_I2C_CTRL_MAX); - -static const uint16_t i2c_controller_pcr[] = { - MCHP_PCR_I2C0, MCHP_PCR_I2C1, MCHP_PCR_I2C2, MCHP_PCR_I2C3, -#if defined(CHIP_FAMILY_MEC172X) - MCHP_PCR_I2C4 -#elif defined(CHIP_FAMILY_MEC152X) - MCHP_PCR_I2C4, MCHP_PCR_I2C5, MCHP_PCR_I2C6, MCHP_PCR_I2C7, -#endif -}; -BUILD_ASSERT(ARRAY_SIZE(i2c_controller_pcr) == MCHP_I2C_CTRL_MAX); - -static uintptr_t i2c_ctrl_base_addr[] = { - MCHP_I2C0_BASE, MCHP_I2C1_BASE, MCHP_I2C2_BASE, MCHP_I2C3_BASE, -#if defined(CHIP_FAMILY_MEC172X) - MCHP_I2C4_BASE -#elif defined(CHIP_FAMILY_MEC152X) - MCHP_I2C4_BASE, - /* NOTE: 5-7 do not implement network layer hardware */ - MCHP_I2C5_BASE, MCHP_I2C6_BASE, MCHP_I2C7_BASE -#endif -}; -BUILD_ASSERT(ARRAY_SIZE(i2c_ctrl_base_addr) == MCHP_I2C_CTRL_MAX); - -static bool chip_i2c_is_controller_valid(int controller) -{ - if ((controller < 0) || (controller >= MCHP_I2C_CTRL_MAX)) - return false; - return true; -} - -static uintptr_t chip_i2c_ctrl_base(int controller) -{ - if (!chip_i2c_is_controller_valid(controller)) - return 0; - - return i2c_ctrl_base_addr[controller]; -} - -static uint32_t chip_i2c_ctrl_nvic_id(int controller) -{ - if (!chip_i2c_is_controller_valid(controller)) - return 0; - - return (uint32_t)i2c_ctrl_nvic_id[controller]; -} - -static void i2c_ctrl_slp_en(int controller, int sleep_en) -{ - if (!chip_i2c_is_controller_valid(controller)) - return; - if (sleep_en) - MCHP_PCR_SLP_EN_DEV(i2c_controller_pcr[controller]); - else - MCHP_PCR_SLP_DIS_DEV(i2c_controller_pcr[controller]); -} - -uint32_t chip_i2c_get_ctx_flags(int port) -{ - int controller = i2c_port_to_controller(port); - - if (!chip_i2c_is_controller_valid(controller)) - return 0; - return cdata[controller].flags; -} - -/* - * MCHP I2C controller tuned bus clock values. - * MCHP I2C_SMB_Controller_3.6.pdf Table 6-3 - */ -struct i2c_bus_clk { - int freq_khz; - int bus_clk; -}; - -const struct i2c_bus_clk i2c_freq_tbl[] = { - { 40, SPEED_40KHZ_BUS_CLOCK }, { 80, SPEED_80KHZ_BUS_CLOCK }, - { 100, SPEED_100KHZ_BUS_CLOCK }, { 333, SPEED_333KHZ_BUS_CLOCK }, - { 400, SPEED_400KHZ_BUS_CLOCK }, { 1000, SPEED_1MHZ_BUS_CLOCK }, -}; -BUILD_ASSERT(ARRAY_SIZE(i2c_freq_tbl) == MCHP_I2C_SUPPORTED_BUS_CLOCKS); - -/* I2C controller assignment to a port */ -static int i2c_p2c[MCHP_I2C_PORT_COUNT]; - -static int get_closest(int lesser, int greater, int target) -{ - if (target - i2c_freq_tbl[lesser].freq_khz >= - i2c_freq_tbl[greater].freq_khz - target) - return greater; - else - return lesser; -} - -/* - * Return index in i2c_freq_tbl of supported frequencies - * closest to requested frequency. - */ -static const struct i2c_bus_clk *get_supported_speed_idx(int req_kbps) -{ - int i, limit, m, imax; - - if (req_kbps <= i2c_freq_tbl[0].freq_khz) - return &i2c_freq_tbl[0]; - - imax = ARRAY_SIZE(i2c_freq_tbl); - if (req_kbps >= i2c_freq_tbl[imax - 1].freq_khz) - return &i2c_freq_tbl[imax - 1]; - - /* we only get here if ARRAY_SIZE(...) > 1 - * and req_kbps is in range. - */ - i = 0; - limit = imax; - while (i < limit) { - m = (i + limit) / 2; - if (i2c_freq_tbl[m].freq_khz == req_kbps) - break; - - if (req_kbps < i2c_freq_tbl[m].freq_khz) { - if (m > 0 && req_kbps > i2c_freq_tbl[m - 1].freq_khz) { - m = get_closest(m - 1, m, req_kbps); - break; - } - limit = m; - } else { - if (m < imax - 1 && - req_kbps < i2c_freq_tbl[m + 1].freq_khz) { - m = get_closest(m, m + 1, req_kbps); - break; - } - i = m + 1; - } - } - - return &i2c_freq_tbl[m]; -} - -/* - * Refer to NXP UM10204 for minimum timing requirement of T_Low and T_High. - * http://www.nxp.com/documents/user_manual/UM10204.pdf - * I2C spec. timing value are used in recommended registers values - * in MCHP I2C_SMB_Controller_3.6.pdf - * Restrict frequencies to those in the above MCHP spec. - * 40, 80, 100, 333, 400, and 1000 kHz. - */ -static void configure_controller_speed(int controller, int kbps) -{ - const struct i2c_bus_clk *p; - uintptr_t raddr; - - raddr = chip_i2c_ctrl_base(controller); - - p = get_supported_speed_idx(kbps); - MCHP_I2C_BUS_CLK(raddr) = p->bus_clk; - - if (p->freq_khz > 400) { /* Fast mode plus */ - MCHP_I2C_DATA_TIM(raddr) = SPEED_1MHZ_DATA_TIMING; - MCHP_I2C_DATA_TIM_2(raddr) = SPEED_1MHZ_DATA_TIMING_2; - MCHP_I2C_IDLE_SCALE(raddr) = SPEED_1MHZ_IDLE_SCALING; - MCHP_I2C_TOUT_SCALE(raddr) = SPEED_1MHZ_TIMEOUT_SCALING; - } else if (p->freq_khz > 100) { /* Fast mode */ - MCHP_I2C_DATA_TIM(raddr) = SPEED_400KHZ_DATA_TIMING; - MCHP_I2C_DATA_TIM_2(raddr) = SPEED_400KHZ_DATA_TIMING_2; - MCHP_I2C_IDLE_SCALE(raddr) = SPEED_400KHZ_IDLE_SCALING; - MCHP_I2C_TOUT_SCALE(raddr) = SPEED_400KHZ_TIMEOUT_SCALING; - } else { /* Standard mode */ - MCHP_I2C_DATA_TIM(raddr) = SPEED_100KHZ_DATA_TIMING; - MCHP_I2C_DATA_TIM_2(raddr) = SPEED_100KHZ_DATA_TIMING_2; - MCHP_I2C_IDLE_SCALE(raddr) = SPEED_100KHZ_IDLE_SCALING; - MCHP_I2C_TOUT_SCALE(raddr) = SPEED_100KHZ_TIMEOUT_SCALING; - } -} - -/* - * NOTE: direct mode interrupts do not need GIRQn bit - * set in aggregator block enable register. - */ -static void enable_controller_irq(int controller) -{ - uint32_t nvic_id = chip_i2c_ctrl_nvic_id(controller); - - MCHP_INT_ENABLE(MCHP_I2C_GIRQ) = MCHP_I2C_GIRQ_BIT(controller); - task_enable_irq(nvic_id); -} - -static void disable_controller_irq(int controller) -{ - uint32_t nvic_id = chip_i2c_ctrl_nvic_id(controller); - - MCHP_INT_DISABLE(MCHP_I2C_GIRQ) = MCHP_I2C_GIRQ_BIT(controller); - /* read back into read-only reg. to insure disable takes effect */ - MCHP_INT_BLK_IRQ = MCHP_INT_DISABLE(MCHP_I2C_GIRQ); - task_disable_irq(nvic_id); - task_clear_pending_irq(nvic_id); -} - -/* - * Do NOT enable controller's IDLE interrupt in the configuration - * register. IDLE is meant for multi-controller and controller acting - * as a peripheral. - */ -static void configure_controller(int controller, int port, int kbps) -{ - uintptr_t raddr = chip_i2c_ctrl_base(controller); - - if (raddr == 0) - return; - - disable_controller_irq(controller); - MCHP_INT_SOURCE(MCHP_I2C_GIRQ) = MCHP_I2C_GIRQ_BIT(controller); - - /* set to default except for port select field b[3:0] */ - MCHP_I2C_CONFIG(raddr) = (uint32_t)(port & 0xf); - MCHP_I2C_CTRL(raddr) = CTRL_PIN; - - /* Set both controller peripheral addresses to 0 the - * general call address. We disable general call - * below. - */ - MCHP_I2C_OWN_ADDR(raddr) = 0; - - configure_controller_speed(controller, kbps); - - /* Controller timings done, clear RO status, enable - * output, and ACK generation. - */ - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_ACK; - - /* filter enable, disable General Call */ - MCHP_I2C_CONFIG(raddr) |= CFG_FEN + CFG_GC_DIS; - /* enable controller */ - MCHP_I2C_CONFIG(raddr) |= CFG_ENABLE; -} - -static void reset_controller(int controller) -{ - int i; - uintptr_t raddr; - - raddr = chip_i2c_ctrl_base(controller); - if (raddr == 0) - return; - - /* Reset asserted for at least one AHB clock */ - MCHP_I2C_CONFIG(raddr) |= BIT(9); - MCHP_EC_ID_RO = 0; - MCHP_I2C_CONFIG(raddr) &= ~BIT(9); - - for (i = 0; i < i2c_ports_used; ++i) - if (controller == i2c_port_to_controller(i2c_ports[i].port)) { - configure_controller(controller, i2c_ports[i].port, - i2c_ports[i].kbps); - cdata[controller].transaction_state = - I2C_TRANSACTION_STOPPED; - break; - } -} - -/* - * !!! WARNING !!! - * We have observed task_wait_event_mask() returning 0 if the I2C - * controller IDLE interrupt is enabled. We believe it is due to the ISR - * post multiple events too quickly but don't have absolute proof. - */ -static int wait_for_interrupt(int controller, int timeout) -{ - int event; - - if (timeout <= 0) - return EC_ERROR_TIMEOUT; - - cdata[controller].task_waiting = task_get_current(); - enable_controller_irq(controller); - - /* Wait until I2C interrupt or timeout. */ - event = task_wait_event_mask(TASK_EVENT_I2C_IDLE, timeout); - - disable_controller_irq(controller); - cdata[controller].task_waiting = TASK_ID_INVALID; - - return (event & TASK_EVENT_TIMER) ? EC_ERROR_TIMEOUT : EC_SUCCESS; -} - -static int wait_idle(int controller) -{ - uintptr_t raddr = chip_i2c_ctrl_base(controller); - uint64_t block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US; - uint64_t task_timeout = block_timeout + cdata[controller].timeout_us; - int rv = 0; - uint8_t sts = MCHP_I2C_STATUS(raddr); - - while (!(sts & STS_NBB)) { - if (rv) - return rv; - if (get_time().val > block_timeout) - rv = wait_for_interrupt(controller, - task_timeout - get_time().val); - sts = MCHP_I2C_STATUS(raddr); - } - - if (sts & (STS_BER | STS_LAB)) - return EC_ERROR_UNKNOWN; - return EC_SUCCESS; -} - -/* - * Return EC_SUCCESS on ACK of byte else EC_ERROR_UNKNOWN. - * Record I2C.Status in cdata[controller] structure. - * Byte transmit finished with no I2C bus error or lost arbitration. - * PIN -> 0. LRB bit contains peripheral ACK/NACK bit. - * Peripheral ACK: I2C.Status == 0x00 - * Peripheral NACK: I2C.Status == 0x08 - * Byte transmit finished with I2C bus errors or lost arbitration. - * PIN -> 0 and BER and/or LAB set. - * - * Byte receive finished with no I2C bus errors or lost arbitration. - * PIN -> 0. LRB=0/1 based on ACK bit in I2C.Control. - * Controller receiver must NACK last byte it wants to receive. - * How do we handle this if we don't know direction of transfer? - * I2C.Control is write-only so we can't see Controller's ACK control - * bit. - */ -static int wait_byte_done(int controller, uint8_t mask, uint8_t expected) -{ - uint64_t block_timeout; - uint64_t task_timeout; - uintptr_t raddr; - int rv; - uint8_t sts; - - rv = 0; - raddr = chip_i2c_ctrl_base(controller); - block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US; - task_timeout = block_timeout + cdata[controller].timeout_us; - sts = MCHP_I2C_STATUS(raddr); - cdata[controller].hwsts = sts; - while (sts & STS_PIN) { - if (rv) - return rv; - if (get_time().val > block_timeout) { - rv = wait_for_interrupt(controller, - task_timeout - get_time().val); - } - sts = MCHP_I2C_STATUS(raddr); - cdata[controller].hwsts = sts; - } - - rv = EC_SUCCESS; - if ((sts & mask) != expected) - rv = EC_ERROR_UNKNOWN; - return rv; -} - -/* - * Select port on controller. If controller configured - * for port do nothing. - * Switch port by reset and reconfigure to handle cases where - * the peripheral on current port is driving line(s) low. - * NOTE: I2C hardware reset only requires one AHB clock, back to back - * writes is OK but we added an extra write as insurance. - */ -static void select_port(int port, int controller) -{ - uint32_t port_sel; - uintptr_t raddr; - - raddr = chip_i2c_ctrl_base(controller); - port_sel = (uint32_t)(port & 0x0f); - if ((MCHP_I2C_CONFIG(raddr) & 0x0f) == port_sel) - return; - - MCHP_I2C_CONFIG(raddr) |= BIT(9); - MCHP_EC_ID_RO = 0; /* extra write to read-only as delay */ - MCHP_I2C_CONFIG(raddr) &= ~BIT(9); - configure_controller(controller, port_sel, i2c_ports[port].kbps); -} - -/* - * Use safe method (reading GPIO.Control PAD input bit) - * to obtain SCL line state in bit[0] and SDA line state in bit[1]. - * NOTE: I2C controller bit-bang register is not safe. Using - * bit-bang requires timeouts be disabled and the controller in an - * idle state. Switching controller to bit-bang mode when the controller - * is not idle will cause problems. - */ -static uint32_t get_line_level(int port) -{ - uint32_t lines; - - lines = i2c_raw_get_scl(port) & 0x01; - lines |= (i2c_raw_get_sda(port) & 0x01) << 1; - return lines; -} - -/* - * Check if I2C port connected to controller has bus error or - * other issues such as stuck clock/data lines. - */ -static int i2c_check_recover(int port, int controller) -{ - uintptr_t raddr; - uint32_t lines; - uint8_t reg; - - raddr = chip_i2c_ctrl_base(controller); - lines = get_line_level(port); - reg = MCHP_I2C_STATUS(raddr); - - if ((((reg & (STS_BER | STS_LAB)) || !(reg & STS_NBB)) || - (lines != I2C_LINE_IDLE))) { - cdata[controller].flags |= (1ul << 16); - CPRINTS("I2C%d port%d recov status 0x%02x, SDA:SCL=0x%0x", - controller, port, reg, lines); - /* Attempt to unwedge the port. */ - if (lines != I2C_LINE_IDLE) - if (i2c_unwedge(port)) - return EC_ERROR_UNKNOWN; - - /* Bus error, bus busy, or arbitration lost. Try reset. */ - reset_controller(controller); - select_port(port, controller); - /* - * We don't know what edges the peripheral saw, so sleep long - * enough that the peripheral will see the new start condition - * below. - */ - usleep(1000); - reg = MCHP_I2C_STATUS(raddr); - lines = get_line_level(port); - if ((reg & (STS_BER | STS_LAB)) || !(reg & STS_NBB) || - (lines != I2C_LINE_IDLE)) - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -static inline void push_in_buf(uint8_t **in, uint8_t val, int skip) -{ - if (!skip) { - **in = val; - (*in)++; - } -} - -/* - * I2C Controller transmit - * Caller has filled in cdata[ctrl] parameters - */ -static int i2c_mtx(int ctrl) -{ - uintptr_t raddr; - int i, rv; - - raddr = chip_i2c_ctrl_base(ctrl); - rv = EC_SUCCESS; - cdata[ctrl].flags |= (1ul << 1); - if (cdata[ctrl].xflags & I2C_XFER_START) { - cdata[ctrl].flags |= (1ul << 2); - MCHP_I2C_DATA(raddr) = cdata[ctrl].periph_addr_8bit; - /* Clock out the peripheral address, sending START bit */ - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_ENI | - CTRL_ACK | CTRL_STA; - cdata[ctrl].transaction_state = I2C_TRANSACTION_OPEN; - } - - for (i = 0; i < cdata[ctrl].out_size; ++i) { - rv = wait_byte_done(ctrl, 0xff, 0x00); - if (rv) { - cdata[ctrl].flags |= (1ul << 17); - MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO | CTRL_ENI | - CTRL_STO | CTRL_ACK; - return rv; - } - cdata[ctrl].flags |= (1ul << 15); - MCHP_I2C_DATA(raddr) = cdata[ctrl].outp[i]; - } - - rv = wait_byte_done(ctrl, 0xff, 0x00); - if (rv) { - cdata[ctrl].flags |= (1ul << 18); - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_ENI | - CTRL_STO | CTRL_ACK; - return rv; - } - - /* - * Send STOP bit if the stop flag is on, and caller - * doesn't expect to receive data. - */ - if ((cdata[ctrl].xflags & I2C_XFER_STOP) && - (cdata[ctrl].in_size == 0)) { - cdata[ctrl].flags |= (1ul << 3); - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_STO | - CTRL_ACK; - cdata[ctrl].transaction_state = I2C_TRANSACTION_STOPPED; - } - return rv; -} - -/* - * I2C Controller-Receive helper routine for sending START or - * Repeated-START. - * This routine should only be called if a (Repeated-)START - * is required. - * If I2C controller is Idle or Stopped - * Send START by: - * Write read address to I2C.Data - * Write PIN=ESO=STA=ACK=1, STO=0 to I2C.Ctrl. This - * will trigger controller to output 8-bits of data. - * Else if I2C controller is Open (previous START sent) - * Send Repeated-START by: - * Write ESO=STA=ACK=1, PIN=STO=0 to I2C.Ctrl. Controller - * will generate START but not transmit data. - * Write read address to I2C.Data. Controller will transmit - * 8-bits of data - * NOTE: Controller clocks in address on SDA as its transmitting. - * Therefore 1-byte RX-FIFO will contain address plus R/nW bit. - * Controller will wait for peripheral to release SCL before transmitting - * 9th clock and latching (N)ACK on SDA. - * Spin on I2C.Status PIN -> 0. Enable I2C interrupt if spin time - * exceeds threshold. If a timeout occurs generate STOP and return - * an error. - * - * Because I2C generates clocks for next byte when reading I2C.Data - * register we must prepare control logic. - * If the caller requests STOP and read length is 1 then set - * clear ACK bit in I2C.Ctrl. Set ESO=ENI=1, PIN=STA=STO=ACK=0 - * in I2C.Ctrl. Controller must NACK last byte. - */ -static int i2c_mrx_start(int ctrl) -{ - uintptr_t raddr; - int rv; - uint8_t u8; - - raddr = chip_i2c_ctrl_base(ctrl); - - cdata[ctrl].flags |= (1ul << 4); - u8 = CTRL_ESO | CTRL_ENI | CTRL_STA | CTRL_ACK; - if (cdata[ctrl].transaction_state == I2C_TRANSACTION_OPEN) { - cdata[ctrl].flags |= (1ul << 5); - /* Repeated-START then address */ - MCHP_I2C_CTRL(raddr) = u8; - } - MCHP_I2C_DATA(raddr) = cdata[ctrl].periph_addr_8bit | 0x01; - if (cdata[ctrl].transaction_state == I2C_TRANSACTION_STOPPED) { - cdata[ctrl].flags |= (1ul << 6); - /* address then START */ - MCHP_I2C_CTRL(raddr) = u8 | CTRL_PIN; - } - cdata[ctrl].transaction_state = I2C_TRANSACTION_OPEN; - /* Controller generates START, transmits data(address) capturing - * 9-bits from SDA (8-bit address + (N)Ack bit). - * We leave captured address in I2C.Data register. - * Controller receive data read routine assumes data is pending - * in I2C.Data - */ - cdata[ctrl].flags |= (1ul << 7); - rv = wait_byte_done(ctrl, 0xff, 0x00); - if (rv) { - cdata[ctrl].flags |= (1ul << 19); - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_STO | - CTRL_ACK; - return rv; - } - /* if STOP requested and last 1 or 2 bytes prepare controller - * to NACK last byte. Do this before read of extra data so - * controller is setup to NACK last byte. - */ - cdata[ctrl].flags |= (1ul << 8); - if (cdata[ctrl].xflags & I2C_XFER_STOP && (cdata[ctrl].in_size < 2)) { - cdata[ctrl].flags |= (1ul << 9); - MCHP_I2C_CTRL(raddr) = CTRL_ESO | CTRL_ENI; - } - /* - * Read & discard peripheral address. - * Generates clocks for next data - */ - cdata[ctrl].flags |= (1ul << 10); - u8 = MCHP_I2C_DATA(raddr); - return rv; -} -/* - * I2C Controller-Receive data read helper. - * Assumes I2C is in use, (Rpt-)START was previously sent. - * Reading I2C.Data generates clocks for the next byte. If caller - * requests STOP then we must clear I2C.Ctrl ACK before reading - * second to last byte from RX-FIFO data register. Before reading - * the last byte we must set I2C.Ctrl to generate a stop after - * the read from RX-FIFO register. - * NOTE: I2C.Status.LRB only records the (N)ACK bit in controller - * transmit mode, not in controller receive mode. - * NOTE2: Do not set ENI bit in I2C.Ctrl for STOP generation. - */ -static int i2c_mrx_data(int ctrl) -{ - uint32_t nrx = (uint32_t)cdata[ctrl].in_size; - uint32_t stop = (uint32_t)cdata[ctrl].xflags & I2C_XFER_STOP; - uint8_t *pdest = cdata[ctrl].inp; - int rv; - uintptr_t raddr; - - raddr = chip_i2c_ctrl_base(ctrl); - - cdata[ctrl].flags |= (1ul << 11); - while (nrx) { - rv = wait_byte_done(ctrl, 0xff, 0x00); - if (rv) { - cdata[ctrl].flags |= (1ul << 20); - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_STO | - CTRL_ACK; - return rv; - } - if (stop) { - if (nrx == 2) { - cdata[ctrl].flags |= (1ul << 12); - MCHP_I2C_CTRL(raddr) = CTRL_ESO | CTRL_ENI; - } else if (nrx == 1) { - cdata[ctrl].flags |= (1ul << 13); - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | - CTRL_STO | CTRL_ACK; - } - } - *pdest++ = MCHP_I2C_DATA(raddr); - nrx--; - } - cdata[ctrl].flags |= (1ul << 14); - return EC_SUCCESS; -} - -/* - * Called from common I2C - */ -int chip_i2c_xfer(int port, uint16_t periph_addr_flags, const uint8_t *out, - int out_size, uint8_t *in, int in_size, int flags) -{ - int ctrl; - int ret_done; - uintptr_t raddr; - - if (out_size == 0 && in_size == 0) - return EC_SUCCESS; - - ctrl = i2c_port_to_controller(port); - if (ctrl < 0) - return EC_ERROR_INVAL; - - raddr = chip_i2c_ctrl_base(ctrl); - if (raddr == 0) - return EC_ERROR_INVAL; - - cdata[ctrl].flags = (1ul << 0); - disable_controller_irq(ctrl); - select_port(port, ctrl); - - /* store transfer context */ - cdata[ctrl].i2c_complete = 0; - cdata[ctrl].hwsts = 0; - cdata[ctrl].hwsts2 = 0; - cdata[ctrl].hwsts3 = 0; - cdata[ctrl].hwsts4 = 0; - cdata[ctrl].port = port & 0xff; - cdata[ctrl].periph_addr_8bit = I2C_STRIP_FLAGS(periph_addr_flags) << 1; - cdata[ctrl].out_size = out_size; - cdata[ctrl].outp = out; - cdata[ctrl].in_size = in_size; - cdata[ctrl].inp = in; - cdata[ctrl].xflags = flags; - - if ((flags & I2C_XFER_START) && - cdata[ctrl].transaction_state == I2C_TRANSACTION_STOPPED) { - wait_idle(ctrl); - ret_done = i2c_check_recover(port, ctrl); - if (ret_done) - goto err_chip_i2c_xfer; - } - - ret_done = EC_SUCCESS; - if (out_size) { - ret_done = i2c_mtx(ctrl); - if (ret_done) - goto err_chip_i2c_xfer; - } - - if (in_size) { - if (cdata[ctrl].xflags & I2C_XFER_START) { - ret_done = i2c_mrx_start(ctrl); - if (ret_done) - goto err_chip_i2c_xfer; - } - ret_done = i2c_mrx_data(ctrl); - if (ret_done) - goto err_chip_i2c_xfer; - } - - cdata[ctrl].flags |= (1ul << 15); - /* MCHP wait for STOP to complete */ - if (cdata[ctrl].xflags & I2C_XFER_STOP) - wait_idle(ctrl); - - /* Check for error conditions */ - if (MCHP_I2C_STATUS(raddr) & (STS_LAB | STS_BER)) { - cdata[ctrl].flags |= (1ul << 21); - goto err_chip_i2c_xfer; - } - cdata[ctrl].flags |= (1ul << 14); - return EC_SUCCESS; - -err_chip_i2c_xfer: - cdata[ctrl].flags |= (1ul << 22); - cdata[ctrl].hwsts2 = MCHP_I2C_STATUS(raddr); /* record status */ - /* NOTE: writing I2C.Ctrl.PIN=1 will clear all bits - * except NBB in I2C.Status - */ - MCHP_I2C_CTRL(raddr) = CTRL_PIN | CTRL_ESO | CTRL_STO | CTRL_ACK; - cdata[ctrl].transaction_state = I2C_TRANSACTION_STOPPED; - /* record status after STOP */ - cdata[ctrl].hwsts4 = MCHP_I2C_STATUS(raddr); - - /* record line levels. - * Note line levels may reflect STOP condition - */ - cdata[ctrl].lines = (uint8_t)get_line_level(cdata[ctrl].port); - if (cdata[ctrl].hwsts2 & STS_BER) { - cdata[ctrl].flags |= (1ul << 23); - reset_controller(ctrl); - } - return EC_ERROR_UNKNOWN; -} -/* - * A safe method of reading port's SCL pin level. - */ -int i2c_raw_get_scl(int port) -{ - enum gpio_signal g; - - /* If no SCL pin defined for this port, - * then return 1 to appear idle. - */ - if (get_scl_from_i2c_port(port, &g) != EC_SUCCESS) - return 1; - return gpio_get_level(g); -} - -/* - * A safe method of reading port's SDA pin level. - */ -int i2c_raw_get_sda(int port) -{ - enum gpio_signal g; - - /* If no SDA pin defined for this port, - * then return 1 to appear idle. - */ - if (get_sda_from_i2c_port(port, &g) != EC_SUCCESS) - return 1; - return gpio_get_level(g); -} - -/* - * Caller is responsible for locking the port. - */ -int i2c_get_line_levels(int port) -{ - int rv, controller; - - controller = i2c_port_to_controller(port); - if (controller < 0) - return 0x03; /* No controller, return high line levels */ - - select_port(port, controller); - rv = get_line_level(port); - return rv; -} - -/* - * this function returns the controller for I2C - * return mod of MCHP_I2C_CTRL_MAX - */ -__overridable int board_i2c_p2c(int port) -{ - if (port < 0 || port >= I2C_PORT_COUNT) - return -1; - return i2c_p2c[port]; -} - -/* - * I2C port must be a zero based number. - * MCHP I2C can map any port to any of the 4 controllers. - * Call board level function as board designs may choose - * to wire up and group ports differently. - */ -int i2c_port_to_controller(int port) -{ - return board_i2c_p2c(port); -} - -void i2c_set_timeout(int port, uint32_t timeout) -{ - /* Parameter is port, but timeout is stored by-controller. */ - cdata[i2c_port_to_controller(port)].timeout_us = - timeout ? timeout : I2C_TIMEOUT_DEFAULT_US; -} - -/* - * Initialize I2C controllers specified by the board configuration. - * If multiple ports are mapped to the same controller choose the - * lowest speed. - */ -void i2c_init(void) -{ - int i, controller, kbps; - int controller_kbps[MCHP_I2C_CTRL_MAX]; - const struct i2c_bus_clk *pbc; - - for (i = 0; i < MCHP_I2C_CTRL_MAX; i++) - controller_kbps[i] = 0; - - /* Configure GPIOs */ - gpio_config_module(MODULE_I2C, 1); - - memset(cdata, 0, sizeof(cdata)); - - for (i = 0; i < i2c_ports_used; ++i) { - /* Assign I2C controller to I2C port */ - i2c_p2c[i2c_ports[i].port] = i % MCHP_I2C_CTRL_MAX; - - controller = i2c_port_to_controller(i2c_ports[i].port); - kbps = i2c_ports[i].kbps; - - /* Clear PCR sleep enable for controller */ - i2c_ctrl_slp_en(controller, 0); - - if (controller_kbps[controller] && - (controller_kbps[controller] != kbps)) { - CPRINTF("I2C[%d] init speed conflict: %d != %d\n", - controller, kbps, controller_kbps[controller]); - kbps = MIN(kbps, controller_kbps[controller]); - } - - /* controller speed hardware limits */ - pbc = get_supported_speed_idx(kbps); - if (pbc->freq_khz != kbps) - CPRINTF("I2C[%d] init requested speed %d" - " using closest supported speed %d\n", - controller, kbps, pbc->freq_khz); - - controller_kbps[controller] = pbc->freq_khz; - configure_controller(controller, i2c_ports[i].port, - controller_kbps[controller]); - cdata[controller].task_waiting = TASK_ID_INVALID; - cdata[controller].transaction_state = I2C_TRANSACTION_STOPPED; - /* Use default timeout. */ - i2c_set_timeout(i2c_ports[i].port, 0); - } -} - -/* - * Handle I2C interrupts. - * I2C controller is configured to fire interrupts on - * anything causing PIN 1->0 and I2C IDLE (NBB -> 1). - * NVIC interrupt disable must clear NVIC pending bit. - */ -static void handle_interrupt(int controller) -{ - uint32_t r; - int id = cdata[controller].task_waiting; - uintptr_t raddr = chip_i2c_ctrl_base(controller); - - /* - * Write to control register interferes with I2C transaction. - * Instead, let's disable IRQ from the core until the next time - * we want to wait for STS_PIN/STS_NBB. - */ - disable_controller_irq(controller); - cdata[controller].hwsts3 = MCHP_I2C_STATUS(raddr); - /* Clear all interrupt status */ - r = MCHP_I2C_COMPLETE(raddr); - MCHP_I2C_COMPLETE(raddr) = r; - cdata[controller].i2c_complete = r; - MCHP_INT_SOURCE(MCHP_I2C_GIRQ) = MCHP_I2C_GIRQ_BIT(controller); - - /* Wake up the task which was waiting on the I2C interrupt, if any. */ - if (id != TASK_ID_INVALID) - task_set_event(id, TASK_EVENT_I2C_IDLE); -} - -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 defined(CHIP_FAMILY_MEC172X) -void i2c4_interrupt(void) -{ - handle_interrupt(4); -} -#elif defined(CHIP_FAMILY_MEC152X) -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(MCHP_IRQ_I2C_0, i2c0_interrupt, 2); -DECLARE_IRQ(MCHP_IRQ_I2C_1, i2c1_interrupt, 2); -DECLARE_IRQ(MCHP_IRQ_I2C_2, i2c2_interrupt, 2); -DECLARE_IRQ(MCHP_IRQ_I2C_3, i2c3_interrupt, 2); -#if defined(CHIP_FAMILY_MEC172X) -DECLARE_IRQ(MCHP_IRQ_I2C_4, i2c4_interrupt, 2); -#elif defined(CHIP_FAMILY_MEC152X) -DECLARE_IRQ(MCHP_IRQ_I2C_4, i2c4_interrupt, 2); -DECLARE_IRQ(MCHP_IRQ_I2C_5, i2c5_interrupt, 2); -DECLARE_IRQ(MCHP_IRQ_I2C_6, i2c6_interrupt, 2); -DECLARE_IRQ(MCHP_IRQ_I2C_7, i2c7_interrupt, 2); -#endif |