-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
atf: add support for flushing i2c bus before ddr init for reliable spd
- Loading branch information
Showing
3 changed files
with
425 additions
and
0 deletions.
There are no files selected for viewing
365 changes: 365 additions & 0 deletions
365
patches/atf/0009-lx2160a-support-flushing-i2c-bus-before-ddr-init.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,365 @@ | ||
From efa6cca1a194050caa37a16ddccbf5d6e568b42f Mon Sep 17 00:00:00 2001 | ||
From: Josua Mayer <[email protected]> | ||
Date: Fri, 25 Oct 2024 15:36:57 +0200 | ||
Subject: [PATCH 09/11] lx2160a: support flushing i2c bus before ddr init | ||
|
||
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. | ||
|
||
The workaround is inactive by default and must be explicitly enabled | ||
through a board platform.mk file defining buses and muxes to flush: | ||
|
||
LX2160_FLUSH_IIC: 1D array, takes comma separate uint8 indicating | ||
human-readable i2c bus number, | ||
e.g: IIC1 & IIC2 = "1, 2". | ||
|
||
LX2160_FLUSH_IIC_MUX: 2D array, takes comma-separated 1D array | ||
initializer expressions with i2c bus number, mux address on the bus and | ||
a bitmask for channels to flush, | ||
e.g. IIC1 mux @ 77, channels 1&2 = "{1, 0x77, 0x03}". | ||
|
||
Signed-off-by: Josua Mayer <[email protected]> | ||
--- | ||
plat/nxp/common/setup/common.mk | 1 + | ||
plat/nxp/common/setup/include/plat_common.h | 2 + | ||
plat/nxp/common/setup/ls_bl2_el3_setup.c | 1 + | ||
plat/nxp/common/setup/ls_i2c_init.c | 274 ++++++++++++++++++++ | ||
plat/nxp/soc-lx2160a/soc.mk | 6 + | ||
5 files changed, 284 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 1fcf1d093..6333ec515 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/include/plat_common.h b/plat/nxp/common/setup/include/plat_common.h | ||
index 45832fa68..1c850ebe2 100644 | ||
--- a/plat/nxp/common/setup/include/plat_common.h | ||
+++ b/plat/nxp/common/setup/include/plat_common.h | ||
@@ -77,6 +77,8 @@ int open_backend(const uintptr_t spec); | ||
void ls_bl2_plat_arch_setup(void); | ||
void ls_bl2_el3_plat_arch_setup(void); | ||
|
||
+void bl2_i2c_init(void); | ||
+ | ||
enum boot_device { | ||
BOOT_DEVICE_IFC_NOR, | ||
BOOT_DEVICE_IFC_NAND, | ||
diff --git a/plat/nxp/common/setup/ls_bl2_el3_setup.c b/plat/nxp/common/setup/ls_bl2_el3_setup.c | ||
index a4cbaef45..63e3460d7 100644 | ||
--- a/plat/nxp/common/setup/ls_bl2_el3_setup.c | ||
+++ b/plat/nxp/common/setup/ls_bl2_el3_setup.c | ||
@@ -276,6 +276,7 @@ void bl2_el3_plat_prepare_exit(void) | ||
*/ | ||
void bl2_plat_preload_setup(void) | ||
{ | ||
+ bl2_i2c_init(); | ||
|
||
soc_preload_setup(); | ||
|
||
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..5392b160d | ||
--- /dev/null | ||
+++ b/plat/nxp/common/setup/ls_i2c_init.c | ||
@@ -0,0 +1,274 @@ | ||
+#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 0x01e0012c | ||
+#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 0x01e00130 | ||
+#define RCWSR12_SDHC2_DAT74_PMUX_MASK 0x00000003 | ||
+#define RCWSR12_SDHC2_DAT74_PMUX_SDHC2 0x00000000 | ||
+#define RCWSR12_SDHC2_DAT74_PMUX_IIC78 0x00000001 | ||
+#define RCWSR14 0x01e00134 | ||
+#define RCWSR14_IIC1_PMUX_MASK 0x00000400 /* [10] */ | ||
+#define RCWSR14_IIC1_PMUX_IIC1 0x00000000 | ||
+#define RCWSR14_IIC1_PMUX_GPIO 0x00000400 | ||
+ | ||
+static void ls_i2c_flush_pca9547(uint8_t busno, const char *busname, uint8_t address, uint8_t channels); | ||
+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() { | ||
+ const uint8_t bus_flush_list[] = {CONFIG_LX2160_FLUSH_IIC}; | ||
+ /* | ||
+ * List of muxes and channels to flush. Takes 2D array: | ||
+ * {<bus number>, <mux chip address>, <channel bitmask>}, | ||
+ */ | ||
+ const uint8_t mux_flush_list[][3] = {CONFIG_LX2160_FLUSH_IIC_MUX}; | ||
+ const uintptr_t iic_base_addr[] = { | ||
+ NXP_IIC1_ADDR, | ||
+ NXP_IIC2_ADDR, | ||
+ NXP_IIC3_ADDR, | ||
+ NXP_IIC4_ADDR, | ||
+ NXP_IIC5_ADDR, | ||
+ NXP_IIC6_ADDR, | ||
+ }; | ||
+ const char *iic_name[] = { | ||
+ "IIC1", | ||
+ "IIC2", | ||
+ "IIC3", | ||
+ "IIC4", | ||
+ "IIC5", | ||
+ "IIC6", | ||
+ }; | ||
+ int i; | ||
+ uint8_t busno; | ||
+ uint8_t address; | ||
+ uint8_t channels; | ||
+ | ||
+ /* flush i2c buses */ | ||
+ for (i = 0; i < ARRAY_SIZE(bus_flush_list); i++) { | ||
+ busno = bus_flush_list[i] - 1; | ||
+ i2c_init(iic_base_addr[busno]); | ||
+ ls_i2c_flush(busno, iic_name[busno]); | ||
+ } | ||
+ | ||
+ /* flush muxes channels */ | ||
+ for (i = 0; i < ARRAY_SIZE(mux_flush_list); i++) { | ||
+ busno = mux_flush_list[i][0] - 1; | ||
+ address = mux_flush_list[i][1]; | ||
+ channels = mux_flush_list[i][2]; | ||
+ i2c_init(iic_base_addr[busno]); | ||
+ ls_i2c_flush_pca9547(busno, iic_name[busno], address, channels); | ||
+ } | ||
+} | ||
+ | ||
+static void ls_i2c_flush_pca9547(uint8_t busno, const char *busname, uint8_t chip, uint8_t channels) { | ||
+ uint8_t channel, creg = 0; | ||
+ char buffer[64]; | ||
+ int ret; | ||
+ | ||
+ /* try read configuration register */ | ||
+ ret = i2c_read(chip, 0x00, 1, &creg, 1); | ||
+ if(ret != 0) { | ||
+ /* no device responding at address, skip */ | ||
+ return; | ||
+ } | ||
+ | ||
+ /* after reset configuration register reads 0x08 */ | ||
+ if(creg != 0x08) { | ||
+ /* probably not a pca9547, skip */ | ||
+ return; | ||
+ } | ||
+ | ||
+ /* flush selected channels */ | ||
+ for(uint8_t i = 8; i > 0; i--) { | ||
+ if (!(channels & (1 << (i-1)))) | ||
+ continue; | ||
+ | ||
+ /* select channel i */ | ||
+ channel = 0x08 | (i-1); | ||
+ i2c_write(chip, 0x00, 1, &channel, 1); | ||
+ | ||
+ /* flush channel */ | ||
+ snprintf(buffer, sizeof(buffer), "%s mux@%02x channel %u", busname, chip, i-1); | ||
+ ls_i2c_flush(busno, buffer); | ||
+ } | ||
+} | ||
+ | ||
+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, | ||
+ .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; | ||
+ uint32_t pinmux, gpdir, gpodr, gpdat; | ||
+ struct { | ||
+ uint32_t pinmux, gpdir, gpodr, gpdat; | ||
+ } 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; | ||
+ 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); | ||
+ | ||
+ /* configure SCL+SDA as GPIOs */ | ||
+ pinmux = (pinmux & ~info->pinmux_mask) | info->pinmux_sel; | ||
+ mmio_write_32(info->pinmux_addr, pinmux); | ||
+ | ||
+ /* configure SCL+SDA as output open drain */ | ||
+ gpdir |= scl_mask | sda_mask; | ||
+ gpodr |= scl_mask | sda_mask; | ||
+ gpdat |= scl_mask | sda_mask; | ||
+ mmio_write_32(gpdir_addr, gpdir); | ||
+ mmio_write_32(gpodr_addr, gpodr); | ||
+ mmio_write_32(gpdat_addr, gpdat); | ||
+ | ||
+ /* | ||
+ * reliable detection of blocked bus is hard | ||
+ * because sda depends on the last sent bit. | ||
+ * Flush unconditionally instead. | ||
+ */ | ||
+ | ||
+ VERBOSE("flushing i2c bus %u (%s)\n", busno, busname); | ||
+ | ||
+ /* 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 configuration registers */ | ||
+ 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/soc.mk b/plat/nxp/soc-lx2160a/soc.mk | ||
index 20e64753c..fa8c63251 100644 | ||
--- a/plat/nxp/soc-lx2160a/soc.mk | ||
+++ b/plat/nxp/soc-lx2160a/soc.mk | ||
@@ -185,3 +185,9 @@ ifneq (${LX2160A_S5_GPIO_ADDR},0) | ||
$(eval $(call add_define_val,CONFIG_LX2160A_S5_GPIO_ADDR,$(LX2160A_S5_GPIO_ADDR))) | ||
$(eval $(call add_define_val,CONFIG_LX2160A_S5_GPIO,$(LX2160A_S5_GPIO))) | ||
endif | ||
+ | ||
+# I2C Bus Flushing (optional) | ||
+LX2160_FLUSH_IIC ?= "" | ||
+LX2160_FLUSH_IIC_MUX ?= "" | ||
+$(eval $(call add_define_val,CONFIG_LX2160_FLUSH_IIC,"$(LX2160_FLUSH_IIC)")) | ||
+$(eval $(call add_define_val,CONFIG_LX2160_FLUSH_IIC_MUX,"$(LX2160_FLUSH_IIC_MUX)")) | ||
-- | ||
2.43.0 | ||
|
30 changes: 30 additions & 0 deletions
30
patches/atf/0010-lx2160acex6-flush-i2c-bus-with-spd-eeprom-before-ddr.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
From 668924da1e8df2036dc05022616eb7d15a8c8727 Mon Sep 17 00:00:00 2001 | ||
From: Josua Mayer <[email protected]> | ||
Date: Fri, 25 Oct 2024 16:43:59 +0200 | ||
Subject: [PATCH 10/11] lx2160acex6: flush i2c bus with spd eeprom before ddr | ||
init | ||
|
||
Signed-off-by: Josua Mayer <[email protected]> | ||
--- | ||
plat/nxp/soc-lx2160a/lx2160acex6/platform.mk | 5 +++++ | ||
1 file changed, 5 insertions(+) | ||
|
||
diff --git a/plat/nxp/soc-lx2160a/lx2160acex6/platform.mk b/plat/nxp/soc-lx2160a/lx2160acex6/platform.mk | ||
index 1165cfa09..950207878 100644 | ||
--- a/plat/nxp/soc-lx2160a/lx2160acex6/platform.mk | ||
+++ b/plat/nxp/soc-lx2160a/lx2160acex6/platform.mk | ||
@@ -29,6 +29,11 @@ CONFIG_DDR_NODIMM := 0 | ||
LX2160A_S5_GPIO_ADDR := NXP_GPIO3_ADDR | ||
LX2160A_S5_GPIO := 0 | ||
|
||
+# I2C Bus Flushing: IIC1 | ||
+LX2160_FLUSH_IIC := 1, | ||
+# I2C Mux Flushing: IIC1: PCA9547@77: Channel 0 (SPD EEPROM) | ||
+LX2160_FLUSH_IIC_MUX := { 1, 0x77, 0x01 }, | ||
+ | ||
# DDR Errata | ||
ERRATA_DDR_A011396 := 1 | ||
ERRATA_DDR_A050450 := 1 | ||
-- | ||
2.43.0 | ||
|
Oops, something went wrong.