<p>Patrick Rudolph has uploaded this change for <strong>review</strong>.</p><p><a href="https://review.coreboot.org/25105">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">[WIP]drivers/spi: Winbond specific writeprotection enable<br><br>Extend the SPI interface to enable write protection.<br><br>Change-Id: Ie3765b013855538eca37bc7800d3f9d5d09b8402<br>Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com><br>---<br>M src/drivers/spi/spi_flash.c<br>M src/drivers/spi/winbond.c<br>M src/include/spi_flash.h<br>3 files changed, 196 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://review.coreboot.org:29418/coreboot refs/changes/05/25105/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c</span><br><span>index 9bad9f3..769ef48 100644</span><br><span>--- a/src/drivers/spi/spi_flash.c</span><br><span>+++ b/src/drivers/spi/spi_flash.c</span><br><span>@@ -452,6 +452,28 @@</span><br><span>      return flash->ops->get_write_protection(flash, region);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_set_write_protected(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+                                const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct region flash_region = { 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!flash)</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  flash_region.size = flash->size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!region_is_subregion(&flash_region, region))</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (flash->ops->set_write_protection) {</span><br><span style="color: hsl(120, 100%, 40%);">+         printk(BIOS_WARNING, "SPI: Setting write-protection is not "</span><br><span style="color: hsl(120, 100%, 40%);">+                       "implemented for this vendor.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</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%);">+   return flash->ops->set_write_protection(flash, region);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static uint32_t volatile_group_count CAR_GLOBAL;</span><br><span> </span><br><span> int spi_flash_volatile_group_begin(const struct spi_flash *flash)</span><br><span>diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c</span><br><span>index 6c1ce0d..2992dc3 100644</span><br><span>--- a/src/drivers/spi/winbond.c</span><br><span>+++ b/src/drivers/spi/winbond.c</span><br><span>@@ -31,6 +31,8 @@</span><br><span> #define CMD_W25_DP              0xb9    /* Deep Power-down */</span><br><span> #define CMD_W25_RES            0xab    /* Release from DP, and Read Signature */</span><br><span> #define CMD_W25_B_LOCK_STATUS      0x3d    /* Read Block/Sector Lock bit */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_B_LOCK_SET    0x36    /* Set Block/Sector Lock bit */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_B_LOCK_CLEAR   0x39    /* Clear Block/Sector Lock bit */</span><br><span> </span><br><span> struct winbond_spi_flash_params {</span><br><span>   uint16_t        id;</span><br><span>@@ -319,9 +321,157 @@</span><br><span>                  return ret;</span><br><span>  }</span><br><span>    if (reg & STS_W25_WPS)</span><br><span style="color: hsl(0, 100%, 40%);">-              return winbond_read_b_lock_protection(flash, region);</span><br><span style="color: hsl(120, 100%, 40%);">+         return winbond_get_b_lock_protection(flash, region);</span><br><span>         else</span><br><span style="color: hsl(0, 100%, 40%);">-            return winbond_read_b_protect_protection(flash, region);</span><br><span style="color: hsl(120, 100%, 40%);">+              return winbond_get_b_protect_protection(flash, region);</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 style="color: hsl(120, 100%, 40%);">+ * Only available on some devices.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Set Block Protection Bits of each block.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+ * -1    on error</span><br><span style="color: hsl(120, 100%, 40%);">+ *  0    on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int winbond_set_b_lock_protection(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int ret, i;</span><br><span style="color: hsl(120, 100%, 40%);">+   u8 cmd[5], reg8;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    cmd[4] = 0x00;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Writeable */</span><br><span style="color: hsl(120, 100%, 40%);">+       cmd[0] = CMD_W25_B_LOCK_CLEAR;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < region->offset; i += flash->sector_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+           cmd[1] = (i >> 16) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+          cmd[2] = (i >> 8) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+           cmd[3] = i & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd),</span><br><span style="color: hsl(120, 100%, 40%);">+                                  &reg8, sizeof(reg8));</span><br><span style="color: hsl(120, 100%, 40%);">+            if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+                      return ret;</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%);">+   /* Readonly */</span><br><span style="color: hsl(120, 100%, 40%);">+        cmd[0] = CMD_W25_B_LOCK_SET;</span><br><span style="color: hsl(120, 100%, 40%);">+  for (i = 0; i < region->size; i += flash->sector_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+             cmd[1] = ((region->offset + i) >> 16) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd[2] = ((region->offset + i) >> 8) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+             cmd[3] = (region->offset + i) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd),</span><br><span style="color: hsl(120, 100%, 40%);">+                                  &reg8, sizeof(reg8));</span><br><span style="color: hsl(120, 100%, 40%);">+            if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+                      return ret;</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%);">+   /* Writeable */</span><br><span style="color: hsl(120, 100%, 40%);">+       cmd[0] = CMD_W25_B_LOCK_CLEAR;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = region->offset + region->size; i < flash->size;</span><br><span style="color: hsl(120, 100%, 40%);">+       i += flash->sector_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd[1] = (i >> 16) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+          cmd[2] = (i >> 8) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+           cmd[3] = i & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd),</span><br><span style="color: hsl(120, 100%, 40%);">+                                  &reg8, sizeof(reg8));</span><br><span style="color: hsl(120, 100%, 40%);">+            if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+                      return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 1;</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 style="color: hsl(120, 100%, 40%);">+ * Available on all devices.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Write block protect bits to Status/Status2 Reg.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+ * -1    on error</span><br><span style="color: hsl(120, 100%, 40%);">+ *  0    on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int winbond_set_b_protect_protection(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int ret, shift, block;</span><br><span style="color: hsl(120, 100%, 40%);">+        union {</span><br><span style="color: hsl(120, 100%, 40%);">+               u8 u;</span><br><span style="color: hsl(120, 100%, 40%);">+         struct {</span><br><span style="color: hsl(120, 100%, 40%);">+                      u8 busy : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  u8 wel  : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  u8 bp   : 3;</span><br><span style="color: hsl(120, 100%, 40%);">+                  u8 tb   : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  u8 sec  : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  u8 srp0 : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+          } s;</span><br><span style="color: hsl(120, 100%, 40%);">+  } reg1 = {.u = 0};</span><br><span style="color: hsl(120, 100%, 40%);">+    union {</span><br><span style="color: hsl(120, 100%, 40%);">+               u8 u;</span><br><span style="color: hsl(120, 100%, 40%);">+         struct {</span><br><span style="color: hsl(120, 100%, 40%);">+                      u8 : 6;</span><br><span style="color: hsl(120, 100%, 40%);">+                       u8 cmp  : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  u8 : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+               } s;</span><br><span style="color: hsl(120, 100%, 40%);">+  } reg2 = {.u = 0};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Need to touch TOP or BOTTOM */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (region->offset == 0 ||</span><br><span style="color: hsl(120, 100%, 40%);">+     region->offset + region->size == flash->size)</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  reg1.s.tb = region->offset == 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ reg1.s.sec = region->size < flash->block_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (reg1.s.sec && (region->size > flash->block_size / 2))</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (reg1.s.sec)</span><br><span style="color: hsl(120, 100%, 40%);">+               block = flash->sector_size;</span><br><span style="color: hsl(120, 100%, 40%);">+        else</span><br><span style="color: hsl(120, 100%, 40%);">+          block = max(flash->block_size, flash->size / 64);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (region->size > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         for (shift = 0; shift <= 0x7; shift++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (region->size == block << shift)</span><br><span style="color: hsl(120, 100%, 40%);">+                          break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (shift > 0x7)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            reg1.s.bp = shift;</span><br><span style="color: hsl(120, 100%, 40%);">+    } else</span><br><span style="color: hsl(120, 100%, 40%);">+                reg1.s.bp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ret = spi_flash_cmd(&flash->spi, CMD_W25_WRSR, &reg1.u, sizeof(reg1.u));</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+              return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return spi_flash_cmd(&flash->spi, CMD_W25_WRSR2, &reg2.u,</span><br><span style="color: hsl(120, 100%, 40%);">+                              sizeof(reg2.u));</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 int winbond_set_write_protection(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       u8 reg = STS_W25_WPS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Check alignment */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (region->offset & (flash->sector_size - 1))</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (region->size & (flash->sector_size - 1))</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (IS_ENABLED(CONFIG_SPI_FLASH_HAS_BLOCK_LOCK_BITS)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               int ret = spi_flash_cmd(&flash->spi, CMD_W25_WRSR3, &reg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  sizeof(reg));</span><br><span style="color: hsl(120, 100%, 40%);">+         if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+                      return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+           return winbond_set_b_lock_protection(flash, region);</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%);">+   return winbond_set_b_protect_protection(flash, region);</span><br><span> }</span><br><span> </span><br><span> static const struct spi_flash_ops spi_flash_ops = {</span><br><span>@@ -334,6 +484,7 @@</span><br><span>        .read = spi_flash_cmd_read_fast,</span><br><span> #endif</span><br><span>   .get_write_protection = winbond_get_write_protection,</span><br><span style="color: hsl(120, 100%, 40%);">+ .set_write_protection = winbond_set_write_protection,</span><br><span> };</span><br><span> </span><br><span> int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode,</span><br><span>diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h</span><br><span>index 82705d4..d1dfa92 100644</span><br><span>--- a/src/include/spi_flash.h</span><br><span>+++ b/src/include/spi_flash.h</span><br><span>@@ -42,7 +42,8 @@</span><br><span>         int (*status)(const struct spi_flash *flash, u8 *reg);</span><br><span>       int (*get_write_protection)(const struct spi_flash *flash,</span><br><span>                               const struct region *region);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+     int (*set_write_protection)(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+                                const struct region *region);</span><br><span> };</span><br><span> </span><br><span> struct spi_flash {</span><br><span>@@ -113,6 +114,25 @@</span><br><span> int spi_flash_is_write_protected(const struct spi_flash *flash,</span><br><span>                           const struct region *region);</span><br><span> /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Enable the vendor dependent SPI flash write protection. The region not</span><br><span style="color: hsl(120, 100%, 40%);">+ * covered by write-protection will be set to write-able state.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Only a single write-protected region is supported.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Some flash ICs require the region to be aligned in the block size, sector</span><br><span style="color: hsl(120, 100%, 40%);">+ * size or page size.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Some flash ICs require the region to start at TOP or BOTTOM.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * @param flash : A SPI flash device</span><br><span style="color: hsl(120, 100%, 40%);">+ * @param region: A subregion of the device's region</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+ *  -1   on error</span><br><span style="color: hsl(120, 100%, 40%);">+ *  -1   if the region alignment is bad</span><br><span style="color: hsl(120, 100%, 40%);">+ *   0   on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_set_write_protected(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct region *region);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span>  * Some SPI controllers require exclusive access to SPI flash when volatile</span><br><span>  * operations like erase or write are being performed. In such cases,</span><br><span>  * volatile_group_begin will gain exclusive access to SPI flash if not already</span><br><span></span><br></pre><p>To view, visit <a href="https://review.coreboot.org/25105">change 25105</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/25105"/><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: Ie3765b013855538eca37bc7800d3f9d5d09b8402 </div>
<div style="display:none"> Gerrit-Change-Number: 25105 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Patrick Rudolph <patrick.rudolph@9elements.com> </div>