summaryrefslogtreecommitdiff
path: root/src/soc
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2014-05-05 18:03:46 -0700
committerMarc Jones <marc.jones@se-eng.com>2014-12-30 22:07:42 +0100
commit37d7ac8b5ba12d4618b8a91f35d444fe9572beb4 (patch)
tree8d395e92cb35d27ad6ad3c2c129c07878317faae /src/soc
parentb4bd53a3cba45b2cbf86b8b020bb8a678e583f97 (diff)
i2c: Add software_i2c driver for I2C debugging and emulation
This patch adds I2C emulation in software through raw toggling of the SDA/SCL lines. Platforms need to provide bindings to toggle their respective I2C busses for this to work (e.g. by pinmuxing them as GPIOs, currently only enabled for Tegra). This is mostly useful as a debugging feature, to drive unusual states on a bus and closely monitor the device output without the need of a bus analyzer. It provides a few functions to "wedge" an I2C bus by aborting a transaction at certain points, which can be used to test if a system can correctly recover from an ill-timed reboot. However, it can also dynamically replace the existing I2C transfer functions and drive some/all I2C transfers on the system, which might be useful if a driver for the actual I2C controller hardware is not (yet) available. Based on original code by Doug Anderson <dianders@chromium.org> and Hung-ying Tyan <tyanh@chromium.org> for the ChromeOS embedded controller project. BRANCH=None BUG=chrome-os-partner:28323 TEST=Spread tegra_software_i2c_init()/tegra_software_i2c_disable() through the code and see that everything still works. Original-Change-Id: I9ee7ccbd1efb38206669a35d0c3318af16f8be63 Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/198791 Original-Reviewed-by: Doug Anderson <dianders@chromium.org> Original-Reviewed-by: Tom Warren <twarren@nvidia.com> Original-Reviewed-by: Stefan Reinauer <reinauer@chromium.org> (cherry picked from commit 8f71503dbbd74c5298e90e2163b67d4efe3e89db) Signed-off-by: Marc Jones <marc.jones@se-eng.com> Change-Id: Id6c5f75bb5baaabd62b6b1fc26c2c71d9f1ce682 Reviewed-on: http://review.coreboot.org/7947 Tested-by: build bot (Jenkins) Reviewed-by: David Hendricks <dhendrix@chromium.org>
Diffstat (limited to 'src/soc')
-rw-r--r--src/soc/nvidia/tegra/i2c.c2
-rw-r--r--src/soc/nvidia/tegra/i2c.h2
-rw-r--r--src/soc/nvidia/tegra/software_i2c.c96
-rw-r--r--src/soc/nvidia/tegra124/Makefile.inc3
-rw-r--r--src/soc/samsung/exynos5250/i2c.c2
-rw-r--r--src/soc/samsung/exynos5420/i2c.c2
6 files changed, 104 insertions, 3 deletions
diff --git a/src/soc/nvidia/tegra/i2c.c b/src/soc/nvidia/tegra/i2c.c
index 6c269cfaed..555d0318c1 100644
--- a/src/soc/nvidia/tegra/i2c.c
+++ b/src/soc/nvidia/tegra/i2c.c
@@ -180,7 +180,7 @@ static int i2c_transfer_segment(unsigned bus, unsigned chip, int restart,
return 0;
}
-int i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
+int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
{
struct i2c_seg *seg = segments;
diff --git a/src/soc/nvidia/tegra/i2c.h b/src/soc/nvidia/tegra/i2c.h
index 6347fff8ad..4b1bddd2af 100644
--- a/src/soc/nvidia/tegra/i2c.h
+++ b/src/soc/nvidia/tegra/i2c.h
@@ -23,6 +23,8 @@
#include <stdint.h>
void i2c_init(unsigned bus);
+void tegra_software_i2c_init(unsigned bus);
+void tegra_software_i2c_disable(unsigned bus);
enum {
/* Word 0 */
diff --git a/src/soc/nvidia/tegra/software_i2c.c b/src/soc/nvidia/tegra/software_i2c.c
new file mode 100644
index 0000000000..b50fb4f654
--- /dev/null
+++ b/src/soc/nvidia/tegra/software_i2c.c
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include <device/i2c.h>
+#include <soc/nvidia/tegra124/gpio.h>
+#include <soc/nvidia/tegra124/pinmux.h>
+
+#include "i2c.h"
+
+static struct {
+ int pinmux_func;
+ gpio_t sda;
+ gpio_t scl;
+} pins[] = {
+ [0]{.pinmux_func = PINMUX_GEN1_I2C_SCL_FUNC_I2C1,
+ .sda = GPIO(C5), .scl = GPIO(C4)},
+ [1]{.pinmux_func = PINMUX_GEN2_I2C_SCL_FUNC_I2C2,
+ .sda = GPIO(T6), .scl = GPIO(T5)},
+ [2]{.pinmux_func = PINMUX_CAM_I2C_SCL_FUNC_I2C3,
+ .sda = GPIO(BB2), .scl = GPIO(BB1)},
+ [3]{.pinmux_func = PINMUX_DDC_SCL_FUNC_I2C4,
+ .sda = GPIO(V5), .scl = GPIO(V4)},
+ [4]{.pinmux_func = PINMUX_PWR_I2C_SCL_FUNC_I2CPMU,
+ .sda = GPIO(Z7), .scl = GPIO(Z6)},
+};
+
+static void tegra_set_sda(unsigned bus, int high)
+{
+ if (high)
+ gpio_input_pullup(pins[bus].sda);
+ else
+ gpio_output(pins[bus].sda, 0);
+}
+
+static void tegra_set_scl(unsigned bus, int high)
+{
+ if (high)
+ gpio_input_pullup(pins[bus].scl);
+ else
+ gpio_output(pins[bus].scl, 0);
+}
+
+static int tegra_get_sda(unsigned bus)
+{
+ return gpio_get_in_value(pins[bus].sda);
+}
+
+static int tegra_get_scl(unsigned bus)
+{
+ return gpio_get_in_value(pins[bus].scl);
+}
+
+static struct software_i2c_ops tegra_ops = {
+ .set_sda = tegra_set_sda,
+ .set_scl = tegra_set_scl,
+ .get_sda = tegra_get_sda,
+ .get_scl = tegra_get_scl,
+};
+
+void tegra_software_i2c_init(unsigned bus)
+{
+ software_i2c[bus] = &tegra_ops;
+
+ /* Initialize bus to idle state. */
+ tegra_set_sda(bus, 1);
+ tegra_set_scl(bus, 1);
+}
+
+void tegra_software_i2c_disable(unsigned bus)
+{
+ software_i2c[bus] = NULL;
+
+ /* Return pins to I2C controller. */
+ pinmux_set_config(pins[bus].sda >> GPIO_PINMUX_SHIFT,
+ pins[bus].pinmux_func | PINMUX_INPUT_ENABLE);
+ pinmux_set_config(pins[bus].scl >> GPIO_PINMUX_SHIFT,
+ pins[bus].pinmux_func | PINMUX_INPUT_ENABLE);
+ gpio_set_mode(pins[bus].sda, GPIO_MODE_SPIO);
+ gpio_set_mode(pins[bus].scl, GPIO_MODE_SPIO);
+}
diff --git a/src/soc/nvidia/tegra124/Makefile.inc b/src/soc/nvidia/tegra124/Makefile.inc
index c7b2e16c4b..792bb9992b 100644
--- a/src/soc/nvidia/tegra124/Makefile.inc
+++ b/src/soc/nvidia/tegra124/Makefile.inc
@@ -11,6 +11,7 @@ bootblock-y += power.c
bootblock-y += spi.c
bootblock-y += ../tegra/gpio.c
bootblock-y += ../tegra/i2c.c
+bootblock-$(CONFIG_SOFTWARE_I2C) += ../tegra/software_i2c.c
bootblock-y += ../tegra/pingroup.c
bootblock-y += ../tegra/pinmux.c
bootblock-y += ../tegra/apbmisc.c
@@ -32,6 +33,7 @@ romstage-y += sdram_lp0.c
romstage-y += spi.c
romstage-y += ../tegra/gpio.c
romstage-y += ../tegra/i2c.c
+romstage-$(CONFIG_SOFTWARE_I2C) += ../tegra/software_i2c.c
romstage-y += ../tegra/pinmux.c
romstage-y += timer.c
romstage-$(CONFIG_CONSOLE_SERIAL) += uart.c
@@ -51,6 +53,7 @@ ramstage-y += spi.c
ramstage-y += dp.c
ramstage-y += ../tegra/gpio.c
ramstage-y += ../tegra/i2c.c
+ramstage-$(CONFIG_SOFTWARE_I2C) += ../tegra/software_i2c.c
ramstage-y += ../tegra/pinmux.c
ramstage-y += ../tegra/usb.c
ramstage-y += timer.c
diff --git a/src/soc/samsung/exynos5250/i2c.c b/src/soc/samsung/exynos5250/i2c.c
index d5d83b393a..1e526d633a 100644
--- a/src/soc/samsung/exynos5250/i2c.c
+++ b/src/soc/samsung/exynos5250/i2c.c
@@ -237,7 +237,7 @@ static int i2c_recv_buf(struct i2c_regs *regs, uint8_t *data, int len)
return 0;
}
-int i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count)
+int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count)
{
struct s3c24x0_i2c_bus *i2c = &i2c_busses[bus];
struct i2c_regs *regs = i2c->regs;
diff --git a/src/soc/samsung/exynos5420/i2c.c b/src/soc/samsung/exynos5420/i2c.c
index b79f9144a9..49875d79ed 100644
--- a/src/soc/samsung/exynos5420/i2c.c
+++ b/src/soc/samsung/exynos5420/i2c.c
@@ -633,7 +633,7 @@ static int i2c_recv_buf(struct i2c_regs *regs, uint8_t *data, int len)
return 0;
}
-int i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
+int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
{
struct i2c_bus *i2c = &i2c_busses[bus];
if (i2c->is_highspeed)