mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 01:29:21 -04:00
spi_flash: move the unlock patch to bootloader and add support for GD
This commit is contained in:
parent
47e1b41563
commit
c29b3e2e36
@ -10,6 +10,10 @@
|
|||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "soc/soc_caps.h"
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SOC_CACHE_SUPPORT_WRAP
|
#if SOC_CACHE_SUPPORT_WRAP
|
||||||
/**
|
/**
|
||||||
* @brief Set the burst mode setting command for specified wrap mode.
|
* @brief Set the burst mode setting command for specified wrap mode.
|
||||||
@ -19,3 +23,15 @@
|
|||||||
*/
|
*/
|
||||||
esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode);
|
esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unlock Flash write protect.
|
||||||
|
* Please do not call this function in SDK.
|
||||||
|
*
|
||||||
|
* @note This can be overridden because it's attribute weak.
|
||||||
|
*/
|
||||||
|
esp_err_t bootloader_flash_unlock(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -23,7 +23,9 @@
|
|||||||
# define SPIFLASH SPIMEM1
|
# define SPIFLASH SPIMEM1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#include "esp32/rom/spi_flash.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
#include "esp32s2/rom/spi_flash.h"
|
#include "esp32s2/rom/spi_flash.h"
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
#include "esp32s3/rom/spi_flash.h"
|
#include "esp32s3/rom/spi_flash.h"
|
||||||
@ -39,6 +41,17 @@
|
|||||||
#define ENCRYPTION_IS_VIRTUAL 0
|
#define ENCRYPTION_IS_VIRTUAL 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF)
|
||||||
|
#define ISSI_ID 0x9D
|
||||||
|
#define GD_Q_ID_HIGH 0xC8
|
||||||
|
#define GD_Q_ID_MID 0x40
|
||||||
|
#define GD_Q_ID_LOW 0x16
|
||||||
|
|
||||||
|
#define ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
|
||||||
|
#define ESP_BOOTLOADER_SPIFLASH_QE_16B BIT9 // QE position when you write 16 bits at one time.
|
||||||
|
#define ESP_BOOTLOADER_SPIFLASH_QE_8B BIT1 // QE position when you write 8 bits(for SR2) at one time.
|
||||||
|
#define ESP_BOOTLOADER_SPIFLASH_WRITE_8B (8)
|
||||||
|
#define ESP_BOOTLOADER_SPIFLASH_WRITE_16B (16)
|
||||||
|
|
||||||
#ifndef BOOTLOADER_BUILD
|
#ifndef BOOTLOADER_BUILD
|
||||||
/* Normal app version maps to esp_spi_flash.h operations...
|
/* Normal app version maps to esp_spi_flash.h operations...
|
||||||
@ -426,7 +439,7 @@ esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool
|
|||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = spi_to_esp_err(esp_rom_spiflash_unlock());
|
err = bootloader_flash_unlock();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -470,10 +483,90 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
|
||||||
|
{
|
||||||
|
return BYTESHIFT(chip->device_id, 2) == ISSI_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GD25Q32, GD25Q64, GD25Q127C, GD25Q128, which use single command to read/write different SR.
|
||||||
|
FORCE_INLINE_ATTR bool is_gd_q_chip(const esp_rom_spiflash_chip_t* chip)
|
||||||
|
{
|
||||||
|
return BYTESHIFT(chip->device_id, 2) == GD_Q_ID_HIGH && BYTESHIFT(chip->device_id, 1) == GD_Q_ID_MID && BYTESHIFT(chip->device_id, 0) >= GD_Q_ID_LOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void)
|
||||||
|
{
|
||||||
|
uint16_t status = 0; // status for SR1 or SR1+SR2 if writing SR with 01H + 2Bytes.
|
||||||
|
uint16_t new_status = 0;
|
||||||
|
uint8_t status_sr2 = 0; // status_sr2 for SR2.
|
||||||
|
uint8_t new_status_sr2 = 0;
|
||||||
|
uint8_t write_sr_bit = 0;
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
if (is_issi_chip(&g_rom_flashchip)) {
|
||||||
|
write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_8B;
|
||||||
|
// ISSI chips have different QE position
|
||||||
|
|
||||||
|
status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
|
||||||
|
|
||||||
|
/* Clear all bits in the mask.
|
||||||
|
(This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.)
|
||||||
|
*/
|
||||||
|
new_status = status & (~ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI);
|
||||||
|
// Skip if nothing needs to be cleared. Otherwise will waste time waiting for the flash to clear nothing.
|
||||||
|
} else if (is_gd_q_chip(&g_rom_flashchip)) {
|
||||||
|
/* The GD chips behaviour is to clear all bits in SR1 and clear bits in SR2 except QE bit.
|
||||||
|
Use 01H to write SR1 and 31H to write SR2.
|
||||||
|
*/
|
||||||
|
write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_8B;
|
||||||
|
|
||||||
|
status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
|
||||||
|
new_status = 0;
|
||||||
|
|
||||||
|
status_sr2 = bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8);
|
||||||
|
new_status_sr2 = status_sr2 & ESP_BOOTLOADER_SPIFLASH_QE_8B;
|
||||||
|
} else {
|
||||||
|
/* For common behaviour, like XMC chips, Use 01H+2Bytes to write both SR1 and SR2*/
|
||||||
|
write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_16B;
|
||||||
|
status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8) | (bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8);
|
||||||
|
|
||||||
|
/* Clear all bits except QE, if it is set.
|
||||||
|
(This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.)
|
||||||
|
*/
|
||||||
|
new_status = status & ESP_BOOTLOADER_SPIFLASH_QE_16B;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != new_status) {
|
||||||
|
/* if the status in SR not equal to the ideal status, the status need to be updated */
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
bootloader_execute_flash_command(CMD_WRSR, new_status, write_sr_bit, 0);
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status_sr2 != new_status_sr2) {
|
||||||
|
/* If the status in SR2 not equal to the ideal status, the status need to be updated.
|
||||||
|
It doesn't need to be updated if status in SR2 is 0.
|
||||||
|
Note: if we need to update both SR1 and SR2, the `CMD_WREN` needs to be sent again.
|
||||||
|
*/
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
bootloader_execute_flash_command(CMD_WRSR2, new_status_sr2, write_sr_bit, 0);
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0);
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef g_rom_spiflash_dummy_len_plus // ESP32-C3 uses a macro to access ROM data here
|
#ifndef g_rom_spiflash_dummy_len_plus // ESP32-C3 uses a macro to access ROM data here
|
||||||
extern uint8_t g_rom_spiflash_dummy_len_plus[];
|
extern uint8_t g_rom_spiflash_dummy_len_plus[];
|
||||||
#endif
|
#endif
|
||||||
uint32_t bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
|
uint32_t IRAM_ATTR bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
|
||||||
{
|
{
|
||||||
uint32_t old_ctrl_reg = SPIFLASH.ctrl.val;
|
uint32_t old_ctrl_reg = SPIFLASH.ctrl.val;
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
@ -252,7 +252,7 @@ static esp_err_t bootloader_init_spi_flash(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
|
|
||||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||||
bootloader_enable_qio_mode();
|
bootloader_enable_qio_mode();
|
||||||
|
@ -203,7 +203,7 @@ static esp_err_t bootloader_init_spi_flash(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bootloader_spi_flash_resume();
|
bootloader_spi_flash_resume();
|
||||||
esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
|
|
||||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||||
bootloader_enable_qio_mode();
|
bootloader_enable_qio_mode();
|
||||||
|
@ -202,7 +202,7 @@ static esp_err_t bootloader_init_spi_flash(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bootloader_spi_flash_resume();
|
bootloader_spi_flash_resume();
|
||||||
esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
|
|
||||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||||
bootloader_enable_qio_mode();
|
bootloader_enable_qio_mode();
|
||||||
|
@ -198,7 +198,7 @@ static esp_err_t bootloader_init_spi_flash(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
|
|
||||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||||
bootloader_enable_qio_mode();
|
bootloader_enable_qio_mode();
|
||||||
|
@ -199,7 +199,7 @@ static esp_err_t bootloader_init_spi_flash(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
|
|
||||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||||
bootloader_enable_qio_mode();
|
bootloader_enable_qio_mode();
|
||||||
|
@ -260,7 +260,7 @@ esp_rom_spiflash_result_t esp_rom_spiflash_read_status(esp_rom_spiflash_chip_t *
|
|||||||
esp_rom_spiflash_result_t esp_rom_spiflash_read_statushigh(esp_rom_spiflash_chip_t *spi, uint32_t *status);
|
esp_rom_spiflash_result_t esp_rom_spiflash_read_statushigh(esp_rom_spiflash_chip_t *spi, uint32_t *status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write status to Falsh status register.
|
* @brief Write status to Flash status register.
|
||||||
* Please do not call this function in SDK.
|
* Please do not call this function in SDK.
|
||||||
*
|
*
|
||||||
* @param esp_rom_spiflash_chip_t *spi : The information for Flash, which is exported from ld file.
|
* @param esp_rom_spiflash_chip_t *spi : The information for Flash, which is exported from ld file.
|
||||||
|
@ -40,7 +40,6 @@ typedef struct {
|
|||||||
#define ESP_ROM_SPIFLASH_BP2 BIT4
|
#define ESP_ROM_SPIFLASH_BP2 BIT4
|
||||||
#define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
|
#define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
|
||||||
#define ESP_ROM_SPIFLASH_QE BIT9
|
#define ESP_ROM_SPIFLASH_QE BIT9
|
||||||
#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
|
|
||||||
|
|
||||||
#define FLASH_OP_MODE_RDCMD_DOUT 0x3B
|
#define FLASH_OP_MODE_RDCMD_DOUT 0x3B
|
||||||
#define ESP_ROM_FLASH_SECTOR_SIZE 0x1000
|
#define ESP_ROM_FLASH_SECTOR_SIZE 0x1000
|
||||||
|
@ -119,7 +119,6 @@ extern "C" {
|
|||||||
#define ESP_ROM_SPIFLASH_BP2 BIT4
|
#define ESP_ROM_SPIFLASH_BP2 BIT4
|
||||||
#define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
|
#define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
|
||||||
#define ESP_ROM_SPIFLASH_QE BIT9
|
#define ESP_ROM_SPIFLASH_QE BIT9
|
||||||
#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
|
|
||||||
|
|
||||||
#define FLASH_ID_GD25LQ32C 0xC86016
|
#define FLASH_ID_GD25LQ32C 0xC86016
|
||||||
|
|
||||||
|
@ -111,7 +111,6 @@ extern "C" {
|
|||||||
#define ESP_ROM_SPIFLASH_BP2 BIT4
|
#define ESP_ROM_SPIFLASH_BP2 BIT4
|
||||||
#define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
|
#define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
|
||||||
#define ESP_ROM_SPIFLASH_QE BIT9
|
#define ESP_ROM_SPIFLASH_QE BIT9
|
||||||
#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
|
|
||||||
|
|
||||||
#define FLASH_ID_GD25LQ32C 0xC86016
|
#define FLASH_ID_GD25LQ32C 0xC86016
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
|
|
||||||
#include "spi_flash_private.h"
|
#include "spi_flash_private.h"
|
||||||
#include "bootloader_flash_config.h"
|
#include "bootloader_flash_config.h"
|
||||||
|
#include "bootloader_flash.h"
|
||||||
#include "esp_private/crosscore_int.h"
|
#include "esp_private/crosscore_int.h"
|
||||||
#include "esp_flash_encrypt.h"
|
#include "esp_flash_encrypt.h"
|
||||||
|
|
||||||
@ -527,7 +528,7 @@ void IRAM_ATTR call_start_cpu0(void)
|
|||||||
|
|
||||||
extern void esp_rom_spiflash_attach(uint32_t, bool);
|
extern void esp_rom_spiflash_attach(uint32_t, bool);
|
||||||
esp_rom_spiflash_attach(esp_rom_efuse_get_flash_gpio_info(), false);
|
esp_rom_spiflash_attach(esp_rom_efuse_get_flash_gpio_info(), false);
|
||||||
esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
#else
|
#else
|
||||||
// This assumes that DROM is the first segment in the application binary, i.e. that we can read
|
// This assumes that DROM is the first segment in the application binary, i.e. that we can read
|
||||||
// the binary header through cache by accessing SOC_DROM_LOW address.
|
// the binary header through cache by accessing SOC_DROM_LOW address.
|
||||||
|
@ -63,7 +63,7 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp
|
|||||||
about interrupts, CPU coordination, flash mapping. However some of
|
about interrupts, CPU coordination, flash mapping. However some of
|
||||||
the functions in esp_spi_flash.c call it.
|
the functions in esp_spi_flash.c call it.
|
||||||
*/
|
*/
|
||||||
esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void)
|
__attribute__((__unused__)) esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void)
|
||||||
{
|
{
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
uint32_t new_status;
|
uint32_t new_status;
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
#include "esp_flash.h"
|
#include "esp_flash.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "spi_flash_private.h"
|
#include "spi_flash_private.h"
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
|
||||||
esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size);
|
esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size);
|
||||||
|
|
||||||
@ -248,11 +249,8 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock(void)
|
|||||||
static bool unlocked = false;
|
static bool unlocked = false;
|
||||||
if (!unlocked) {
|
if (!unlocked) {
|
||||||
spi_flash_guard_start();
|
spi_flash_guard_start();
|
||||||
esp_rom_spiflash_result_t rc = esp_rom_spiflash_unlock();
|
bootloader_flash_unlock();
|
||||||
spi_flash_guard_end();
|
spi_flash_guard_end();
|
||||||
if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
unlocked = true;
|
unlocked = true;
|
||||||
}
|
}
|
||||||
return ESP_ROM_SPIFLASH_RESULT_OK;
|
return ESP_ROM_SPIFLASH_RESULT_OK;
|
||||||
|
@ -75,6 +75,11 @@ extern "C" int spi_flash_get_erase_cycles(size_t sector)
|
|||||||
return spiflash.get_erase_cycles(sector);
|
return spiflash.get_erase_cycles(sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" esp_err_t bootloader_flash_unlock(void)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_rom_spiflash_result_t esp_rom_spiflash_read(uint32_t target, uint32_t *dest, int32_t len)
|
esp_rom_spiflash_result_t esp_rom_spiflash_read(uint32_t target, uint32_t *dest, int32_t len)
|
||||||
{
|
{
|
||||||
return spiflash.read(target, dest, len);
|
return spiflash.read(target, dest, len);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user