summaryrefslogtreecommitdiff
path: root/src/soc
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc')
-rw-r--r--src/soc/nvidia/tegra/i2c.c34
-rw-r--r--src/soc/nvidia/tegra/i2c.h17
2 files changed, 48 insertions, 3 deletions
diff --git a/src/soc/nvidia/tegra/i2c.c b/src/soc/nvidia/tegra/i2c.c
index 9a4d71bc3c..6c269cfaed 100644
--- a/src/soc/nvidia/tegra/i2c.c
+++ b/src/soc/nvidia/tegra/i2c.c
@@ -23,9 +23,36 @@
#include <stdlib.h>
#include <string.h>
#include <soc/addressmap.h>
-
#include "i2c.h"
+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;
+
+ // BUS CLEAR regs (from TRM):
+ // 1. Reset the I2C controller (already done)
+ // 2. Set the # of clock pulses required (using default of 9)
+ // 3. Select STOP condition (using default of 1 = STOP)
+ // 4. Set TERMINATE condition (1 = THRESHOLD)
+ bc = read32(&regs->bus_clear_config);
+ bc |= I2C_BUS_CLEAR_CONFIG_BC_TERMINATE_THRESHOLD;
+ write32(bc, &regs->bus_clear_config);
+ // 4.1 Set MSTR_CONFIG_LOAD and wait for clear
+ write32(I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE, &regs->config_load);
+ do {
+ printk(BIOS_DEBUG, "%s: wait for MSTR_CONFIG_LOAD to clear\n",
+ __func__);
+ } while (read32(&regs->config_load) & I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE);
+ // 5. Set ENABLE to start the bus clear op
+ write32(bc | I2C_BUS_CLEAR_CONFIG_BC_ENABLE, &regs->bus_clear_config);
+ do {
+ printk(BIOS_DEBUG, "%s: wait for bus clear completion\n",
+ __func__);
+ } while (read32(&regs->bus_clear_config) & I2C_BUS_CLEAR_CONFIG_BC_ENABLE);
+}
+
static int tegra_i2c_send_recv(int bus, int read,
uint32_t *headers, int header_words,
uint8_t *data, int data_len)
@@ -91,6 +118,11 @@ static int tegra_i2c_send_recv(int bus, int read,
"%s: Lost arbitration.\n",
__func__);
info->reset_func(info->reset_bit);
+
+ /* Use Tegra bus clear registers to unlock SDA */
+ do_bus_clear(bus);
+
+ /* Return w/error, let caller decide what to do */
return -1;
}
}
diff --git a/src/soc/nvidia/tegra/i2c.h b/src/soc/nvidia/tegra/i2c.h
index 03a6d66e1f..6347fff8ad 100644
--- a/src/soc/nvidia/tegra/i2c.h
+++ b/src/soc/nvidia/tegra/i2c.h
@@ -110,6 +110,19 @@ enum {
0xf << I2C_FIFO_STATUS_RX_FIFO_FULL_CNT_SHIFT
};
+enum {
+ I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD_SHIFT = 16,
+ I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD_MASK =
+ 0x7f << I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD_SHIFT,
+ I2C_BUS_CLEAR_CONFIG_BC_STOP_COND_STOP = 0x1 << 2,
+ I2C_BUS_CLEAR_CONFIG_BC_TERMINATE_THRESHOLD = 0x1 << 1,
+ I2C_BUS_CLEAR_CONFIG_BC_ENABLE = 0x1 << 0,
+
+ I2C_BUS_CLEAR_STATUS_CLEARED = 0x1 << 0,
+
+ I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD_ENABLE = 0x1 << 0
+};
+
struct tegra_i2c_bus_info {
void *base;
uint32_t reset_bit;
@@ -153,8 +166,8 @@ struct tegra_i2c_regs {
uint32_t slv_packet_status;
uint32_t bus_clear_config;
uint32_t bus_clear_status;
- uint32_t spare;
+ uint32_t config_load;
};
-check_member(tegra_i2c_regs, bus_clear_status, 0x88);
+check_member(tegra_i2c_regs, config_load, 0x8C);
#endif /* __SOC_NVIDIA_TEGRA_I2C_H__ */