<p>Richard Spiegel has uploaded this change for <strong>review</strong>.</p><p><a href="https://review.coreboot.org/28571">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">soc/amd/stoneyridge/gpio.c: Create I2C slave reset code<br><br>AMD soc does not wait for I2C transaction to complete before executing a<br>reset. Because of this, it's possible for the reset to happen in the middle<br>of a transaction, resulting on a slave hang. There are 2 possible solutions:<br>If the slave has a reset pin connected to a GPIO pin, it can be used to<br>reset the slave, else the only solution is to bang SCL 9 times. Create code<br>that makes it easy to implement either solution.<br><br>BUG=b:114479395<br>TEST=Build and boot grunt.<br><br>Change-Id: I825b6380fb658c3ea5fc669117ae04d2dc181819<br>Signed-off-by: Richard Spiegel <richard.spiegel@silverbackltd.com><br>---<br>M src/soc/amd/stoneyridge/gpio.c<br>M src/soc/amd/stoneyridge/include/soc/gpio.h<br>2 files changed, 108 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://review.coreboot.org:29418/coreboot refs/changes/71/28571/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/soc/amd/stoneyridge/gpio.c b/src/soc/amd/stoneyridge/gpio.c</span><br><span>index 88e7d3b..d846315 100644</span><br><span>--- a/src/soc/amd/stoneyridge/gpio.c</span><br><span>+++ b/src/soc/amd/stoneyridge/gpio.c</span><br><span>@@ -22,6 +22,7 @@</span><br><span> #include <soc/southbridge.h></span><br><span> #include <assert.h></span><br><span> #include <compiler.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <delay.h></span><br><span> </span><br><span> static const struct soc_amd_event gpio_event_table[] = {</span><br><span>    { GPIO_1, GEVENT_19 },</span><br><span>@@ -297,6 +298,85 @@</span><br><span>                                        edge_level, mask);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static const struct soc_amd_gpio gpo_i2c_2_gpo[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     PAD_GPO(GPIO_19, HIGH),</span><br><span style="color: hsl(120, 100%, 40%);">+       PAD_GPI(GPIO_20, PULL_UP),</span><br><span style="color: hsl(120, 100%, 40%);">+    PAD_GPO(GPIO_113, HIGH),</span><br><span style="color: hsl(120, 100%, 40%);">+      PAD_GPI(GPIO_114, PULL_UP),</span><br><span style="color: hsl(120, 100%, 40%);">+   PAD_GPO(GPIO_145, HIGH),</span><br><span style="color: hsl(120, 100%, 40%);">+      PAD_GPI(GPIO_146, PULL_UP),</span><br><span style="color: hsl(120, 100%, 40%);">+   PAD_GPO(GPIO_147, HIGH),</span><br><span style="color: hsl(120, 100%, 40%);">+      PAD_GPI(GPIO_148, PULL_UP),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void save_i2c_pin_registers(uint8_t gpio,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   struct soc_amd_i2c_save *save_table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint32_t *gpio_ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t *mux_ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mux_ptr = (uint8_t *)(uintptr_t)(gpio + AMD_GPIO_MUX);</span><br><span style="color: hsl(120, 100%, 40%);">+        gpio_ptr = (uint32_t *)gpio_get_address(gpio);</span><br><span style="color: hsl(120, 100%, 40%);">+        save_table->mux_pointer = mux_ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+ save_table->mux_value = read8(mux_ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+    save_table->control_pointer = gpio_ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+    save_table->control_value = read32(gpio_ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sb_reset_i2c_slaves(const struct soc_amd_force_toggle *pin_table,</span><br><span style="color: hsl(120, 100%, 40%);">+                 size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct soc_amd_i2c_save save_table[8];</span><br><span style="color: hsl(120, 100%, 40%);">+        struct soc_and_pointer_count control_table[8];</span><br><span style="color: hsl(120, 100%, 40%);">+        uint32_t reg32, *reg_ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t i, j, remain;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       for (i = 0; i < 8; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          save_i2c_pin_registers(gpo_i2c_2_gpo[i].gpio, &save_table[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+            control_table[i].ptr = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     sb_program_gpios(gpo_i2c_2_gpo, ARRAY_SIZE(gpo_i2c_2_gpo));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (size > 8) {</span><br><span style="color: hsl(120, 100%, 40%);">+            printk(BIOS_WARNING, "Maximum reset size is 8 elements\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         j = 8;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else</span><br><span style="color: hsl(120, 100%, 40%);">+                j = size;</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = 0; i < j; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          reg_ptr = (uint32_t *)gpio_get_address(pin_table[i].gpio);</span><br><span style="color: hsl(120, 100%, 40%);">+            control_table[i].ptr = reg_ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+               control_table[i].count = pin_table[i].count;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   remain = 1; /* at least 1 pin needs to be toggled */</span><br><span style="color: hsl(120, 100%, 40%);">+  while(remain) {</span><br><span style="color: hsl(120, 100%, 40%);">+               for (i = 0; i < j; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (control_table[i].count) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         reg32 = read32(control_table[i].ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+                         reg32 ^= GPIO_OUTPUT_VALUE;</span><br><span style="color: hsl(120, 100%, 40%);">+                           write32(control_table[i].ptr, reg32);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             udelay(5);</span><br><span style="color: hsl(120, 100%, 40%);">+            remain = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           for (i = 0; i < j; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (control_table[i].count) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         reg32 = read32(control_table[i].ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+                         reg32 ^= GPIO_OUTPUT_VALUE;</span><br><span style="color: hsl(120, 100%, 40%);">+                           write32(control_table[i].ptr, reg32);</span><br><span style="color: hsl(120, 100%, 40%);">+                         control_table[i].count--;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     remain += control_table[i].count;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             udelay(5);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < 8; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          write8(save_table->mux_pointer, save_table->mux_value);</span><br><span style="color: hsl(120, 100%, 40%);">+         write32(save_table->control_pointer, save_table->control_value);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int gpio_interrupt_status(gpio_t gpio)</span><br><span> {</span><br><span>        uintptr_t gpio_address = gpio_get_address(gpio);</span><br><span>diff --git a/src/soc/amd/stoneyridge/include/soc/gpio.h b/src/soc/amd/stoneyridge/include/soc/gpio.h</span><br><span>index 6e72293..e49db6f 100644</span><br><span>--- a/src/soc/amd/stoneyridge/include/soc/gpio.h</span><br><span>+++ b/src/soc/amd/stoneyridge/include/soc/gpio.h</span><br><span>@@ -36,6 +36,23 @@</span><br><span>   uint8_t event;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct soc_amd_i2c_save{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint32_t *control_pointer;</span><br><span style="color: hsl(120, 100%, 40%);">+    uint32_t control_value;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t *mux_pointer;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t mux_value;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct soc_and_pointer_count {</span><br><span style="color: hsl(120, 100%, 40%);">+      uint32_t *ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t count;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct soc_amd_force_toggle {</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t gpio;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t count;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define GPIO_TOTAL_PINS         149</span><br><span> #define GPIO_PIN_IN              (1 << 0)  /* for byte access */</span><br><span> #define GPIO_PIN_OUT           (1 << 6)  /* for byte access */</span><br><span>@@ -558,6 +575,15 @@</span><br><span>                 .control = (GPIO_IN  ## _ ## type | GPIO_IN  ## _ ## time), \</span><br><span>                .flags = GPIO_FLAG_DEBOUNCE }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Use these to define GPIO to be toggled, to solve I2C reset issues */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PAD_RESET_I2C_SLAVE(pin) \</span><br><span style="color: hsl(120, 100%, 40%);">+        { .gpio = (pin), \</span><br><span style="color: hsl(120, 100%, 40%);">+            .count = 9 }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define PAD_RESET_DEVICE(pin) \</span><br><span style="color: hsl(120, 100%, 40%);">+       { .gpio = (pin), \</span><br><span style="color: hsl(120, 100%, 40%);">+            .count = 1 }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> typedef uint32_t gpio_t;</span><br><span> /* Get the address of the control register of a particular pin */</span><br><span> uintptr_t gpio_get_address(gpio_t gpio_num);</span><br><span>@@ -571,6 +597,8 @@</span><br><span>  * @return none</span><br><span>  */</span><br><span> void sb_program_gpios(const struct soc_amd_gpio *gpio_list_ptr, size_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+void sb_reset_i2c_slaves(const struct soc_amd_force_toggle *pin_table,</span><br><span style="color: hsl(120, 100%, 40%);">+                      size_t size);</span><br><span> </span><br><span> /* Return the interrupt status and clear if set. */</span><br><span> int gpio_interrupt_status(gpio_t gpio);</span><br><span></span><br></pre><p>To view, visit <a href="https://review.coreboot.org/28571">change 28571</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://review.coreboot.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://review.coreboot.org/28571"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: coreboot </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I825b6380fb658c3ea5fc669117ae04d2dc181819 </div>
<div style="display:none"> Gerrit-Change-Number: 28571 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Richard Spiegel <richard.spiegel@silverbackltd.com> </div>