diff options
Diffstat (limited to 'chip/mec1322/i2c.c')
-rw-r--r-- | chip/mec1322/i2c.c | 531 |
1 files changed, 0 insertions, 531 deletions
diff --git a/chip/mec1322/i2c.c b/chip/mec1322/i2c.c deleted file mode 100644 index fe72b870ef..0000000000 --- a/chip/mec1322/i2c.c +++ /dev/null @@ -1,531 +0,0 @@ -/* Copyright 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* I2C port module for MEC1322 */ - -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "i2c.h" -#include "registers.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_I2C, outstr) -#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) - -#define I2C_CLOCK 16000000 /* 16 MHz */ - -/* 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_IDLE BIT(29) /* i2c bus is idle */ -#define COMP_RW_BITS_MASK 0x3C /* R/W bits mask */ - -/* 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 */ -static struct { - /* Transaction timeout, or 0 to use default. */ - uint32_t timeout_us; - /* Task waiting on port, or TASK_ID_INVALID if none. */ - volatile task_id_t task_waiting; - enum i2c_transaction_state transaction_state; -} cdata[I2C_CONTROLLER_COUNT]; - -/* Map port number to port name in datasheet, for debug prints. */ -static const char *i2c_port_names[MEC1322_I2C_PORT_COUNT] = { - [MEC1322_I2C0_0] = "0_0", - [MEC1322_I2C0_1] = "0_1", - [MEC1322_I2C1] = "1", - [MEC1322_I2C2] = "2", - [MEC1322_I2C3] = "3", -}; - -static void configure_controller_speed(int controller, int kbps) -{ - int t_low, t_high; - const int period = I2C_CLOCK / 1000 / kbps; - - /* - * Refer to NXP UM10204 for minimum timing requirement of T_Low and - * T_High. - * http://www.nxp.com/documents/user_manual/UM10204.pdf - */ - if (kbps > 400) { - /* Fast mode plus */ - t_low = t_high = period / 2 - 1; - MEC1322_I2C_DATA_TIM(controller) = 0x06060601; - MEC1322_I2C_DATA_TIM_2(controller) = 0x06; - } else if (kbps > 100) { - /* Fast mode */ - /* By spec, clk low period is 1.3us min */ - t_low = MAX((int)(I2C_CLOCK * 1.3 / 1000000), period / 2 - 1); - t_high = period - t_low - 2; - MEC1322_I2C_DATA_TIM(controller) = 0x040a0a01; - MEC1322_I2C_DATA_TIM_2(controller) = 0x0a; - } else { - /* Standard mode */ - t_low = t_high = period / 2 - 1; - MEC1322_I2C_DATA_TIM(controller) = 0x0c4d5006; - MEC1322_I2C_DATA_TIM_2(controller) = 0x4d; - } - - /* Clock periods is one greater than the contents of these fields */ - MEC1322_I2C_BUS_CLK(controller) = ((t_high & 0xff) << 8) | - (t_low & 0xff); -} - -static void configure_controller(int controller, int kbps) -{ - MEC1322_I2C_CTRL(controller) = CTRL_PIN; - MEC1322_I2C_OWN_ADDR(controller) = 0x0; - configure_controller_speed(controller, kbps); - MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO | - CTRL_ACK | CTRL_ENI; - MEC1322_I2C_CONFIG(controller) |= BIT(10); /* ENAB */ - - /* Enable interrupt */ - MEC1322_I2C_CONFIG(controller) |= BIT(29); /* ENIDI */ - MEC1322_INT_ENABLE(12) |= BIT(controller); - MEC1322_INT_BLK_EN |= BIT(12); -} - -static void reset_controller(int controller) -{ - int i; - - MEC1322_I2C_CONFIG(controller) |= BIT(9); - udelay(100); - MEC1322_I2C_CONFIG(controller) &= ~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].kbps); - cdata[controller].transaction_state = - I2C_TRANSACTION_STOPPED; - break; - } -} - -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(); - task_enable_irq(MEC1322_IRQ_I2C_0 + controller); - - /* Wait until I2C interrupt or timeout. */ - event = task_wait_event_mask(TASK_EVENT_I2C_IDLE, timeout); - - task_disable_irq(MEC1322_IRQ_I2C_0 + controller); - cdata[controller].task_waiting = TASK_ID_INVALID; - - return (event & TASK_EVENT_TIMER) ? EC_ERROR_TIMEOUT : EC_SUCCESS; -} - -static int wait_idle(int controller) -{ - uint8_t sts = MEC1322_I2C_STATUS(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; - - 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 = MEC1322_I2C_STATUS(controller); - } - - if (sts & (STS_BER | STS_LAB)) - return EC_ERROR_UNKNOWN; - return EC_SUCCESS; -} - -static int wait_byte_done(int controller) -{ - uint8_t sts = MEC1322_I2C_STATUS(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; - - 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 = MEC1322_I2C_STATUS(controller); - } - - return sts & STS_LRB; -} - -static void select_port(int port) -{ - /* - * I2C0_1 uses port 1 of controller 0. All other I2C pin sets - * use port 0. - */ - uint8_t port_sel = (port == MEC1322_I2C0_1) ? 1 : 0; - int controller = i2c_port_to_controller(port); - - MEC1322_I2C_CONFIG(controller) &= ~0xf; - MEC1322_I2C_CONFIG(controller) |= port_sel; - -} - -static inline int get_line_level(int controller) -{ - int ret, ctrl; - /* - * We need to enable BB (Bit Bang) mode in order to read line level - * properly, othervise line levels return always idle (0x60). - */ - ctrl = MEC1322_I2C_BB_CTRL(controller); - MEC1322_I2C_BB_CTRL(controller) |= 1; - ret = (MEC1322_I2C_BB_CTRL(controller) >> 5) & 0x3; - MEC1322_I2C_BB_CTRL(controller) = ctrl; - return ret; -} - -static inline void push_in_buf(uint8_t **in, uint8_t val, int skip) -{ - if (!skip) { - **in = val; - (*in)++; - } -} - -int chip_i2c_xfer(const int port, - const uint16_t slave_addr_flags, - const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags) -{ - int i; - int controller; - int send_start = flags & I2C_XFER_START; - int send_stop = flags & I2C_XFER_STOP; - int skip = 0; - int bytes_to_read; - uint8_t reg; - int ret_done; - - if (out_size == 0 && in_size == 0) - return EC_SUCCESS; - - select_port(port); - controller = i2c_port_to_controller(port); - if (send_start && - cdata[controller].transaction_state == I2C_TRANSACTION_STOPPED) - wait_idle(controller); - - reg = MEC1322_I2C_STATUS(controller); - if (send_start && - cdata[controller].transaction_state == I2C_TRANSACTION_STOPPED && - (((reg & (STS_BER | STS_LAB)) || !(reg & STS_NBB)) || - (get_line_level(controller) - != I2C_LINE_IDLE))) { - CPRINTS("i2c%s bad status 0x%02x, SCL=%d, SDA=%d", - i2c_port_names[port], reg, - get_line_level(controller) & I2C_LINE_SCL_HIGH, - get_line_level(controller) & I2C_LINE_SDA_HIGH); - - /* Attempt to unwedge the port. */ - i2c_unwedge(port); - - /* Bus error, bus busy, or arbitration lost. Try reset. */ - reset_controller(controller); - select_port(port); - - /* - * We don't know what edges the slave saw, so sleep long enough - * that the slave will see the new start condition below. - */ - usleep(1000); - } - - if (out_size) { - if (send_start) { - MEC1322_I2C_DATA(controller) = - (uint8_t)(I2C_GET_ADDR(slave_addr_flags) - << 1); - - /* Clock out the slave address, sending START bit */ - MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO | - CTRL_ENI | CTRL_ACK | - CTRL_STA; - cdata[controller].transaction_state = - I2C_TRANSACTION_OPEN; - } - - for (i = 0; i < out_size; ++i) { - ret_done = wait_byte_done(controller); - if (ret_done) - goto err_chip_i2c_xfer; - MEC1322_I2C_DATA(controller) = out[i]; - } - ret_done = wait_byte_done(controller); - if (ret_done) - goto err_chip_i2c_xfer; - - /* - * Send STOP bit if the stop flag is on, and caller - * doesn't expect to receive data. - */ - if (send_stop && in_size == 0) { - MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO | - CTRL_STO | CTRL_ACK; - cdata[controller].transaction_state = - I2C_TRANSACTION_STOPPED; - } - } - - if (in_size) { - /* Resend start bit when changing direction */ - if (out_size || send_start) { - /* Repeated start case */ - if (cdata[controller].transaction_state == - I2C_TRANSACTION_OPEN) - MEC1322_I2C_CTRL(controller) = CTRL_ESO | - CTRL_STA | - CTRL_ACK | - CTRL_ENI; - - MEC1322_I2C_DATA(controller) = - (uint8_t)(I2C_GET_ADDR(slave_addr_flags) - << 1) - | 0x01; - - /* New transaction case, clock out slave address. */ - if (cdata[controller].transaction_state == - I2C_TRANSACTION_STOPPED) - MEC1322_I2C_CTRL(controller) = CTRL_ESO | - CTRL_STA | - CTRL_ACK | - CTRL_ENI | - CTRL_PIN; - - cdata[controller].transaction_state = - I2C_TRANSACTION_OPEN; - - /* Skip over the dummy byte */ - skip = 1; - in_size++; - } - - /* Special flags need to be set for last two bytes */ - bytes_to_read = send_stop ? in_size - 2 : in_size; - - for (i = 0; i < bytes_to_read; ++i) { - ret_done = wait_byte_done(controller); - if (ret_done) - goto err_chip_i2c_xfer; - push_in_buf(&in, MEC1322_I2C_DATA(controller), skip); - skip = 0; - } - ret_done = wait_byte_done(controller); - if (ret_done) - goto err_chip_i2c_xfer; - - if (send_stop) { - /* - * De-assert ACK bit before reading the next to last - * byte, so that the last byte is NACK'ed. - */ - MEC1322_I2C_CTRL(controller) = CTRL_ESO | CTRL_ENI; - push_in_buf(&in, MEC1322_I2C_DATA(controller), skip); - ret_done = wait_byte_done(controller); - if (ret_done) - goto err_chip_i2c_xfer; - - /* Send STOP */ - MEC1322_I2C_CTRL(controller) = - CTRL_PIN | CTRL_ESO | CTRL_ACK | CTRL_STO; - - cdata[controller].transaction_state = - I2C_TRANSACTION_STOPPED; - - /* - * We need to know our stop point two bytes in - * advance. If we don't know soon enough, we need - * to do an extra dummy read (to last_addr + 1) to - * issue the stop. - */ - push_in_buf(&in, MEC1322_I2C_DATA(controller), - in_size == 1); - } - } - - /* Check for error conditions */ - if (MEC1322_I2C_STATUS(controller) & (STS_LAB | STS_BER)) - return EC_ERROR_UNKNOWN; - - return EC_SUCCESS; -err_chip_i2c_xfer: - /* Send STOP and return error */ - MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO | - CTRL_STO | CTRL_ACK; - cdata[controller].transaction_state = I2C_TRANSACTION_STOPPED; - if (ret_done == STS_LRB) - return EC_ERROR_BUSY; - else if (ret_done == EC_ERROR_TIMEOUT) { - /* - * If our transaction timed out then our i2c controller - * may be wedged without showing any other outward signs - * of failure. Reset the controller so that future - * transactions have a chance of success. - */ - reset_controller(controller); - return EC_ERROR_TIMEOUT; - } - else - return EC_ERROR_UNKNOWN; -} - -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); -} - -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); -} - -int i2c_get_line_levels(int port) -{ - int rv; - - i2c_lock(port, 1); - select_port(port); - rv = get_line_level(i2c_port_to_controller(port)); - i2c_lock(port, 0); - return rv; -} - -int i2c_port_to_controller(int port) -{ - if (port < 0 || port >= MEC1322_I2C_PORT_COUNT) - return -1; - return (port == MEC1322_I2C0_0) ? 0 : port - 1; -} - -void i2c_set_timeout(int port, uint32_t timeout) -{ - /* Param is port, but timeout is stored by-controller. */ - cdata[i2c_port_to_controller(port)].timeout_us = - timeout ? timeout : I2C_TIMEOUT_DEFAULT_US; -} - -void i2c_init(void) -{ - int i; - int controller; - int controller0_kbps = -1; - - /* Configure GPIOs */ - gpio_config_module(MODULE_I2C, 1); - - for (i = 0; i < i2c_ports_used; ++i) { - /* - * If this controller has multiple ports, check if we already - * configured it. If so, ensure previously configured bitrate - * matches. - */ - controller = i2c_port_to_controller(i2c_ports[i].port); - if (controller == 0) { - if (controller0_kbps != -1) { - ASSERT(controller0_kbps == i2c_ports[i].kbps); - continue; - } - controller0_kbps = i2c_ports[i].kbps; - } - configure_controller(controller, i2c_ports[i].kbps); - 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); - } -} - -static void handle_interrupt(int controller) -{ - int id = cdata[controller].task_waiting; - - /* Clear the interrupt status */ - MEC1322_I2C_COMPLETE(controller) &= (COMP_RW_BITS_MASK | COMP_IDLE); - - /* - * 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. - */ - task_disable_irq(MEC1322_IRQ_I2C_0 + 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, 0); -} - -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); } - -DECLARE_IRQ(MEC1322_IRQ_I2C_0, i2c0_interrupt, 2); -DECLARE_IRQ(MEC1322_IRQ_I2C_1, i2c1_interrupt, 2); -DECLARE_IRQ(MEC1322_IRQ_I2C_2, i2c2_interrupt, 2); -DECLARE_IRQ(MEC1322_IRQ_I2C_3, i2c3_interrupt, 2); |