Richard Spiegel has uploaded this change for review. ( https://review.coreboot.org/28571
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: I825b6380fb658c3ea5fc669117ae04d2dc181819 Signed-off-by: Richard Spiegel richard.spiegel@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/71/28571/1
diff --git a/src/soc/amd/stoneyridge/gpio.c b/src/soc/amd/stoneyridge/gpio.c index 88e7d3b..d846315 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..e49db6f 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);