feat(spi_flash): Add patch to fix TH25Q16HB page 0 hardware issue

This commit is contained in:
Dong Heng
2023-02-16 17:11:31 +08:00
parent d48c4c1706
commit e2f55cbcca
7 changed files with 743 additions and 0 deletions

View File

@ -93,6 +93,10 @@ static void user_init_entry(void *param)
esp_set_cpu_freq(ESP_CPU_FREQ_160M);
#endif
#ifdef CONFIG_ENABLE_TH25Q16HB_PATCH_0
assert(th25q16hb_apply_patch_0() == 0);
#endif
app_main();
vTaskDelete(NULL);

View File

@ -5,6 +5,10 @@ if(BOOTLOADER_BUILD)
set(srcs "${srcs}" "port/port.c")
set(priv_requires "bootloader_support")
else()
if(CONFIG_ENABLE_TH25Q16HB_PATCH_0)
list(APPEND srcs "src/patch/th25q16hb.c")
endif()
set(priv_requires "esp8266" "freertos" "bootloader_support")
endif()

View File

@ -0,0 +1,12 @@
menu "SPI Flash"
menu "Patch"
config ENABLE_TH25Q16HB_PATCH_0
bool "Enable TH25Q16HB Patch 0"
default n
help
WARNING: If you don't use TH25Q16HB, you must not enable this option.
Although you use TH25Q16HB, you should ask your flash manufacturer
if your flash need use this patch.
endmenu
endmenu

View File

@ -15,4 +15,8 @@ CFLAGS += -DPARTITION_QUEUE_HEADER=\"sys/queue.h\"
ifdef IS_BOOTLOADER_BUILD
COMPONENT_SRCDIRS += port
COMPONENT_OBJS += port/port.o
else
ifdef CONFIG_ENABLE_TH25Q16HB_PATCH_0
COMPONENT_SRCDIRS += src/patch
endif
endif

View File

@ -204,6 +204,15 @@ int esp_patition_table_init_data(void *partition_info);
int esp_patition_copy_ota1_to_ota0(const void *partition_info);
#endif
#ifdef CONFIG_ENABLE_TH25Q16HB_PATCH_0
/**
* @brief Apply TH25Q16HB patch 0 to avoid some hardware issues.
*
* @return 0 if success or others if failed
*/
int th25q16hb_apply_patch_0(void);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -2,3 +2,4 @@
archive: libspi_flash.a
entries:
spi_flash_raw (noflash)
th25q16hb (noflash)

View File

@ -0,0 +1,709 @@
// Copyright 2023 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 <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/errno.h>
#include "esp_log.h"
#include "esp_attr.h"
#include "spi_flash.h"
#include "priv/esp_spi_flash_raw.h"
#include "FreeRTOS.h"
#include "esp8266/eagle_soc.h"
#include "esp8266/pin_mux_register.h"
#include "esp8266/spi_register.h"
#include "esp8266/spi_struct.h"
#define SPI_FLASH SPI0
#define SPI_BLOCK_SIZE 32
#define ADDR_SHIFT_BITS 8
#if 0
typedef int (*__ets_printf_t)(const char *fmt, ...);
#define ROM_PRINTF(_fmt, ...) ((__ets_printf_t)(0x400024cc))(_fmt, ##__VA_ARGS__)
#else
#define ROM_PRINTF(_fmt, ...)
#endif
#define TOCHAR(_v) #_v
#define PRINT_STEP(_s) ROM_PRINTF("Step %d\n", (_s));
#define JUMP_TO_STEP(_s) { ROM_PRINTF("Jump to " TOCHAR(_s) "\n"); goto _s; }
#define GOTO_FAILED(_s) { ROM_PRINTF("ERROR: " TOCHAR(_s) " failed\n"); ret = -EIO; JUMP_TO_STEP(step17); }
#define write_u8_dummy(_c, _a, _d8,_d) {uint32_t __data = _d8; spi_trans(1, (_c), 8, (_a), 24, (uint8_t *)&__data, 1, (_d));}
#define write_u8(_c, _a, _d8) write_u8_dummy((_c), (_a), (_d8), 0)
extern void Cache_Read_Disable_2(void);
extern void Cache_Read_Enable_2();
extern void vPortEnterCritical(void);
extern void vPortExitCritical(void);
extern uint32_t spi_flash_get_id(void);
static void delay(int ms)
{
for (volatile int i = 0; i < ms; i++) {
for (volatile int j = 0; j < 7800; j++) {
}
}
}
#if 0
static void dump_hex(const uint8_t *ptr, int n)
{
const uint8_t *s1 = ptr;
const int line_bytes = 16;
ROM_PRINTF("\nHex:\n");
for (int i = 0; i < n ; i += line_bytes)
{
int m = MIN(n - i, line_bytes);
ROM_PRINTF("\t");
for (int j = 0; j < m; j++)
{
ROM_PRINTF("%02x ", s1[i + j]);
}
ROM_PRINTF("\n");
}
ROM_PRINTF("\n");
}
static void dump_hex_compare(const uint8_t *s1, const uint8_t *s2, int n)
{
const int line_bytes = 16;
ROM_PRINTF("\nHex:\n");
for (int i = 0; i < n ; i += line_bytes)
{
int m = MIN(n - i, line_bytes);
ROM_PRINTF("\t");
for (int j = 0; j < m; j++)
{
ROM_PRINTF("%02x:%02x ", s1[i + j], s2[i + j]);
}
ROM_PRINTF("\n");
}
ROM_PRINTF("\n");
}
#endif
typedef struct spi_state {
uint32_t io_mux_reg;
uint32_t spi_clk_reg;
uint32_t spi_ctrl_reg;
uint32_t spi_user_reg;
} spi_state_t;
static void spi_enter(spi_state_t *state)
{
vPortEnterCritical();
Cache_Read_Disable_2();
Wait_SPI_Idle(&g_rom_flashchip);
state->io_mux_reg = READ_PERI_REG(PERIPHS_IO_MUX_CONF_U);
state->spi_clk_reg = SPI_FLASH.clock.val;
state->spi_ctrl_reg = SPI_FLASH.ctrl.val;
state->spi_user_reg = SPI_FLASH.user.val;
SPI_FLASH.user.usr_command = 1;
SPI_FLASH.user.flash_mode = 0;
SPI_FLASH.user.usr_miso_highpart = 0;
CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYS_CLK);
SPI_FLASH.user.cs_setup = 1;
SPI_FLASH.user.cs_hold = 1;
SPI_FLASH.user.usr_mosi = 1;
SPI_FLASH.user.usr_command = 1;
SPI_FLASH.user.flash_mode = 0;
SPI_FLASH.ctrl.fread_qio = 0;
SPI_FLASH.ctrl.fread_dio = 0;
SPI_FLASH.ctrl.fread_quad = 0;
SPI_FLASH.ctrl.fread_dual = 0;
SPI_FLASH.clock.val = 0;
SPI_FLASH.clock.clkcnt_l = 3;
SPI_FLASH.clock.clkcnt_h = 1;
SPI_FLASH.clock.clkcnt_n = 3;
SPI_FLASH.ctrl.fastrd_mode = 1;
while (SPI_FLASH.cmd.usr) {
;
}
}
static void spi_exit(spi_state_t *state)
{
while (SPI_FLASH.cmd.usr) {
;
}
WRITE_PERI_REG(PERIPHS_IO_MUX_CONF_U, state->io_mux_reg);
SPI_FLASH.ctrl.val = state->spi_ctrl_reg;
SPI_FLASH.clock.val = state->spi_clk_reg;
SPI_FLASH.user.val = state->spi_user_reg;
Cache_Read_Enable_2();
vPortExitCritical();
}
static void spi_trans_block(bool write_mode,
uint32_t cmd,
uint32_t cmd_bits,
uint32_t addr,
uint32_t addr_bits,
uint8_t *data,
uint32_t data_bytes,
uint32_t dummy_bits)
{
if ((uint32_t)data & 0x3) {
ROM_PRINTF("ERROR: data=%p\n", data);
return;
}
if (cmd_bits) {
SPI_FLASH.user.usr_command = 1;
SPI_FLASH.user2.usr_command_value = cmd;
SPI_FLASH.user2.usr_command_bitlen = cmd_bits - 1;
} else {
SPI_FLASH.user.usr_command = 0;
}
if (addr_bits) {
SPI_FLASH.user.usr_addr = 1;
SPI_FLASH.addr = addr << ADDR_SHIFT_BITS;
SPI_FLASH.user1.usr_addr_bitlen = addr_bits - 1;
} else {
SPI_FLASH.user.usr_addr = 0;
}
if (dummy_bits) {
SPI_FLASH.user.usr_dummy = 1;
SPI_FLASH.user1.usr_dummy_cyclelen = dummy_bits - 1;
} else {
SPI_FLASH.user.usr_dummy = 0;
}
if (write_mode && data_bytes) {
int words = (data_bytes + 3) / 4;
uint32_t *p = (uint32_t *)data;
SPI_FLASH.user.usr_mosi = 1;
SPI_FLASH.user.usr_miso = 0;
SPI_FLASH.user1.usr_mosi_bitlen = data_bytes * 8 - 1;
for (int i = 0; i < words; i++) {
SPI_FLASH.data_buf[i] = p[i];
}
} else if (!write_mode && data_bytes) {
int words = (data_bytes + 3) / 4;
SPI_FLASH.user.usr_mosi = 0;
SPI_FLASH.user.usr_miso = 1;
SPI_FLASH.user1.usr_miso_bitlen = data_bytes * 8 - 1;
for (int i = 0; i < words; i++) {
SPI_FLASH.data_buf[i] = 0;
}
} else {
SPI_FLASH.user.usr_mosi = 0;
SPI_FLASH.user.usr_miso = 0;
}
SPI_FLASH.cmd.usr = 1;
while (SPI_FLASH.cmd.usr) {
;
}
if (!write_mode && data_bytes) {
int words = (data_bytes + 3) / 4;
uint32_t *p = (uint32_t *)data;
for (int i = 0; i < words; i++) {
p[i] = SPI_FLASH.data_buf[i];
}
}
}
static void spi_trans(bool write_mode,
uint32_t cmd,
uint32_t cmd_bits,
uint32_t addr,
uint32_t addr_bits,
uint8_t *data,
uint32_t data_bytes,
uint32_t dummy_bits)
{
if (!data_bytes || data_bytes <= SPI_BLOCK_SIZE) {
return spi_trans_block(write_mode, cmd, cmd_bits, addr,
addr_bits, data, data_bytes, dummy_bits);
}
for (int i = 0; i < data_bytes; i += SPI_BLOCK_SIZE) {
uint32_t n = MIN(SPI_BLOCK_SIZE, data_bytes - i);
spi_trans_block(write_mode, cmd, cmd_bits, addr + i,
addr_bits, data + i, n, dummy_bits);
}
}
static void write_cmd(uint32_t cmd)
{
spi_trans(1, cmd, 8, 0, 0, NULL, 0, 0);
}
static void write_buffer(uint32_t addr, uint8_t *buffer, int size)
{
for (int i = 0; i < size; i += SPI_BLOCK_SIZE) {
int n = MIN(size - i, SPI_BLOCK_SIZE);
write_cmd(0x6);
spi_trans(1, 0x42, 8, addr + i, 24, buffer + i, n, 0);
delay(3);
}
}
static void read_buffer(uint32_t addr, uint8_t *buffer, int n)
{
spi_trans(0, 0x48, 8, addr, 24, buffer, n, 8);
}
static void erase_sector(uint32_t addr)
{
write_cmd(0x6);
spi_trans(1, 0x44, 8, addr, 24, NULL, 0, 0);
delay(8);
}
int th25q16hb_apply_patch_0(void)
{
int ret = 0;
uint32_t flash_id;
int count;
spi_state_t state;
uint8_t *buffer256_0;
uint8_t *buffer256_1;
uint8_t *buffer256_2;
uint8_t *buffer1024;
flash_id = spi_flash_get_id();
if (flash_id != 0x1560eb) {
ROM_PRINTF("WARN: id=0x%x, is not TH25Q16HB\n", flash_id);
return 0;
}
buffer1024 = heap_caps_malloc(1024, MALLOC_CAP_8BIT);
if (!buffer1024) {
return -ENOMEM;
}
buffer256_0 = buffer1024;
buffer256_1 = buffer1024 + 256;
buffer256_2 = buffer1024 + 512;
spi_enter(&state);
// Step 1
PRINT_STEP(1);
write_cmd(0x33);
// Step 2
PRINT_STEP(2);
write_cmd(0xcc);
// Step 3
PRINT_STEP(3);
write_cmd(0xaa);
// Step 4
PRINT_STEP(4);
write_u8(0xfa, 0x1200d, 0x3);
write_u8_dummy(0xfa, 0x2200d, 0x3, 1);
// Step 5-1
PRINT_STEP(5);
read_buffer(0xbed, buffer256_0, 3);
if (buffer256_0[0] == 0xff &&
buffer256_0[1] == 0xff &&
buffer256_0[2] == 0xff) {
ROM_PRINTF("INFO: check done 0\n");
} else if (buffer256_0[0] == 0x55 &&
buffer256_0[1] == 0xff &&
buffer256_0[2] == 0xff) {
JUMP_TO_STEP(step10);
} else if (buffer256_0[0] == 0x55 &&
buffer256_0[1] == 0x55 &&
buffer256_0[2] == 0xff) {
JUMP_TO_STEP(step14);
} else if (buffer256_0[0] == 0x55 &&
buffer256_0[1] == 0x55 &&
buffer256_0[2] == 0x55) {
JUMP_TO_STEP(step17);
} else {
ROM_PRINTF("ERROR: 0xbed=0x%x 0xbee=0x%x 0xbef=0x%x\n",
buffer256_0[0], buffer256_0[1], buffer256_0[2]);
GOTO_FAILED(5-1);
}
// Step 5-2
read_buffer(0x50d, buffer256_0, 1);
buffer256_0[0] &= 0x7f;
if (buffer256_0[0] == 0x7c) {
JUMP_TO_STEP(step17);
} else if (buffer256_0[0] == 0x3c) {
ROM_PRINTF("INFO: check done 1\n");
} else {
ROM_PRINTF("ERROR: 0x50d=0x%x\n", buffer256_0[0]);
GOTO_FAILED(5-2);
}
// Step 6
PRINT_STEP(6);
for (count = 0; count < 3; count++) {
erase_sector(0);
bool check_done = true;
read_buffer(0x0, buffer1024, 1024);
for (int i = 0; i < 1024; i++) {
if (buffer1024[i] != 0xff) {
check_done = false;
ROM_PRINTF("ERROR: buffer1024[%d]=0x%x\n", i, buffer1024[i]);
break;
}
}
if (check_done) {
break;
}
}
if (count >= 3) {
GOTO_FAILED(6)
}
// Step 7-1.1
PRINT_STEP(7);
read_buffer(0x400, buffer256_0, 256);
read_buffer(0x400, buffer256_1, 256);
read_buffer(0x400, buffer256_2, 256);
if (memcmp(buffer256_0, buffer256_1, 256) ||
memcmp(buffer256_0, buffer256_2, 256)) {
GOTO_FAILED(7-1.1);
}
write_buffer(0, buffer256_0, 256);
write_buffer(0x200, buffer256_0, 256);
// Step 7-1.2
for (count = 0; count < 3; count++) {
read_buffer(0, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
read_buffer(0x200, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
}
if (count < 3) {
GOTO_FAILED(7-1.2);
}
// Step 7-2.1
read_buffer(0x500, buffer256_0, 256);
read_buffer(0x500, buffer256_1, 256);
read_buffer(0x500, buffer256_2, 256);
if (memcmp(buffer256_0, buffer256_1, 256) ||
memcmp(buffer256_0, buffer256_2, 256)) {
GOTO_FAILED(7-2.1);
}
write_buffer(0x100, buffer256_0, 256);
write_buffer(0x300, buffer256_0, 256);
// Step 7-2.2
for (count = 0; count < 3; count++) {
read_buffer(0x100, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
read_buffer(0x300, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
}
if (count < 3) {
GOTO_FAILED(7-2.2);
}
// Step 8
PRINT_STEP(8);
read_buffer(0x0, buffer256_0, 1);
read_buffer(0x23, buffer256_1, 1);
if (buffer256_0[0] != 0x13 || buffer256_1[0] != 0x14) {
ROM_PRINTF("ERROR: 0x0=0x%x 0x23=0x%x\n", buffer256_0[0], buffer256_1[0]);
GOTO_FAILED(8);
}
// Step 9-1
PRINT_STEP(9);
read_buffer(0x140, buffer256_0, 2);
if (buffer256_0[0] != 0 || buffer256_0[1] != 0xff) {
ROM_PRINTF("ERROR: 0x140=0x%x 0x141=0x%x\n", buffer256_0[0], buffer256_0[1]);
GOTO_FAILED(9-1);
}
// Step 9-2
buffer256_0[0] = 0x55;
write_buffer(0xaed, buffer256_0, 1);
write_buffer(0xbed, buffer256_0, 1);
for (count = 0; count < 3; count++) {
read_buffer(0xaed, buffer256_0, 1);
if (buffer256_0[0] != 0x55) {
break;
}
read_buffer(0xbed, buffer256_0, 1);
if (buffer256_0[0] != 0x55) {
break;
}
}
if (count < 3) {
GOTO_FAILED(9-2);
}
step10:
// Step 10-1
PRINT_STEP(10);
for (count = 0; count < 3; count++) {
erase_sector(0x400);
bool check_done = true;
read_buffer(0x400, buffer1024, 1024);
for (int i = 0; i < 1024; i++) {
if (buffer1024[i] != 0xff) {
check_done = false;
ROM_PRINTF("ERROR: buffer1024[%d]=0x%x\n", i, buffer1024[i]);
break;
}
}
if (check_done) {
break;
}
}
if (count >= 3) {
GOTO_FAILED(10-1);
}
// Step 10-3.1
read_buffer(0, buffer256_0, 256);
read_buffer(0, buffer256_1, 256);
read_buffer(0, buffer256_2, 256);
if (memcmp(buffer256_0, buffer256_1, 256) ||
memcmp(buffer256_0, buffer256_2, 256)) {
GOTO_FAILED(10-3.1);
}
write_buffer(0x400, buffer256_0, 256);
write_buffer(0x600, buffer256_0, 256);
// Step 10-3.2
for (count = 0; count < 3; count++) {
read_buffer(0x400, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
read_buffer(0x600, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
}
if (count < 3) {
GOTO_FAILED(10-3.2);
}
// Step 10-3.3
read_buffer(0x100, buffer256_0, 256);
read_buffer(0x100, buffer256_1, 256);
read_buffer(0x100, buffer256_2, 256);
if (memcmp(buffer256_0, buffer256_1, 256) ||
memcmp(buffer256_0, buffer256_2, 256)) {
GOTO_FAILED(10-3.3);
}
buffer256_0[9] = 0x79;
buffer256_0[13] = (buffer256_0[13] & 0x3f) |
((~buffer256_0[13]) & 0x80) |
0x40;
write_buffer(0x500, buffer256_0, 256);
write_buffer(0x700, buffer256_0, 256);
// Step 10-3.4
for (count = 0; count < 3; count++) {
read_buffer(0x500, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
read_buffer(0x700, buffer256_1, 256);
if (memcmp(buffer256_0, buffer256_1, 256)) {
break;
}
}
if (count < 3) {
GOTO_FAILED(10-3.4);
}
// Step11-1
PRINT_STEP(11);
for (count = 0; count < 3; count++) {
read_buffer(0x400, buffer256_0, 1);
read_buffer(0x423, buffer256_1, 1);
if (buffer256_0[0] != 0x13 || buffer256_1[0] != 0x14) {
ROM_PRINTF("ERROR: 0x400=0x%x 0x423=0x%x\n", buffer256_0[0], buffer256_1[0]);
break;
}
}
if (count < 3) {
GOTO_FAILED(11-1);
}
// Step11-2.1
for (count = 0; count < 3; count++) {
read_buffer(0x540, buffer256_0, 2);
if (buffer256_0[0] != 0 || buffer256_0[1] != 0xff) {
ROM_PRINTF("ERROR: 0x540=0x%x 0x541=0x%x\n", buffer256_0[0], buffer256_0[1]);
break;
}
}
if (count < 3) {
GOTO_FAILED(11-2);
}
// Step11-2.2
for (count = 0; count < 3; count++) {
read_buffer(0x50d, buffer256_0, 1);
buffer256_0[0] &= 0x7f;
if (buffer256_0[0] != 0x7c) {
ROM_PRINTF("ERROR: 0x50d=0x%x\n", buffer256_0[0]);
break;
}
}
if (count < 3) {
GOTO_FAILED(11-2);
}
// Step 12
PRINT_STEP(12);
buffer256_0[0] = 0x55;
write_buffer(0xaee, buffer256_0, 1);
write_buffer(0xbee, buffer256_0, 1);
// Step 13
PRINT_STEP(13);
for (count = 0; count < 3; count++) {
read_buffer(0xaee, buffer256_0, 1);
if (buffer256_0[0] != 0x55) {
break;
}
read_buffer(0xbee, buffer256_0, 1);
if (buffer256_0[0] != 0x55) {
break;
}
}
if (count < 3) {
GOTO_FAILED(13);
}
step14:
// Step 14
PRINT_STEP(14);
for (count = 0; count < 3; count++) {
erase_sector(0);
bool check_done = true;
read_buffer(0x0, buffer1024, 1024);
for (int i = 0; i < 1024; i++) {
if (buffer1024[i] != 0xff) {
check_done = false;
ROM_PRINTF("ERROR: buffer1024[%d]=0x%x\n", i, buffer1024[i]);
break;
}
}
if (check_done) {
break;
}
}
if (count >= 3) {
GOTO_FAILED(14);
}
// Step 15
PRINT_STEP(15);
buffer256_0[0] = 0x55;
write_buffer(0xaef, buffer256_0, 1);
write_buffer(0xbef, buffer256_0, 1);
// Step 16
PRINT_STEP(16);
for (count = 0; count < 3; count++) {
read_buffer(0xaef, buffer256_0, 1);
if (buffer256_0[0] != 0x55) {
break;
}
read_buffer(0xbef, buffer256_0, 1);
if (buffer256_0[0] != 0x55) {
break;
}
}
if (count < 3) {
GOTO_FAILED(16);
}
step17:
// Step 17
PRINT_STEP(17);
write_cmd(0x55);
// Step 18
PRINT_STEP(18);
write_cmd(0x88);
spi_exit(&state);
heap_caps_free(buffer1024);
if (!ret) {
ROM_PRINTF("INFO: Patch for TH25Q16HB is done\n");
}
return ret;
}