[coreboot-gerrit] Change in coreboot[master]: soc/amd/stoneyridge/gpio.c: Create I2C slave reset code

Richard Spiegel (Code Review) gerrit at coreboot.org
Tue Sep 11 20:38:38 CEST 2018


Richard Spiegel has uploaded this change for review. ( https://review.coreboot.org/28574


Change subject: soc/amd/stoneyridge/gpio.c: Create I2C slave reset code
......................................................................

soc/amd/stoneyridge/gpio.c: Create I2C slave reset code

AMD soc does not wait for I2C transaction to complete before executing a
reset. Because of this, it's possible for the reset to happen in the middle
of a transaction, resulting on a slave hang. There are 2 possible solutions:
If the slave has a reset pin connected to a GPIO pin, it can be used to
reset the slave, else the only solution is to bang SCL 9 times. Create code
that makes it easy to implement either solution.

BUG=b:114479395
TEST=Build and boot grunt.

Change-Id: I7f74b7e45c509044825355874753969f074e2382
Signed-off-by: Richard Spiegel <richard.spiegel at silverbackltd.com>
---
M src/soc/amd/stoneyridge/gpio.c
M src/soc/amd/stoneyridge/include/soc/gpio.h
2 files changed, 108 insertions(+), 0 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/74/28574/1

diff --git a/src/soc/amd/stoneyridge/gpio.c b/src/soc/amd/stoneyridge/gpio.c
index 88e7d3b..1da7ab4 100644
--- a/src/soc/amd/stoneyridge/gpio.c
+++ b/src/soc/amd/stoneyridge/gpio.c
@@ -22,6 +22,7 @@
 #include <soc/southbridge.h>
 #include <assert.h>
 #include <compiler.h>
+#include <delay.h>
 
 static const struct soc_amd_event gpio_event_table[] = {
 	{ GPIO_1, GEVENT_19 },
@@ -297,6 +298,85 @@
 					edge_level, mask);
 }
 
+static const struct soc_amd_gpio gpo_i2c_2_gpo[] = {
+	PAD_GPO(GPIO_19, HIGH),
+	PAD_GPI(GPIO_20, PULL_UP),
+	PAD_GPO(GPIO_113, HIGH),
+	PAD_GPI(GPIO_114, PULL_UP),
+	PAD_GPO(GPIO_145, HIGH),
+	PAD_GPI(GPIO_146, PULL_UP),
+	PAD_GPO(GPIO_147, HIGH),
+	PAD_GPI(GPIO_148, PULL_UP),
+};
+
+static void save_i2c_pin_registers(uint8_t gpio,
+					struct soc_amd_i2c_save *save_table)
+{
+	uint32_t *gpio_ptr;
+	uint8_t *mux_ptr;
+
+	mux_ptr = (uint8_t *)(uintptr_t)(gpio + AMD_GPIO_MUX);
+	gpio_ptr = (uint32_t *)gpio_get_address(gpio);
+	save_table->mux_pointer = mux_ptr;
+	save_table->mux_value = read8(mux_ptr);
+	save_table->control_pointer = gpio_ptr;
+	save_table->control_value = read32(gpio_ptr);
+}
+
+void sb_reset_i2c_slaves(const struct soc_amd_force_toggle *pin_table,
+			size_t size)
+{
+	struct soc_amd_i2c_save save_table[8];
+	struct soc_and_pointer_count control_table[8];
+	uint32_t reg32, *reg_ptr;
+	uint8_t i, j, remain;
+
+	for (i = 0; i < 8; i++) {
+		save_i2c_pin_registers(gpo_i2c_2_gpo[i].gpio, &save_table[i]);
+		control_table[i].ptr = NULL;
+	}
+	sb_program_gpios(gpo_i2c_2_gpo, ARRAY_SIZE(gpo_i2c_2_gpo));
+
+	if (size > 8) {
+		printk(BIOS_WARNING, "Maximum reset size is 8 elements\n");
+		j = 8;
+	} else
+		j = size;
+	for (i = 0; i < j; i++) {
+		reg_ptr = (uint32_t *)gpio_get_address(pin_table[i].gpio);
+		control_table[i].ptr = reg_ptr;
+		control_table[i].count = pin_table[i].count;
+	}
+
+	remain = 1; /* at least 1 pin needs to be toggled */
+	while (remain) {
+		for (i = 0; i < j; i++) {
+			if (control_table[i].count) {
+				reg32 = read32(control_table[i].ptr);
+				reg32 ^= GPIO_OUTPUT_VALUE;
+				write32(control_table[i].ptr, reg32);
+			}
+		}
+		udelay(5);
+		remain = 0;
+		for (i = 0; i < j; i++) {
+			if (control_table[i].count) {
+				reg32 = read32(control_table[i].ptr);
+				reg32 ^= GPIO_OUTPUT_VALUE;
+				write32(control_table[i].ptr, reg32);
+				control_table[i].count--;
+			}
+			remain += control_table[i].count;
+		}
+		udelay(5);
+	}
+
+	for (i = 0; i < 8; i++) {
+		write8(save_table->mux_pointer, save_table->mux_value);
+		write32(save_table->control_pointer, save_table->control_value);
+	}
+}
+
 int gpio_interrupt_status(gpio_t gpio)
 {
 	uintptr_t gpio_address = gpio_get_address(gpio);
diff --git a/src/soc/amd/stoneyridge/include/soc/gpio.h b/src/soc/amd/stoneyridge/include/soc/gpio.h
index 6e72293..d4383f8 100644
--- a/src/soc/amd/stoneyridge/include/soc/gpio.h
+++ b/src/soc/amd/stoneyridge/include/soc/gpio.h
@@ -36,6 +36,23 @@
 	uint8_t event;
 };
 
+struct soc_amd_i2c_save {
+	uint32_t *control_pointer;
+	uint32_t control_value;
+	uint8_t *mux_pointer;
+	uint8_t mux_value;
+};
+
+struct soc_and_pointer_count {
+	uint32_t *ptr;
+	uint8_t count;
+};
+
+struct soc_amd_force_toggle {
+	uint8_t gpio;
+	uint8_t count;
+};
+
 #define GPIO_TOTAL_PINS		149
 #define GPIO_PIN_IN		(1 << 0)	/* for byte access */
 #define GPIO_PIN_OUT		(1 << 6)	/* for byte access */
@@ -558,6 +575,15 @@
 		.control = (GPIO_IN  ## _ ## type | GPIO_IN  ## _ ## time), \
 		.flags = GPIO_FLAG_DEBOUNCE }
 
+/* Use these to define GPIO to be toggled, to solve I2C reset issues */
+#define PAD_RESET_I2C_SLAVE(pin) \
+	{ .gpio = (pin), \
+		.count = 9 }
+
+#define PAD_RESET_DEVICE(pin) \
+	{ .gpio = (pin), \
+		.count = 1 }
+
 typedef uint32_t gpio_t;
 /* Get the address of the control register of a particular pin */
 uintptr_t gpio_get_address(gpio_t gpio_num);
@@ -571,6 +597,8 @@
  * @return none
  */
 void sb_program_gpios(const struct soc_amd_gpio *gpio_list_ptr, size_t size);
+void sb_reset_i2c_slaves(const struct soc_amd_force_toggle *pin_table,
+			size_t size);
 
 /* Return the interrupt status and clear if set. */
 int gpio_interrupt_status(gpio_t gpio);

-- 
To view, visit https://review.coreboot.org/28574
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7f74b7e45c509044825355874753969f074e2382
Gerrit-Change-Number: 28574
Gerrit-PatchSet: 1
Gerrit-Owner: Richard Spiegel <richard.spiegel at silverbackltd.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180911/6cdc15f4/attachment-0001.html>


More information about the coreboot-gerrit mailing list