diff options
Diffstat (limited to 'common/i2c_bitbang.c')
-rw-r--r-- | common/i2c_bitbang.c | 363 |
1 files changed, 0 insertions, 363 deletions
diff --git a/common/i2c_bitbang.c b/common/i2c_bitbang.c deleted file mode 100644 index 86d76a8b47..0000000000 --- a/common/i2c_bitbang.c +++ /dev/null @@ -1,363 +0,0 @@ -/* Copyright 2019 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 "console.h" -#include "gpio.h" -#include "i2c_bitbang.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#define CPUTS(str) cputs(CC_I2C, str) - -static int started; - -/* TODO: respect i2c_port->kbps setting */ -static void i2c_delay(void) -{ - udelay(5); -} - -/* Number of attempts to unwedge each pin. */ -#define UNWEDGE_SCL_ATTEMPTS 10 -#define UNWEDGE_SDA_ATTEMPTS 3 - -static void i2c_bitbang_unwedge(const struct i2c_port_t *i2c_port) -{ - int i, j; - - gpio_set_level(i2c_port->scl, 1); - /* - * If clock is low, wait for a while in case of clock stretched - * by a peripheral. - */ - if (!gpio_get_level(i2c_port->scl)) { - for (i = 0;; i++) { - if (i >= UNWEDGE_SCL_ATTEMPTS) { - /* - * If we get here, a peripheral is holding the - * clock low and there is nothing we can do. - */ - CPUTS("I2C unwedge failed, SCL is held low\n"); - return; - } - i2c_delay(); - if (gpio_get_level(i2c_port->scl)) - break; - } - } - - if (gpio_get_level(i2c_port->sda)) - return; - - CPUTS("I2C unwedge called with SDA held low\n"); - - /* Keep trying to unwedge the SDA line until we run out of attempts. */ - for (i = 0; i < UNWEDGE_SDA_ATTEMPTS; i++) { - /* Drive the clock high. */ - gpio_set_level(i2c_port->scl, 0); - i2c_delay(); - - /* - * Clock through the problem by clocking out 9 bits. If - * peripheral releases the SDA line, then we can stop clocking - * bits and send a STOP. - */ - for (j = 0; j < 9; j++) { - if (gpio_get_level(i2c_port->sda)) - break; - - gpio_set_level(i2c_port->scl, 0); - i2c_delay(); - gpio_set_level(i2c_port->scl, 1); - i2c_delay(); - } - - /* Take control of SDA line and issue a STOP command. */ - gpio_set_level(i2c_port->sda, 0); - i2c_delay(); - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - /* Check if the bus is unwedged. */ - if (gpio_get_level(i2c_port->sda) && - gpio_get_level(i2c_port->scl)) - break; - } - - if (!gpio_get_level(i2c_port->sda)) - CPUTS("I2C unwedge failed, SDA still low\n"); - if (!gpio_get_level(i2c_port->scl)) - CPUTS("I2C unwedge failed, SCL still low\n"); -} - -static void i2c_stop_cond(const struct i2c_port_t *i2c_port) -{ - int i; - - if (!started) - return; - - gpio_set_level(i2c_port->sda, 0); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - - /* - * SMBus 3.0, 4.2.5 - * - * the recommendation is that if SMBDAT is still low tTIMEOUT,MAX after - * SMBCLK has gone high at the end of a transaction the controller - * should hold SMBCLK low for at least tTIMEOUT,MAX in an attempt to - * reset the SMBus interface of all of the devices on the bus. - */ - for (i = 0; i < 7000; i++) { - if (gpio_get_level(i2c_port->scl)) - break; - i2c_delay(); - } - i2c_delay(); - - /* SCL is high, set SDA from 0 to 1 */ - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - started = 0; -} - -static int clock_stretching(const struct i2c_port_t *i2c_port) -{ - int i; - - i2c_delay(); - /* 5us * 7000 iterations ~= 35ms */ - for (i = 0; i < 7000; i++) { - if (gpio_get_level(i2c_port->scl)) - return 0; - i2c_delay(); - } - - /* - * SMBus 3.0, Note 3 - * Devices participating in a transfer can abort the transfer in - * progress and release the bus when any single clock low interval - * exceeds the value of tTIMEOUT,MIN(=25ms). - * After the controller in a transaction detects this condition, it must - * generate a stop condition within or after the current data byte in - * the transfer process. - */ - i2c_stop_cond(i2c_port); - CPUTS("clock low timeout\n"); - - return EC_ERROR_TIMEOUT; -} - -static int i2c_start_cond(const struct i2c_port_t *i2c_port) -{ - int err; - - if (started) { - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - err = clock_stretching(i2c_port); - if (err) - return err; - i2c_delay(); - - if (gpio_get_level(i2c_port->sda) == 0) { - CPUTS("start_cond: arbitration lost\n"); - started = 0; - return EC_ERROR_UNKNOWN; - } - } - - /* check if bus is idle before starting */ - if (gpio_get_level(i2c_port->scl) == 0 || - gpio_get_level(i2c_port->sda) == 0) - return EC_ERROR_UNKNOWN; - - gpio_set_level(i2c_port->sda, 0); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 0); - started = 1; - - return 0; -} - -static int i2c_write_bit(const struct i2c_port_t *i2c_port, int bit) -{ - int err; - - gpio_set_level(i2c_port->sda, !!bit); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - err = clock_stretching(i2c_port); - if (err) - return err; - i2c_delay(); - - if (bit && gpio_get_level(i2c_port->sda) == 0) { - CPUTS("write_bit: arbitration lost\n"); - started = 0; - return EC_ERROR_UNKNOWN; - } - - gpio_set_level(i2c_port->scl, 0); - - return 0; -} - -static int i2c_read_bit(const struct i2c_port_t *i2c_port, int *bit) -{ - int err; - - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - err = clock_stretching(i2c_port); - if (err) - return err; - i2c_delay(); - *bit = gpio_get_level(i2c_port->sda); - - gpio_set_level(i2c_port->scl, 0); - - return 0; -} - -static int i2c_write_byte(const struct i2c_port_t *i2c_port, uint8_t byte) -{ - int i, nack, err; - - for (i = 7; i >= 0; i--) { - err = i2c_write_bit(i2c_port, byte & (1 << i)); - if (err) - return err; - } - - err = i2c_read_bit(i2c_port, &nack); - if (err) - return err; - - if (nack) { - /* - * The peripheral device detects an invalid command or invalid - * data. In this case the peripheral device must NACK the - * received byte. The controller upon detection of this - * condition must generate a STOP condition and retry the - * transaction - */ - i2c_stop_cond(i2c_port); - /* return EC_ERROR_BUSY to indicate i2c_xfer() to retry */ - return EC_ERROR_BUSY; - } - return 0; -} - -static int i2c_read_byte(const struct i2c_port_t *i2c_port, uint8_t *byte, - int nack) -{ - int i; - - *byte = 0; - for (i = 0; i < 8; i++) { - int bit = 0, err; - - err = i2c_read_bit(i2c_port, &bit); - if (err) - return err; - *byte = (*byte << 1) | bit; - } - - return i2c_write_bit(i2c_port, nack); -} - -static int i2c_bitbang_xfer(const struct i2c_port_t *i2c_port, - const uint16_t addr_flags, - const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags) -{ - uint16_t addr_8bit = addr_flags << 1, err = EC_SUCCESS; - int i = 0; - - if (i2c_port->kbps != 100) - CPUTS("warning: bitbang driver only supports 100kbps\n"); - - if (out_size) { - if (flags & I2C_XFER_START) { - err = i2c_start_cond(i2c_port); - if (err) - goto exit; - err = i2c_write_byte(i2c_port, addr_8bit); - if (err) - goto exit; - } - - for (i = 0; i < out_size; i++) { - err = i2c_write_byte(i2c_port, out[i]); - if (err) - goto exit; - } - } - - if (in_size) { - if (flags & I2C_XFER_START) { - err = i2c_start_cond(i2c_port); - if (err) - goto exit; - err = i2c_write_byte(i2c_port, addr_8bit | 1); - if (err) - goto exit; - } - - for (i = 0; i < in_size; i++) { - err = i2c_read_byte(i2c_port, &in[i], - (flags & I2C_XFER_STOP) && (i == in_size - 1)); - if (err) - goto exit; - } - } - - if (flags & I2C_XFER_STOP) - i2c_stop_cond(i2c_port); - -exit: - if (err) { - i2c_bitbang_unwedge(i2c_port); - started = 0; - } - return err; -} - -const struct i2c_drv bitbang_drv = { - .xfer = &i2c_bitbang_xfer -}; - -#ifdef TEST_BUILD -int bitbang_start_cond(const struct i2c_port_t *i2c_port) -{ - return i2c_start_cond(i2c_port); -} - -void bitbang_stop_cond(const struct i2c_port_t *i2c_port) -{ - i2c_stop_cond(i2c_port); -} - -int bitbang_write_byte(const struct i2c_port_t *i2c_port, uint8_t byte) -{ - return i2c_write_byte(i2c_port, byte); -} - -void bitbang_set_started(int val) -{ - started = val; -} -#endif |