Commit 275c6a53 authored by Josua Mayer's avatar Josua Mayer

atf: i2c0: recover i2c bus if blocked by slave

The i2c bus may be blocked by anz slave device if the system was reset
during a transaction, leading to ddr initialisation failures.
Implement a workaround releasing the bus by toggling scl 9 times.
Signed-off-by: default avatarJosua Mayer <josua@solid-run.com>
parent 7e9db45d
From 097f2c352af953cdb2515f8ffdff4f384f8c35c0 Mon Sep 17 00:00:00 2001
From: Josua Mayer <josua@solid-run.com>
Date: Thu, 26 May 2022 11:22:53 +0300
Subject: [PATCH] lx2160a: flush i2c bus before initialising ddr
The i2c bus can get locked by a slave device holding sda low, when the
cpu is reset during a transaction.
Implement workaround according to LX2160A Chip Errata 07/2020 A-010650.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
plat/nxp/common/setup/common.mk | 1 +
plat/nxp/common/setup/ls_bl2_el3_setup.c | 5 +
plat/nxp/common/setup/ls_common.c | 1 +
plat/nxp/common/setup/ls_i2c_init.c | 228 ++++++++++++++++++++
plat/nxp/soc-lx2160a/lx2160acex7/ddr_init.c | 1 +
5 files changed, 236 insertions(+)
create mode 100644 plat/nxp/common/setup/ls_i2c_init.c
diff --git a/plat/nxp/common/setup/common.mk b/plat/nxp/common/setup/common.mk
index 7588afcfa..c896842de 100644
--- a/plat/nxp/common/setup/common.mk
+++ b/plat/nxp/common/setup/common.mk
@@ -80,6 +80,7 @@ BL2_SOURCES += drivers/io/io_fip.c \
plat/nxp/common/setup/ls_image_load.c \
plat/nxp/common/setup/ls_io_storage.c \
plat/nxp/common/setup/ls_bl2_el3_setup.c \
+ plat/nxp/common/setup/ls_i2c_init.c \
plat/nxp/common/setup/${ARCH}/ls_bl2_mem_params_desc.c
BL31_SOURCES += plat/nxp/common/setup/ls_bl31_setup.c \
diff --git a/plat/nxp/common/setup/ls_bl2_el3_setup.c b/plat/nxp/common/setup/ls_bl2_el3_setup.c
index cd554766f..49dbec1be 100644
--- a/plat/nxp/common/setup/ls_bl2_el3_setup.c
+++ b/plat/nxp/common/setup/ls_bl2_el3_setup.c
@@ -139,6 +139,8 @@ static uint32_t ls_get_spsr_for_bl33_entry(void)
}
#endif /* AARCH32 */
+extern void bl2_i2c_init();
+
void bl2_el3_early_platform_setup(u_register_t arg0 __unused,
u_register_t arg1 __unused,
u_register_t arg2 __unused,
@@ -160,6 +162,9 @@ void bl2_el3_early_platform_setup(u_register_t arg0 __unused,
if (dram_regions_info.total_dram_size > 0)
populate_dram_regions_info();
+ /* flush i2c buses */
+ bl2_i2c_init();
+
#ifdef NXP_NV_SW_MAINT_LAST_EXEC_DATA
read_nv_app_data();
#if DEBUG
diff --git a/plat/nxp/common/setup/ls_common.c b/plat/nxp/common/setup/ls_common.c
index f8bc99130..6d9b6c9aa 100644
--- a/plat/nxp/common/setup/ls_common.c
+++ b/plat/nxp/common/setup/ls_common.c
@@ -10,6 +10,7 @@
#include <arch.h>
#include <arch_helpers.h>
#include <common/debug.h>
+#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <mmu_def.h>
diff --git a/plat/nxp/common/setup/ls_i2c_init.c b/plat/nxp/common/setup/ls_i2c_init.c
new file mode 100644
index 000000000..ef113126c
--- /dev/null
+++ b/plat/nxp/common/setup/ls_i2c_init.c
@@ -0,0 +1,228 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <i2c.h>
+#include <soc.h>
+
+#define NXP_IIC1_ADDR 0x02000000
+#define NXP_IIC2_ADDR 0x02010000
+#define NXP_IIC3_ADDR 0x02020000
+#define NXP_IIC4_ADDR 0x02030000
+#define NXP_IIC5_ADDR 0x02040000
+#define NXP_IIC6_ADDR 0x02050000
+#define NXP_IIC7_ADDR 0x02060000
+#define NXP_IIC8_ADDR 0x02070000
+
+#define RCWSR12 0x70010012c
+#define RCWSR12_IIC2_PMUX_MASK 0x00000007 // [0..2]
+#define RCWSR12_IIC2_PMUX_IIC2 0x00000000
+#define RCWSR12_IIC2_PMUX_GPIO 0x00000001
+#define RCWSR12_IIC3_PMUX_MASK 0x00000038 // [3..5]
+#define RCWSR12_IIC3_PMUX_IIC3 0x00000000
+#define RCWSR12_IIC3_PMUX_GPIO 0x00000008
+#define RCWSR12_IIC4_PMUX_MASK 0x000001c0 // [6..8]
+#define RCWSR12_IIC4_PMUX_IIC4 0x00000000
+#define RCWSR12_IIC4_PMUX_GPIO 0x00000040
+#define RCWSR12_IIC5_PMUX_MASK 0x00000e00 // [9..11]
+#define RCWSR12_IIC5_PMUX_IIC5 0x00000000
+#define RCWSR12_IIC5_PMUX_GPIO 0x00000200
+#define RCWSR12_IIC6_PMUX_MASK 0x00007000 // [12..14]
+#define RCWSR12_IIC6_PMUX_IIC6 0x00000000
+#define RCWSR12_IIC6_PMUX_GPIO 0x00001000
+#define RCWSR13 0x700100130
+#define RCWSR12_SDHC2_DAT74_PMUX_MASK 0x00000003
+#define RCWSR12_SDHC2_DAT74_PMUX_SDHC2 0x00000000
+#define RCWSR12_SDHC2_DAT74_PMUX_IIC78 0x00000001
+#define RCWSR14 0x700100134
+#define RCWSR14_IIC1_PMUX_MASK 0x00000400 // [10]
+#define RCWSR14_IIC1_PMUX_IIC1 0x00000000
+#define RCWSR14_IIC1_PMUX_GPIO 0x00000400
+
+static void ls_i2c_flush(uint8_t busno, const char *busname);
+
+/**
+ * Flush i2c buses to make slave devices release sda,
+ * in case the system was reset during a transaction.
+ */
+void bl2_i2c_init() {
+ // IIC5
+ i2c_init(NXP_IIC5_ADDR);
+ ls_i2c_flush(4, "IIC5");
+
+ // IIC3
+ i2c_init(NXP_IIC3_ADDR);
+ ls_i2c_flush(2, "IIC3");
+
+ // IIC1
+ i2c_init(NXP_IIC1_ADDR);
+ ls_i2c_flush(0, "IIC1");
+}
+
+static struct i2c_bus_info {
+ uintptr_t pinmux_addr;
+ uint32_t pinmux_mask;
+ uint32_t pinmux_sel;
+ uintptr_t gpio_addr;
+ uint8_t gpio_scl;
+ uint8_t gpio_sda;
+} ls_i2c_bus_info[] = {
+ {
+ .pinmux_addr = RCWSR14,
+ .pinmux_mask = RCWSR14_IIC1_PMUX_MASK, // bit 10 - 0x200000?
+ .pinmux_sel = RCWSR14_IIC1_PMUX_GPIO,
+ .gpio_addr = NXP_GPIO1_ADDR,
+ .gpio_scl = 3, // GPIO1_DAT03
+ .gpio_sda = 2, // GPIO1_DAT02
+ },
+ {
+ .pinmux_addr = RCWSR12,
+ .pinmux_mask = RCWSR12_IIC2_PMUX_MASK,
+ .pinmux_sel = RCWSR12_IIC2_PMUX_GPIO,
+ .gpio_addr = NXP_GPIO1_ADDR,
+ .gpio_scl = 31, // GPIO1_DAT31
+ .gpio_sda = 30, // GPIO1_DAT30
+ },
+ {
+ .pinmux_addr = RCWSR12,
+ .pinmux_mask = RCWSR12_IIC3_PMUX_MASK,
+ .pinmux_sel = RCWSR12_IIC3_PMUX_GPIO,
+ .gpio_addr = NXP_GPIO1_ADDR,
+ .gpio_scl = 29, // GPIO1_DAT29
+ .gpio_sda = 28, // GPIO1_DAT28
+ },
+ {
+ .pinmux_addr = RCWSR12,
+ .pinmux_mask = RCWSR12_IIC4_PMUX_MASK,
+ .pinmux_sel = RCWSR12_IIC4_PMUX_GPIO,
+ .gpio_addr = NXP_GPIO1_ADDR,
+ .gpio_scl = 27, // GPIO1_DAT27
+ .gpio_sda = 26, // GPIO1_DAT26
+ },
+ {
+ .pinmux_addr = RCWSR12,
+ .pinmux_mask = RCWSR12_IIC5_PMUX_MASK,
+ .pinmux_sel = RCWSR12_IIC5_PMUX_GPIO,
+ .gpio_addr = NXP_GPIO1_ADDR,
+ .gpio_scl = 25, // GPIO1_DAT25
+ .gpio_sda = 24, // GPIO1_DAT24
+ },
+ {
+ .pinmux_addr = RCWSR12,
+ .pinmux_mask = RCWSR12_IIC6_PMUX_MASK,
+ .pinmux_sel = RCWSR12_IIC6_PMUX_GPIO,
+ .gpio_addr = NXP_GPIO1_ADDR,
+ .gpio_scl = 23, // GPIO1_DAT23
+ .gpio_sda = 22, // GPIO1_DAT22
+ },
+ {
+ .pinmux_addr = RCWSR13,
+ .pinmux_mask = RCWSR12_SDHC2_DAT74_PMUX_MASK,
+ .pinmux_sel = RCWSR12_SDHC2_DAT74_PMUX_IIC78,
+ .gpio_addr = NXP_GPIO2_ADDR,
+ .gpio_scl = 16, // GPIO2_DAT16
+ .gpio_sda = 15, // GPIO2_DAT15
+ },
+ {
+ .pinmux_addr = RCWSR13,
+ .pinmux_mask = RCWSR12_SDHC2_DAT74_PMUX_MASK,
+ .pinmux_sel = RCWSR12_SDHC2_DAT74_PMUX_IIC78,
+ .gpio_addr = NXP_GPIO2_ADDR,
+ .gpio_scl = 18, // GPIO2_DAT18
+ .gpio_sda = 17, // GPIO2_DAT17
+ },
+};
+
+/*
+ * Flush the i2c bus through any muxes with 9 clock cycles
+ * to ensure all slave devices release their locks on SDA.
+ * This is a work-around for i2c slave devices locking SDA,
+ * when the system has been reset during a transaction.
+ *
+ * The implementation is inspired by LX2160A Chip Errata 07/2020 A-010650.
+ */
+static void ls_i2c_flush(uint8_t busno, const char *busname) {
+ struct i2c_bus_info *info;
+ uintptr_t gpdir_addr, gpodr_addr, gpdat_addr, gpibe_addr;
+ uint32_t pinmux, gpdir, gpodr, gpdat, gpibe;
+ struct {
+ uint32_t pinmux, gpdir, gpodr, gpdat, gpibe;
+ } backup;
+ uint32_t scl_mask, sda_mask;
+
+ if(busno >= 8) {
+ ERROR("failed to flush i2c bus %u %s: invalid bus number!\n", busno, busname);
+ return;
+ }
+ /* load i2c bus specific information */
+ info = &ls_i2c_bus_info[busno];
+ gpdir_addr = info->gpio_addr + 0x0;
+ gpodr_addr = info->gpio_addr + 0x4;
+ gpdat_addr = info->gpio_addr + 0x8;
+ gpibe_addr = info->gpio_addr + 0x18;
+ scl_mask = 0x80000000 >> info->gpio_scl;
+ sda_mask = 0x80000000 >> info->gpio_sda;
+
+ /* backup configuration registers */
+ pinmux = backup.pinmux = mmio_read_32(info->pinmux_addr);
+ gpdir = backup.gpdir = mmio_read_32(gpdir_addr);
+ gpodr = backup.gpodr = mmio_read_32(gpodr_addr);
+ gpdat = backup.gpdat = mmio_read_32(gpdat_addr);
+ gpibe = backup.gpibe = mmio_read_32(gpibe_addr);
+
+ /* configure SCL+SDA as GPIOs */
+ pinmux = (pinmux & ~info->pinmux_mask) | info->pinmux_sel;
+ mmio_write_32(info->pinmux_addr, pinmux);
+
+ /* configure SCL as output open drain + SDA as input open drain */
+ //gpdir = (gpdir | scl_mask) & ~sda_mask;
+ gpdir = (gpdir | scl_mask) & ~sda_mask;
+ gpodr |= scl_mask | sda_mask;
+ gpdat |= scl_mask | sda_mask;
+ gpibe |= sda_mask;
+ mmio_write_32(gpdir_addr, gpdir);
+ mmio_write_32(gpodr_addr, gpodr);
+ mmio_write_32(gpdat_addr, gpdat);
+ mmio_write_32(gpibe_addr, gpibe);
+ udelay(1);
+
+ /*
+ * detect if bus is blocked: sda is expected high while scl is high.
+ * If sda is still low, a slave device is blocking it.
+ */
+ gpdat = mmio_read_32(gpdat_addr);
+ if(gpdat & sda_mask) {
+ // not blocked, nothing to do
+ goto restore;
+ }
+ NOTICE("flushing blocked i2c bus %u (%s)\n", busno, busname);
+
+ /* configure SCL+SDA as output open drain */
+ gpdir |= sda_mask;
+ gpdat |= sda_mask;
+ gpibe &= ~sda_mask;
+ mmio_write_32(gpdir_addr, gpdir);
+ mmio_write_32(gpdat_addr, gpdat);
+ mmio_write_32(gpibe_addr, gpibe);
+
+ /* toggle clock 9 times */
+ for(uint8_t i = 0; i < 9; i++) {
+ mmio_write_32(gpdat_addr, gpdat & ~scl_mask);
+ udelay(10);
+ mmio_write_32(gpdat_addr, gpdat | scl_mask);
+ udelay(10);
+ }
+
+restore:
+ /* restore configuration registers */
+ mmio_write_32(gpibe_addr, backup.gpibe);
+ mmio_write_32(gpdat_addr, backup.gpdat);
+ mmio_write_32(gpodr_addr, backup.gpodr);
+ mmio_write_32(gpdir_addr, backup.gpdir);
+ mmio_write_32(info->pinmux_addr, backup.pinmux);
+
+ return;
+}
diff --git a/plat/nxp/soc-lx2160a/lx2160acex7/ddr_init.c b/plat/nxp/soc-lx2160a/lx2160acex7/ddr_init.c
index eb28216e7..9f1c50449 100644
--- a/plat/nxp/soc-lx2160a/lx2160acex7/ddr_init.c
+++ b/plat/nxp/soc-lx2160a/lx2160acex7/ddr_init.c
@@ -101,6 +101,7 @@ long long init_ddr(void)
}
#endif
+ NOTICE("DDR init\n");
dram_size = dram_init(&info
#if defined(NXP_HAS_CCN504) || defined(NXP_HAS_CCN508)
, NXP_CCN_HN_F_0_ADDR
--
2.35.3
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment