summaryrefslogtreecommitdiff
path: root/common/i2c_bitbang.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/i2c_bitbang.c')
-rw-r--r--common/i2c_bitbang.c363
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