]> granicus.if.org Git - esp-idf/commitdiff
Replace ROM SPIUnlock function with a version that can't lock flash
authorAngus Gratton <angus@espressif.com>
Fri, 21 Oct 2016 05:02:06 +0000 (16:02 +1100)
committerAngus Gratton <angus@espressif.com>
Fri, 21 Oct 2016 05:12:51 +0000 (16:12 +1100)
Avoid bug where a bad status read is copied back to flash and can set
lock bits.

components/esp32/ld/esp32.rom.ld
components/spi_flash/spi_flash_rom_patch.c [new file with mode: 0644]

index 0fa28397ee24f499dbe5e824a086c241adae72d1..4b0c1c17eb01ecf7341427ff56973a635d1a769a 100644 (file)
@@ -1598,7 +1598,7 @@ PROVIDE ( SPI_read_status = 0x4006226c );
 PROVIDE ( SPI_read_status_high = 0x40062448 );
 PROVIDE ( SPIUnlock = 0x400628b0 );
 PROVIDE ( SPI_user_command_read = 0x400621b0 );
-PROVIDE ( spi_w25q16 = 0x3ffae270 );
+PROVIDE ( SPI_flashchip_data = 0x3ffae270 );
 PROVIDE ( SPIWrite = 0x40062d50 );
 /* This is static function, but can be used, not generated by script*/
 PROVIDE ( SPI_write_enable = 0x40062320 );
diff --git a/components/spi_flash/spi_flash_rom_patch.c b/components/spi_flash/spi_flash_rom_patch.c
new file mode 100644 (file)
index 0000000..7e23bea
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "rom/spi_flash.h"
+#include "soc/spi_reg.h"
+
+static const uint32_t STATUS_QIE_BIT = (1 << 9); /* Quad Enable */
+
+#define SPI_IDX 1
+#define OTH_IDX 0
+
+extern SpiFlashChip SPI_flashchip_data;
+
+static void IRAM_ATTR Wait_SPI_Idle(void)
+{
+  /* Wait for SPI state machine to be idle */
+  while((REG_READ(SPI_EXT2_REG(SPI_IDX)) & SPI_ST)) {
+  }
+  while(REG_READ(SPI_EXT2_REG(OTH_IDX)) & SPI_ST) {
+  }
+}
+
+/* Modified version of SPIUnlock() that replaces version in ROM.
+
+   This works around a bug where SPIUnlock sometimes reads the wrong
+   high status byte (RDSR2 result) and then copies it back to the
+   flash status, which can cause the CMP bit or Status Register
+   Protect bit to become set.
+
+   Like other ROM SPI functions, this function is not designed to be
+   called directly from an RTOS environment without taking precautions
+   about interrupts, CPU coordination, flash mapping. However some of
+   the functions in esp_spi_flash.c call it.
+ */
+SpiFlashOpResult IRAM_ATTR SPIUnlock(void)
+{
+  uint32_t status;
+
+  Wait_SPI_Idle();
+
+  if (SPI_read_status_high(&status) != SPI_FLASH_RESULT_OK) {
+    return SPI_FLASH_RESULT_ERR;
+  }
+
+  /* Clear all bits except QIE, if it is set.
+     (This is different from ROM SPIUnlock, which keeps all bits as-is.)
+   */
+  status &= STATUS_QIE_BIT;
+
+  Wait_SPI_Idle();
+  REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN);
+  while(REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) {
+  }
+  Wait_SPI_Idle();
+
+  SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B);
+  if (SPI_write_status(&SPI_flashchip_data, status) != SPI_FLASH_RESULT_OK) {
+    return SPI_FLASH_RESULT_ERR;
+  }
+
+  return SPI_FLASH_RESULT_OK;
+}