Marc Jones (marc.jones(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7951
-gerrit
commit 9656c1a22c09e60ad194fa222a8420c5bb4908ec
Author: Vince Hsu <vinceh(a)nvidia.com>
Date: Fri May 16 18:07:53 2014 +0800
tegra124: Active dc/sor register change immediately
When doing DP attach, we need to make sure the register change to
take effect immediately, otherwise it may fail to catch the attach
timing.
BRANCH=None
BUG=chrome-os-partner:28128
TEST=Display works and system boots up on Nyan and Big
Original-Change-Id: I569dc435a1aa4aac0d5ecd0655d2ad87a791246d
Original-Signed-off-by: Vince Hsu <vinceh(a)nvidia.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/200414
Original-Reviewed-by: Hung-Te Lin <hungte(a)chromium.org>
Original-Reviewed-by: Jimmy Zhang <jimmzhang(a)nvidia.com>
Original-Reviewed-by: David Hendricks <dhendrix(a)chromium.org>
(cherry picked from commit 47b86e2893fa667bebada6a0e0b443886dd5ee02)
Signed-off-by: Marc Jones <marc.jones(a)se-eng.com>
Change-Id: Icf809b46e675bbdb8633d9a4f31d005d6644bd2a
---
src/soc/nvidia/tegra124/sor.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/soc/nvidia/tegra124/sor.c b/src/soc/nvidia/tegra124/sor.c
index 0e96764..2c059bf 100644
--- a/src/soc/nvidia/tegra124/sor.c
+++ b/src/soc/nvidia/tegra124/sor.c
@@ -762,9 +762,11 @@ void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor)
tegra_dc_sor_super_update(sor);
/* Enable dc */
+ reg_val = READL(&disp_ctrl->cmd.state_access);
+ WRITEL(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd);
WRITEL(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
- WRITEL(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+ WRITEL(reg_val, &disp_ctrl->cmd.state_access);
if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST,
NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
Marc Jones (marc.jones(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7950
-gerrit
commit f078705d276b55285c11478ed28c1068e3d879ee
Author: Vince Hsu <vinceh(a)nvidia.com>
Date: Fri May 16 12:46:02 2014 +0800
tegra124: display clock should be initialized before any access
We initialized the dc before the plld's initialization. So some
of the dc init settings did not took effect. This patch moves
the clock_display() before the dc init call.
BRANCH=None
BUG=chrome-os-partner:28128
TEST=Display works and system boots up on Nyan and Big
Original-Change-Id: If2c40e2526fdf7a6aa33a2684ba324bd0ec40e90
Original-Signed-off-by: Vince Hsu <vinceh(a)nvidia.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/200413
Original-Reviewed-by: Tom Warren <twarren(a)nvidia.com>
Original-Reviewed-by: Jimmy Zhang <jimmzhang(a)nvidia.com>
Original-Reviewed-by: David Hendricks <dhendrix(a)chromium.org>
Original-Reviewed-by: Hung-Te Lin <hungte(a)chromium.org>
Original-Commit-Queue: David Hendricks <dhendrix(a)chromium.org>
Original-Tested-by: David Hendricks <dhendrix(a)chromium.org>
(cherry picked from commit dc3cc253c319c21772c30962d963ec9dfc4944a7)
Signed-off-by: Marc Jones <marc.jones(a)se-eng.com>
Change-Id: I021290f4293c740666d460f73fecbe79146896a4
---
src/soc/nvidia/tegra124/display.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/soc/nvidia/tegra124/display.c b/src/soc/nvidia/tegra124/display.c
index 898e9b3..9ad76f0 100644
--- a/src/soc/nvidia/tegra124/display.c
+++ b/src/soc/nvidia/tegra124/display.c
@@ -135,7 +135,7 @@ static int update_display_mode(struct display_controller *disp_ctrl,
&disp_ctrl->disp.disp_clk_ctrl);
printk(BIOS_DEBUG, "%s: PixelClock=%u, ShiftClockDiv=%u\n",
__func__, config->pixel_clock, shift_clock_div);
- return clock_display(config->pixel_clock * shift_clock_div * 2);
+ return 0;
}
static void update_window(struct display_controller *disp_ctrl,
@@ -278,6 +278,15 @@ void display_startup(device_t dev)
* light things up here once we're sure it's all working.
*/
+ /* The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
+ * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
+ * update_display_mode() for detail.
+ */
+ if (clock_display(config->pixel_clock * 2)) {
+ printk(BIOS_ERR, "dc: clock init failed\n");
+ return;
+ };
+
/* Init dc */
if (tegra_dc_init(disp_ctrl)) {
printk(BIOS_ERR, "dc: init failed\n");
Marc Jones (marc.jones(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7949
-gerrit
commit 2d4a6cf32322b2fa731de4cc986637f81063a991
Author: Neil Chen <neilc(a)nvidia.com>
Date: Mon May 19 18:53:14 2014 +0800
nyan_blaze: Enable USB port2
There is a hub in USB port2 downstream.
BUG=chrome-os-partner:28964
BRANCH=None
TEST=emerge-nyan_blaze coreboot depthcharge chromeos-bootimage and verify usb
port2 is workable
Original-Change-Id: I0e698970729911f401f89594232f9d49e4da93cc
Original-Signed-off-by: Neil Chen <neilc(a)nvidia.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/200417
Original-Reviewed-by: Tom Warren <twarren(a)nvidia.com>
Original-Reviewed-by: Julius Werner <jwerner(a)chromium.org>
(cherry picked from commit 9316acfe8791585f778eecead95943e6422ca419)
Signed-off-by: Marc Jones <marc.jones(a)se-eng.com>
Change-Id: I76e4331ea6e803bfbbddefab449310421c0c1d9c
---
src/mainboard/google/nyan_blaze/mainboard.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/mainboard/google/nyan_blaze/mainboard.c b/src/mainboard/google/nyan_blaze/mainboard.c
index 3e6725b..0c5078f 100644
--- a/src/mainboard/google/nyan_blaze/mainboard.c
+++ b/src/mainboard/google/nyan_blaze/mainboard.c
@@ -213,7 +213,7 @@ static void mainboard_init(device_t dev)
CLK_L_HOST1X | CLK_L_PWM,
CLK_H_EMC | CLK_H_I2C2 | CLK_H_PMC |
- CLK_H_MEM | CLK_H_USB3,
+ CLK_H_MEM | CLK_H_USB2 | CLK_H_USB3,
CLK_U_CSITE | CLK_U_SDMMC3,
@@ -229,7 +229,7 @@ static void mainboard_init(device_t dev)
CLK_X_AFC5);
usb_setup_utmip1();
- /* USB2 is the camera, we don't need it in firmware */
+ usb_setup_utmip2();
usb_setup_utmip3();
setup_pinmux();
Marc Jones (marc.jones(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7948
-gerrit
commit d9fd4e4e7cbd00ad78930e5e3fd71b0c394b0e48
Author: Julius Werner <jwerner(a)chromium.org>
Date: Fri May 16 11:27:46 2014 -0700
tegra: i2c: Add a timeout to I2C bit clear recovery mechanism
Our tests with the I2C bit clear mechanism (recovering from "lost
arbitration" errors) show that the bit clear hardware does not work
correctly in some situations. When a wedged slave device tries to send
more than one 0-to-1-to-0 transition to the host (e.g. leftover bits
from an aborted read), the controller never transitions the BC_ENABLE
bit back to zero.
This patch adds a long timeout to the bit clear code that waits for
register transitions as a safeguard. This way, We will still eventually
exit the function (probably followed by a reboot). Our tests show that
this will recover from all conditions after at most a few reboots.
BRANCH=nyan
BUG=chrome-os-partner:28323
TEST=Ran wedge_ack and wedge_read tests with software_i2c patch, system
recovered as expected in all cases.
Original-Change-Id: I6c37119130e1240e1ef3a5944582abbcd2e39ff0
Original-Signed-off-by: Julius Werner <jwerner(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/200265
Original-Reviewed-by: Tom Warren <twarren(a)nvidia.com>
Original-Reviewed-by: Gabe Black <gabeblack(a)chromium.org>
(cherry picked from commit 4c8d0af25cf107a38c856b38067b8f2f74384f22)
Signed-off-by: Marc Jones <marc.jones(a)se-eng.com>
Change-Id: I600d5c9a8e68719cf8795c083c5fac63f626f5bf
---
src/soc/nvidia/tegra/i2c.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/soc/nvidia/tegra/i2c.c b/src/soc/nvidia/tegra/i2c.c
index 555d031..26c2559 100644
--- a/src/soc/nvidia/tegra/i2c.c
+++ b/src/soc/nvidia/tegra/i2c.c
@@ -19,6 +19,7 @@
#include <arch/io.h>
#include <console/console.h>
+#include <delay.h>
#include <device/i2c.h>
#include <stdlib.h>
#include <string.h>
@@ -30,6 +31,7 @@ static void do_bus_clear(int bus)
struct tegra_i2c_bus_info *info = &tegra_i2c_info[bus];
struct tegra_i2c_regs * const regs = info->base;
uint32_t bc;
+ int i, timeout_ms = 10;
// BUS CLEAR regs (from TRM):
// 1. Reset the I2C controller (already done)
@@ -41,16 +43,20 @@ static void do_bus_clear(int bus)
write32(bc, ®s->bus_clear_config);
// 4.1 Set MSTR_CONFIG_LOAD and wait for clear
write32(I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE, ®s->config_load);
- do {
+ for (i = 0; i < timeout_ms * 10 && (read32(®s->config_load) &
+ I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE); i++) {
printk(BIOS_DEBUG, "%s: wait for MSTR_CONFIG_LOAD to clear\n",
__func__);
- } while (read32(®s->config_load) & I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE);
+ udelay(100);
+ }
// 5. Set ENABLE to start the bus clear op
write32(bc | I2C_BUS_CLEAR_CONFIG_BC_ENABLE, ®s->bus_clear_config);
- do {
+ for (i = 0; i < timeout_ms * 10 && (read32(®s->bus_clear_config) &
+ I2C_BUS_CLEAR_CONFIG_BC_ENABLE); i++) {
printk(BIOS_DEBUG, "%s: wait for bus clear completion\n",
__func__);
- } while (read32(®s->bus_clear_config) & I2C_BUS_CLEAR_CONFIG_BC_ENABLE);
+ udelay(100);
+ }
}
static int tegra_i2c_send_recv(int bus, int read,
Marc Jones (marc.jones(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7947
-gerrit
commit 74de8b4cfbe7d056304703e2d179c9193d859bb0
Author: Julius Werner <jwerner(a)chromium.org>
Date: Mon May 5 18:03:46 2014 -0700
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(a)chromium.org> and
Hung-ying Tyan <tyanh(a)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(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/198791
Original-Reviewed-by: Doug Anderson <dianders(a)chromium.org>
Original-Reviewed-by: Tom Warren <twarren(a)nvidia.com>
Original-Reviewed-by: Stefan Reinauer <reinauer(a)chromium.org>
(cherry picked from commit 8f71503dbbd74c5298e90e2163b67d4efe3e89db)
Signed-off-by: Marc Jones <marc.jones(a)se-eng.com>
Change-Id: Id6c5f75bb5baaabd62b6b1fc26c2c71d9f1ce682
---
src/device/Kconfig | 10 +
src/device/Makefile.inc | 4 +
src/device/software_i2c.c | 345 +++++++++++++++++++++++++++++++++++
src/include/device/i2c.h | 34 +++-
src/soc/nvidia/tegra/i2c.c | 2 +-
src/soc/nvidia/tegra/i2c.h | 2 +
src/soc/nvidia/tegra/software_i2c.c | 96 ++++++++++
src/soc/nvidia/tegra124/Makefile.inc | 3 +
src/soc/samsung/exynos5250/i2c.c | 2 +-
src/soc/samsung/exynos5420/i2c.c | 2 +-
10 files changed, 496 insertions(+), 4 deletions(-)
diff --git a/src/device/Kconfig b/src/device/Kconfig
index bd2f34b..67d01e0 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -376,6 +376,16 @@ config PXE_ROM_ID
Under GNU/Linux you can run `lspci -nn` to list the IDs of your PCI devices.
+config SOFTWARE_I2C
+ bool "Enable I2C controller emulation in software"
+ default n
+ help
+ This config option will enable code to override the i2c_transfer
+ routine with a (simple) software emulation of the protocol. This may
+ be useful for debugging or on platforms where a driver for the real
+ I2C controller is not (yet) available. The platform code needs to
+ provide bindings to manually toggle I2C lines.
+
endmenu
menu "Display"
diff --git a/src/device/Makefile.inc b/src/device/Makefile.inc
index a7f3364..7fdd9b5 100644
--- a/src/device/Makefile.inc
+++ b/src/device/Makefile.inc
@@ -24,3 +24,7 @@ romstage-$(CONFIG_PCI) += pci_early.c
subdirs-y += oprom dram
ramstage-$(CONFIG_VGA_ROM_RUN) += pci_rom.c
+
+bootblock-$(CONFIG_SOFTWARE_I2C) += software_i2c.c
+romstage-$(CONFIG_SOFTWARE_I2C) += software_i2c.c
+ramstage-$(CONFIG_SOFTWARE_I2C) += software_i2c.c
diff --git a/src/device/software_i2c.c b/src/device/software_i2c.c
new file mode 100644
index 0000000..6c9c200
--- /dev/null
+++ b/src/device/software_i2c.c
@@ -0,0 +1,345 @@
+/*
+ * 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 <assert.h>
+#include <timer.h>
+#include <console/console.h>
+#include <device/i2c.h>
+
+/*
+ * The implementation is based on Wikipedia.
+ */
+
+#define DEBUG 0 /* Set to 1 for per-byte output */
+#define SPEW 0 /* Set to 1 for verbose bitwise/line-state output */
+#define DELAY_US 5 /* Default setup delay: 4us (+1 for timer inaccuracy) */
+#define TIMEOUT_US 50000 /* Maximum clock stretching time we want to allow */
+
+#define spew(...) do { if (SPEW) printk(BIOS_SPEW, ##__VA_ARGS__); } while (0)
+
+struct software_i2c_ops *software_i2c[SOFTWARE_I2C_MAX_BUS];
+
+/*
+ * Waits until either timeout_us have passed or (iff for_scl is set) until SCL
+ * goes high. Will report random line changes during the wait and return SCL.
+ */
+static int __wait(unsigned bus, int timeout_us, int for_scl)
+{
+ int us;
+ int sda = software_i2c[bus]->get_sda(bus);
+ int scl = software_i2c[bus]->get_scl(bus);
+ struct mono_time start;
+ timer_monotonic_get(&start);
+
+ do {
+ int old_sda = sda;
+ int old_scl = scl;
+ struct rela_time diff = current_time_from(&start);
+ us = rela_time_in_microseconds(&diff);
+
+ if (old_sda != (sda = software_i2c[bus]->get_sda(bus)))
+ spew("[SDA transitioned to %d after %dus] ", sda, us);
+ if (old_scl != (scl = software_i2c[bus]->get_scl(bus)))
+ spew("[SCL transitioned to %d after %dus] ", scl, us);
+ } while (us < timeout_us && (!for_scl || !scl));
+
+ return scl;
+}
+
+/* Waits the default DELAY_US to allow line state to stabilize. */
+static void wait(unsigned bus)
+{
+ __wait(bus, DELAY_US, 0);
+}
+
+/* Waits until SCL goes high. Prints a contextual error message on timeout. */
+static int wait_for_scl(unsigned bus, const char *error_context)
+{
+ if (!__wait(bus, TIMEOUT_US, 1)) {
+ printk(BIOS_ERR, "software_i2c(%d): ERROR: Clock stretching "
+ "timeout %s!\n", bus, error_context);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int start_cond(unsigned bus)
+{
+ spew("software_i2c(%d): Sending start condition... ", bus);
+
+ /* SDA might not yet be high if repeated start. */
+ software_i2c[bus]->set_sda(bus, 1);
+ wait(bus);
+
+ /* Might need to wait for clock stretching if repeated start. */
+ software_i2c[bus]->set_scl(bus, 1);
+ if (wait_for_scl(bus, "before start condition"))
+ return -1;
+ wait(bus); /* Repeated start setup time, minimum 4.7us */
+
+ if (!software_i2c[bus]->get_sda(bus)) {
+ printk(BIOS_ERR, "software_i2c(%d): Arbitration lost trying "
+ "to send start condition!\n", bus);
+ return -1;
+ }
+
+ /* SCL is high, transition SDA low as first part of start condition. */
+ software_i2c[bus]->set_sda(bus, 0);
+ wait(bus);
+ assert(software_i2c[bus]->get_scl(bus));
+
+ /* Pull SCL low to finish start condition (next pulse will be data). */
+ software_i2c[bus]->set_scl(bus, 0);
+
+ spew("Start condition transmitted!\n");
+ return 0;
+}
+
+static int stop_cond(unsigned bus)
+{
+ spew("software_i2c(%d): Sending stop condition... ", bus);
+
+ /* SDA is unknown, set it to low. SCL must be low. */
+ software_i2c[bus]->set_sda(bus, 0);
+ wait(bus);
+
+ /* Clock stretching */
+ assert(!software_i2c[bus]->get_scl(bus));
+ software_i2c[bus]->set_scl(bus, 1);
+ if (wait_for_scl(bus, "before stop condition"))
+ return -1;
+ wait(bus); /* Stop bit setup time, minimum 4us */
+
+ /* SCL is high, transition SDA high to signal stop condition. */
+ software_i2c[bus]->set_sda(bus, 1);
+ wait(bus);
+ if (!software_i2c[bus]->get_sda(bus)) {
+ printk(BIOS_WARNING, "software_i2c(%d): WARNING: SDA low after "
+ "stop condition... access by another master or line "
+ "stuck from faulty slave?\n", bus);
+ /* Could theoretically happen with multi-master, so no -1. */
+ }
+
+ spew("Stop condition transmitted\n");
+ return 0;
+}
+
+static int out_bit(unsigned bus, int bit)
+{
+ spew("software_i2c(%d): Sending a %d bit... ", bus, bit);
+
+ software_i2c[bus]->set_sda(bus, bit);
+ wait(bus);
+
+ if (bit && !software_i2c[bus]->get_sda(bus)) {
+ printk(BIOS_ERR, "software_i2c(%d): ERROR: SDA wedged low "
+ "by slave before clock pulse on transmit!\n", bus);
+ return -1;
+ }
+
+ /* Clock stretching */
+ assert(!software_i2c[bus]->get_scl(bus));
+ software_i2c[bus]->set_scl(bus, 1);
+ if (wait_for_scl(bus, "on transmit"))
+ return -1;
+ wait(bus);
+
+ if (bit && !software_i2c[bus]->get_sda(bus)) {
+ printk(BIOS_ERR, "software_i2c(%d): ERROR: SDA wedged low "
+ "by slave after clock pulse on transmit!\n", bus);
+ return -1;
+ }
+
+ assert(software_i2c[bus]->get_scl(bus));
+ software_i2c[bus]->set_scl(bus, 0);
+
+ spew("%d bit sent!\n", bit);
+ return 0;
+}
+
+static int in_bit(unsigned bus)
+{
+ int bit;
+
+ spew("software_i2c(%d): Receiving a bit... ", bus);
+
+ /* Let the slave drive data */
+ software_i2c[bus]->set_sda(bus, 1);
+ wait(bus);
+
+ /* Clock stretching */
+ assert(!software_i2c[bus]->get_scl(bus));
+ software_i2c[bus]->set_scl(bus, 1);
+ if (wait_for_scl(bus, "on receive"))
+ return -1;
+
+ /* SCL is high, now data is valid */
+ bit = software_i2c[bus]->get_sda(bus);
+ wait(bus);
+ assert(software_i2c[bus]->get_scl(bus));
+ software_i2c[bus]->set_scl(bus, 0);
+
+ spew("Received a %d!\n", bit);
+
+ return bit;
+}
+
+/* Write a byte to I2C bus. Return 0 if ack by the slave. */
+static int out_byte(unsigned bus, u8 byte)
+{
+ unsigned bit;
+ int nack;
+
+ for (bit = 0; bit < 8; bit++)
+ if (out_bit(bus, (byte >> (7 - bit)) & 0x1) < 0)
+ return -1;
+
+ nack = in_bit(bus);
+
+ if (DEBUG && nack >= 0)
+ printk(BIOS_DEBUG, "software_i2c(%d): wrote byte 0x%02x, "
+ "received %s\n", bus, byte, nack ? "NAK" : "ACK");
+
+ return nack;
+}
+
+static int in_byte(unsigned bus, int ack)
+{
+ u8 byte = 0;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ int bit = in_bit(bus);
+ if (bit < 0)
+ return -1;
+ byte = (byte << 1) | bit;
+ }
+
+ if (out_bit(bus, !ack) < 0)
+ return -1;
+
+ if (DEBUG)
+ printk(BIOS_DEBUG, "software_i2c(%d): read byte 0x%02x, "
+ "sent %s\n", bus, byte, ack ? "ACK" : "NAK");
+
+ return byte;
+}
+
+int software_i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
+{
+ int i;
+ struct i2c_seg *seg;
+
+ for (seg = segments; seg - segments < count; seg++) {
+ if (start_cond(bus) < 0)
+ return -1;
+ if (out_byte(bus, seg->chip << 1 | !!seg->read) < 0)
+ return -1;
+ for (i = 0; i < seg->len; i++) {
+ int ret;
+ if (seg->read) {
+ ret = in_byte(bus, i < seg->len - 1);
+ seg->buf[i] = (u8)ret;
+ } else {
+ ret = out_byte(bus, seg->buf[i]);
+ }
+ if (ret < 0)
+ return -1;
+ }
+ }
+ if (stop_cond(bus) < 0)
+ return -1;
+
+ return 0;
+}
+
+void software_i2c_wedge_ack(unsigned bus, u8 chip)
+{
+ int i;
+
+ /* Start a command to 'chip'... */
+ start_cond(bus);
+
+ /* Send the address bits but don't yet read the ACK. */
+ chip <<= 1;
+ for (i = 0; i < 8; ++i)
+ out_bit(bus, (chip >> (7 - i)) & 0x1);
+
+ /* Let the slave drive it's ACK but keep the clock high forever. */
+ software_i2c[bus]->set_sda(bus, 1);
+ wait(bus);
+ software_i2c[bus]->set_scl(bus, 1);
+ wait_for_scl(bus, "on wedge_ack()");
+
+ printk(BIOS_INFO, "software_i2c(%d): wedged address write on slave "
+ "ACK. SDA %d, SCL %d\n", bus, software_i2c[bus]->get_sda(bus),
+ software_i2c[bus]->get_scl(bus));
+}
+
+void software_i2c_wedge_read(unsigned bus, u8 chip, u8 reg, int bits)
+{
+ int i;
+
+ /* Start a command to 'chip'... */
+ start_cond(bus);
+ out_byte(bus, chip << 1);
+ /* ...for register 'reg'. */
+ out_byte(bus, reg);
+
+ /* Start a read command... */
+ start_cond(bus);
+ out_byte(bus, chip << 1 | 1);
+
+ /* Read bit_count bits and stop */
+ for (i = 0; i < bits; ++i)
+ in_bit(bus);
+
+ /* Let the slave drive SDA but keep the clock high forever. */
+ software_i2c[bus]->set_sda(bus, 1);
+ wait(bus);
+ software_i2c[bus]->set_scl(bus, 1);
+ wait_for_scl(bus, "on wedge_read()");
+
+ printk(BIOS_INFO, "software_i2c(%d): wedged data read after %d bits. "
+ "SDA %d, SCL %d\n", bus, bits, software_i2c[bus]->get_sda(bus),
+ software_i2c[bus]->get_scl(bus));
+}
+
+void software_i2c_wedge_write(unsigned bus, u8 chip, u8 reg, int bits)
+{
+ int i;
+
+ /* Start a command to 'chip'... */
+ start_cond(bus);
+ out_byte(bus, chip << 1);
+
+ /* Write bit_count register bits and stop */
+ for (i = 0; i < bits; ++i)
+ out_bit(bus, (reg >> (7 - i)) & 0x1);
+
+ /* Pretend to write another 1 bit but keep the clock high forever. */
+ software_i2c[bus]->set_sda(bus, 1);
+ wait(bus);
+ software_i2c[bus]->set_scl(bus, 1);
+ wait_for_scl(bus, "on wedge_write()");
+
+ printk(BIOS_INFO, "software_i2c(%d): wedged data write after %d bits. "
+ "SDA %d, SCL %d\n", bus, bits, software_i2c[bus]->get_sda(bus),
+ software_i2c[bus]->get_scl(bus));
+}
diff --git a/src/include/device/i2c.h b/src/include/device/i2c.h
index da9d1bc..c1f2a21 100644
--- a/src/include/device/i2c.h
+++ b/src/include/device/i2c.h
@@ -31,7 +31,39 @@ struct i2c_seg
int len;
};
-int i2c_transfer(unsigned bus, struct i2c_seg *segments, int count);
+int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int count);
+
+#define SOFTWARE_I2C_MAX_BUS 10 /* increase as necessary */
+
+struct software_i2c_ops {
+ void (*set_sda)(unsigned bus, int high);
+ void (*set_scl)(unsigned bus, int high);
+ int (*get_sda)(unsigned bus);
+ int (*get_scl)(unsigned bus);
+};
+
+extern struct software_i2c_ops *software_i2c[];
+
+int software_i2c_transfer(unsigned bus, struct i2c_seg *segments, int count);
+void software_i2c_wedge_ack(unsigned bus, u8 chip);
+void software_i2c_wedge_read(unsigned bus, u8 chip, u8 reg, int bit_count);
+void software_i2c_wedge_write(unsigned bus, u8 chip, u8 reg, int bit_count);
+
+/*
+ * software_i2c is supposed to be a debug feature. It's usually not compiled in,
+ * but when it is it can be dynamically enabled at runtime for certain busses.
+ * Need this ugly stub to arbitrate since I2C device drivers hardcode
+ * 'i2c_transfer()' as their entry point.
+ */
+static inline int i2c_transfer(unsigned bus, struct i2c_seg *segments,
+ int count)
+{
+ if (CONFIG_SOFTWARE_I2C)
+ if (bus < SOFTWARE_I2C_MAX_BUS && software_i2c[bus])
+ return software_i2c_transfer(bus, segments, count);
+
+ return platform_i2c_transfer(bus, segments, count);
+}
/*
* Read a raw chunk of data in one segment and one frame.
diff --git a/src/soc/nvidia/tegra/i2c.c b/src/soc/nvidia/tegra/i2c.c
index 6c269cf..555d031 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 6347fff..4b1bddd 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 0000000..b50fb4f
--- /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 c7b2e16..792bb99 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 d5d83b3..1e526d6 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 b79f914..49875d7 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)