From 10e6767b22bed83e62026a8e77f83eda328b0320 Mon Sep 17 00:00:00 2001 From: zhangzhaopeng Date: Tue, 21 Jan 2025 15:58:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9spif=E5=92=8Cuboot=EF=BC=8C?= =?UTF-8?q?=E5=8A=A0=E4=B8=8A=E5=86=99=E4=BF=9D=E6=8A=A4=E4=B8=8E=E8=A7=A3?= =?UTF-8?q?=E9=99=A4=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fastboot_sl100_back/linux/config-4.9 | 1 + .../linux/config-4.9_recovery | 1 + .../configs/fastboot_sl100_front/board.dts | 2 +- .../fastboot_sl100_front/linux/config-4.9 | 1 + .../linux/config-4.9_recovery | 1 + .../linux/sys_partition_nor.fex | 13 +- .../drivers/mtd/spi/spi-nor-core.c | 64 +++ lichee/linux-4.9/drivers/mtd/spi-nor/Kconfig | 6 + .../linux-4.9/drivers/mtd/spi-nor/spif-nor.c | 382 +++++++++++++++++- .../APP/SL100FRONTPANEL | Bin 78132 -> 78132 bytes 10 files changed, 456 insertions(+), 15 deletions(-) diff --git a/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 b/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 index cf8e4c56a..cae5b017d 100755 --- a/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 +++ b/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 @@ -757,6 +757,7 @@ CONFIG_MTD_SPI_NOR=y # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set # CONFIG_SPI_CADENCE_QUADSPI is not set # CONFIG_SPI_FLASH_SR is not set +# CONFIG_SPI_FLASH_DEFAULT_LOCK is not set # CONFIG_MTD_UBI is not set CONFIG_DTC=y CONFIG_OF=y diff --git a/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9_recovery b/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9_recovery index afd8c47c7..021529e4b 100755 --- a/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9_recovery +++ b/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9_recovery @@ -757,6 +757,7 @@ CONFIG_MTD_SPI_NOR=y # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set # CONFIG_SPI_CADENCE_QUADSPI is not set # CONFIG_SPI_FLASH_SR is not set +# CONFIG_SPI_FLASH_DEFAULT_LOCK is not set # CONFIG_MTD_UBI is not set CONFIG_DTC=y CONFIG_OF=y diff --git a/device/config/chips/v851s3/configs/fastboot_sl100_front/board.dts b/device/config/chips/v851s3/configs/fastboot_sl100_front/board.dts index db2171800..f25e8fb76 100755 --- a/device/config/chips/v851s3/configs/fastboot_sl100_front/board.dts +++ b/device/config/chips/v851s3/configs/fastboot_sl100_front/board.dts @@ -24,7 +24,7 @@ /*bootargs = "earlyprintk=sunxi-uart,0x02500000 clk_ignore_unused initcall_debug=0 console=ttyS0,115200 loglevel=6 lpj=240000 root=/dev/mtdblock4 rootwait init=/files/pseudo_init rdinit=/rdinit partitions=env@mtdblock1:env-redund@mtdblock2:boot@mtdblock3:rootfs@mtdblock4:extend@mtdblock5:rootfs_data@mtdblock6:UDISK@mtdblock7 coherent_pool=16K androidboot.hardware=sun8iw21p1 boot_type=3 androidboot.boot_type=3 gpt=1 mbr_offset=2080768 bootreason=unknow";*/ /* for OTA recovery system:(kernel rootfs extend) */ - bootargs = "earlyprintk=sunxi-uart,0x02500000 clk_ignore_unused initcall_debug=0 console=ttyS0,115200 loglevel=1 lpj=240000 root=/dev/mtdblock4 rootwait init=/files/pseudo_init rdinit=/rdinit partitions=env@mtdblock1:env-redund@mtdblock2:boot@mtdblock3:rootfs@mtdblock4:extend@mtdblock5:recovery@mtdblock6:rootfs_data@mtdblock7:UDISK@mtdblock8 coherent_pool=16K androidboot.hardware=sun8iw21p1 boot_type=3 androidboot.boot_type=3 gpt=1 mbr_offset=2080768 bootreason=unknow"; + bootargs = "earlyprintk=sunxi-uart,0x02500000 clk_ignore_unused initcall_debug=0 console=ttyS0,115200 loglevel=1 lpj=240000 root=/dev/mtdblock4 rootwait init=/files/pseudo_init rdinit=/rdinit partitions=env@mtdblock1:env-redund@mtdblock2:boot@mtdblock3:rootfs@mtdblock4:extend@mtdblock5:recovery@mtdblock6:UDISK@mtdblock7 coherent_pool=16K androidboot.hardware=sun8iw21p1 boot_type=3 androidboot.boot_type=3 gpt=1 mbr_offset=2080768 bootreason=unknow"; /* for OTA recovery system:(kernel rootfs extend appImg recoveryImg) */ diff --git a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 index daead19ee..f9bebb5d6 100755 --- a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 +++ b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 @@ -762,6 +762,7 @@ CONFIG_MTD_SPI_NOR=y # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set # CONFIG_SPI_CADENCE_QUADSPI is not set # CONFIG_SPI_FLASH_SR is not set +CONFIG_SPI_FLASH_DEFAULT_LOCK=y # CONFIG_MTD_UBI is not set CONFIG_DTC=y CONFIG_OF=y diff --git a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9_recovery b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9_recovery index 5c3681465..b8ddd79f3 100755 --- a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9_recovery +++ b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9_recovery @@ -765,6 +765,7 @@ CONFIG_MTD_SPI_NOR=y # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set # CONFIG_SPI_CADENCE_QUADSPI is not set # CONFIG_SPI_FLASH_SR is not set +# CONFIG_SPI_FLASH_DEFAULT_LOCK is not set # CONFIG_MTD_UBI is not set CONFIG_DTC=y CONFIG_OF=y diff --git a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/sys_partition_nor.fex b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/sys_partition_nor.fex index 84818e85f..057729de4 100755 --- a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/sys_partition_nor.fex +++ b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/sys_partition_nor.fex @@ -30,6 +30,7 @@ size = 16 ; 2、name最大12个字符 ; 3、size = 0, 将创建一个无大小的空分区 ; 4、为了安全和效率考虑,分区大小最好保证为16M字节的整数倍 +; 5、size 128 = 64KB 扇区512byte py_nor_flash ;******************************************************************************************************** [partition_start] @@ -47,13 +48,13 @@ size = 16 [partition] name = boot - size = 6144 + size = 9728 downloadfile = "boot.fex" user_type = 0x8000 [partition] name = rootfs - size = 20480 + size = 24064 downloadfile = "rootfs.fex" user_type = 0x8000 @@ -81,10 +82,10 @@ size = 16 ; downloadfile = "recoveryimg.fex" ; user_type = 0x8000 -[partition] - name = rootfs_data - size = 2048 - user_type = 0x8000 +;[partition] +; name = rootfs_data +; size = 2048 +; user_type = 0x8000 diff --git a/lichee/brandy-2.0/u-boot-2018/drivers/mtd/spi/spi-nor-core.c b/lichee/brandy-2.0/u-boot-2018/drivers/mtd/spi/spi-nor-core.c index 573fd3f06..4c874b9e4 100644 --- a/lichee/brandy-2.0/u-boot-2018/drivers/mtd/spi/spi-nor-core.c +++ b/lichee/brandy-2.0/u-boot-2018/drivers/mtd/spi/spi-nor-core.c @@ -3873,6 +3873,9 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, static int sunxi_lock_init(struct spi_nor *nor) { + uint8_t mask = 0; + uint8_t status1 = 0; + uint8_t status2 = 0; struct mtd_info *mtd = &nor->mtd; const struct flash_info *info = nor->info; @@ -3884,6 +3887,67 @@ static int sunxi_lock_init(struct spi_nor *nor) if (sunxi_individual_lock_is_enable(nor)) sunxi_individual_unlock_global(nor); + + if (JEDEC_MFR(nor->info) == SNOR_MFR_PUYA) + { + if (nor->info->id[2] == 0x19) // py25q256hb id:0x852019 + { + status1 = (uint8_t)read_sr(nor); + /* check cmp first */ + status2 = (uint8_t)read_sr2(nor); + // if ((status2 < 0) || (status1 < 0)) + // { + // printf("read err!:%x %x\n", status1, status2); + // } + if (status2 & (SR2_CMP_GD)) // cmp=1 + { + mask = (SR_BP2 | SR_BP3); + status1 |= mask; + printf("cmp=1\n"); + } + else // cmp=0 + { + printf("cmp=0\n"); + mask = (uint8_t) ~(SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3); + status1 &= mask; + } + if (write_sr_and_check(nor, status1, mask) != 0) + { + printf("py_unlock sr err!:%x\n", status1); + return -1; + } + return 0; + } + else if (nor->info->id[2] == 0x18) // py25q128ha id:0x852018 + { + status1 = (uint8_t)read_sr(nor); + /* check cmp first */ + status2 = (uint8_t)read_sr2(nor); + // if ((status2 < 0) || (status1 < 0)) + // { + // printf("read err!:%x %x\n", status1, status2); + // } + if (status2 & (SR2_CMP_GD)) // cmp=1 + { + mask = (SR_BP0 | SR_BP1 | SR_BP2); + status1 |= mask; + printf("cmp=1\n"); + } + else // cmp=0 + { + printf("cmp=0\n"); + mask = (uint8_t) ~(SR_BP0 | SR_BP1 | SR_BP2); + status1 &= mask; + } + if (write_sr_and_check(nor, status1, mask) != 0) + { + printf("py_unlock sr err!:%x\n", status1); + return -1; + } + return 0; + } + } + if (JEDEC_MFR(info) == SNOR_MFR_ST || JEDEC_MFR(info) == SNOR_MFR_MICRON || JEDEC_MFR(info) == SNOR_MFR_SST) diff --git a/lichee/linux-4.9/drivers/mtd/spi-nor/Kconfig b/lichee/linux-4.9/drivers/mtd/spi-nor/Kconfig index 2ec9c4a7d..388c2924c 100644 --- a/lichee/linux-4.9/drivers/mtd/spi-nor/Kconfig +++ b/lichee/linux-4.9/drivers/mtd/spi-nor/Kconfig @@ -81,4 +81,10 @@ config SPI_FLASH_SR help when set, enable security register write, read, earse functions. +config SPI_FLASH_DEFAULT_LOCK + bool "SPI FLASH default lock" + default n + help + when set, norflash will be protected after kernel run + endif # MTD_SPI_NOR diff --git a/lichee/linux-4.9/drivers/mtd/spi-nor/spif-nor.c b/lichee/linux-4.9/drivers/mtd/spi-nor/spif-nor.c index fc2c662a1..99e14d6f1 100644 --- a/lichee/linux-4.9/drivers/mtd/spi-nor/spif-nor.c +++ b/lichee/linux-4.9/drivers/mtd/spi-nor/spif-nor.c @@ -507,6 +507,42 @@ static int read_sr(struct spi_nor *nor) return nor->bouncebuf[0]; } +/* + * Read the PUYA status register 2, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_puya_sr2(struct spi_nor *nor) +{ + int ret; + + ret = nor->read_reg(nor, SPINOR_OP_RDSR2, nor->bouncebuf, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + + return nor->bouncebuf[0]; +} + +/* + * Read the PUYA configuration register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_puya_cr(struct spi_nor *nor) +{ + int ret; + + ret = nor->read_reg(nor, SPINOR_OP_RDSR3, nor->bouncebuf, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + + return nor->bouncebuf[0]; +} + /* * Read the flag status register, returning its value in the location * Return the status register value. @@ -553,6 +589,26 @@ static int write_sr(struct spi_nor *nor, u8 val) return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1); } +/* + * Write status register2 1 byte + * Returns negative if error occurred. + */ +static int write_puya_sr2(struct spi_nor *nor, u8 val) +{ + nor->bouncebuf[0] = val; + return nor->write_reg(nor, SPINOR_OP_WRSR2, nor->bouncebuf, 1); +} + +/* + * Write configuration register 1 byte + * Returns negative if error occurred. + */ +static int write_puya_cr(struct spi_nor *nor, u8 val) +{ + nor->bouncebuf[0] = val; + return nor->write_reg(nor, SPINOR_OP_WRSR3, nor->bouncebuf, 1); +} + /* * Set write enable latch with Write Enable command. * Returns negative if error occurred. @@ -1401,6 +1457,48 @@ static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) return ((ret & mask) != (status_new & mask)) ? -EIO : 0; } +/* Write status register2 and ensure bits in mask match written values */ +static int write_sr2_and_check(struct spi_nor *nor, u8 status_new, u8 mask) +{ + int ret; + + write_enable(nor); + ret = write_puya_sr2(nor, status_new); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + ret = read_puya_sr2(nor); + if (ret < 0) + return ret; + + return ((ret & status_new) != (status_new & status_new)) ? -EIO : 0; +} + +/* Write configuration register and ensure bits in mask match written values */ +static int write_cr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) +{ + int ret; + + write_enable(nor); + ret = write_puya_cr(nor, status_new); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + ret = read_puya_cr(nor); + if (ret < 0) + return ret; + + return ((ret & mask) != (status_new & mask)) ? -EIO : 0; +} + static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, uint64_t *len) { @@ -1672,6 +1770,175 @@ static const struct spi_nor_locking_ops stm_locking_ops = { .is_locked = stm_is_locked, }; +/* unlock except UDISK */ +/* 解锁先判断CMP位,再决定sr2应该写什么 */ +static int py_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + uint8_t mask = 0; + uint8_t status1 = 0; + uint8_t status2 = 0; + + if (nor->info->id[2] == 0x19) // py25q256hb id:0x852019 + { + status1 = (uint8_t)read_sr(nor); + /* check cmp first */ + status2 = (uint8_t)read_puya_sr2(nor); + // if ((status2 < 0) || (status1 < 0)) + // { + // pr_emerg("py_lock read err!:%x %x\n", status1, status2); + // return -1; + // } + if (status2 & (SR2_CMP_GD)) // cmp=1 + { + mask = (SR_BP2 | SR_BP3); + status1 |= mask; + } + else // cmp=0 + { + mask = (uint8_t) ~(SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3); + status1 &= mask; + } + if (write_sr_and_check(nor, status1, mask) != 0) + { + pr_emerg("py_unlock sr err!:%x\n", status1); + return -1; + } + return 0; + } + else if (nor->info->id[2] == 0x18) // py25q128ha id:0x852018 + { + pr_emerg("not support py25q128ha yet\n"); + return -1; + } + return -1; +} + +/* lock except UDISK */ +/* 上锁先判断CMP位,然后配置CMP位确保为1(cmp default值需要改变)再写sr2 */ +/* 第一次上电会调用这个函数更改CMP位,只要上电过一次,flash就会改成CMP=1 */ +static int py_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + uint8_t status1 = 0; + uint8_t status2 = 0; + uint8_t status_cr = 0; + uint8_t mask = 0; + if (nor->info->id[2] == 0x19) // py25q256hb id:0x852019 + { + /* read old sr1 and sr2 */ + status2 = (uint8_t)read_puya_sr2(nor); + status1 = (uint8_t)read_sr(nor); + status_cr = (uint8_t)read_puya_cr(nor); + // if ((status2 < 0) || (status1 < 0) || (status_cr < 0)) + // { + // pr_emerg("py_lock read err!:%x %x %x\n", status1, status2, status_cr); + // return -1; + // } + + if ((status2 & SR2_CMP_GD) == 0) // set cmp=1 if it's not 1 + { + /* first write sr2 CMP=1 */ + status2 |= (SR2_CMP_GD); + if (write_sr2_and_check(nor, status2, status2) != 0) + { + pr_emerg("py_lock sr2 err!:%x\n", status2); + return -1; + } + } + + if ((status_cr & SR_WPS_EN_WINBOND) != 0) // set wps=0 if it's not 0 + { + /* first write CR WPS=0 */ + status_cr &= ~(SR_WPS_EN_WINBOND); + if (write_cr_and_check(nor, status_cr, status_cr) != 0) + { + pr_emerg("py_lock cr err!:%x\n", status_cr); + return -1; + } + } + + /* sr1 lower 3/4 24MB except UDISK */ + mask |= (SR_BP3); + mask &= ~(SR_BP0 | SR_BP1 | SR_BP2 | SR_BP4); + // 0x7c 0111 1100 Status Register BP0~BP4,只判断这几位 + if ((status1 & 0x7c) != mask) + { + status1 |= (SR_BP3); + status1 &= ~(SR_BP0 | SR_BP1 | SR_BP2 | SR_BP4); + if (write_sr_and_check(nor, status1, mask) != 0) + { + pr_emerg("py_lock sr err!:%x\n", status1); + return -1; + } + } + + return 0; + } + else if (nor->info->id[2] == 0x18) // py25q128ha id:0x852018 + { + // pr_emerg("not support py25q128ha yet\n"); + return -1; + } + return -1; +} + +/* + * Check if the flash is locked. py_is_locked for + * more info. + * + * Returns 1 if entire region is locked, 0 is unlocked, and + * negative on errors. + */ +static int py_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + uint8_t mask = 0; + uint8_t status1 = 0; + uint8_t status2 = 0; + if (nor->info->id[2] == 0x19) // py25q256hb id:0x852019 + { + status1 = (uint8_t)read_sr(nor); + /* check cmp first */ + status2 = (uint8_t)read_puya_sr2(nor); + // if ((status2 < 0) || (status1 < 0)) + // { + // pr_emerg("py_lock read err!:%x %x\n", status1, status2); + // return -1; + // } + /* unlock status mask */ + if (status2 & (SR2_CMP_GD)) // cmp=1 + { + mask = (SR_BP2 | SR_BP3); + /* TODO:还有一种情况也算unlock,但是暂时未使用 */ + } + else // cmp=0 + { + mask &= ~(SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3); + } + + if ((status1 & 0x7c) != mask) + { + // printk(KERN_EMERG "lock\n"); + return 1; + } + else + { + // printk(KERN_EMERG "unlock\n"); + return 0; + } + } + else if (nor->info->id[2] == 0x18) // py25q128ha id:0x852018 + { + pr_emerg("not support py25q128ha yet\n"); + return -1; + } + return -1; +} + +static const struct spi_nor_locking_ops puya_locking_ops = { + .lock = py_lock, + .unlock = py_unlock, + .is_locked = py_is_locked, +}; + static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); @@ -2091,6 +2358,78 @@ static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor) return spi_nor_clear_sr_bp(nor); } +/** + * HAS : spi_nor_puya_clear_sr_bp() - clear the Status Register Block Protection + * bits on puya flashes. + * @nor: pointer to a 'struct spi_nor' + * 前提WPS=0,根据sr寄存器CMP位,判断应该写什么解锁 + * + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_puya_clear_sr_bp(struct spi_nor *nor) +{ + uint8_t mask = 0; + uint8_t status1 = 0; + uint8_t status2 = 0; + + if (nor->info->id[2] == 0x19) // py25q256hb id:0x852019 + { + status1 = (uint8_t)read_sr(nor); + /* check cmp first */ + status2 = (uint8_t)read_puya_sr2(nor); + // if ((status1 < 0) || (status2 < 0)) + // { + // pr_emerg("clear_sr_bp read err!:%x %x\n", status1, status2); + // return -1; + // } + if (status2 & (SR2_CMP_GD)) // cmp=1 + { + mask = (SR_BP2 | SR_BP3); + status1 |= mask; + } + else // cmp=0 + { + mask = (uint8_t) ~(SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3); + status1 &= mask; + } + if (write_sr_and_check(nor, status1, status1) != 0) + { + pr_emerg("clear_sr_bp sr err!:%x\n", status1); + return -1; + } + + return 0; + } + else if (nor->info->id[2] == 0x18) // py25q128ha id:0x852018 + { + status1 = (uint8_t)read_sr(nor); + /* check cmp first */ + status2 = (uint8_t)read_puya_sr2(nor); + // if ((status2 < 0) || (status1 < 0)) + // { + // printf("clear_sr_bp err!:%x %x\n", status1, status2); + // } + if (status2 & (SR2_CMP_GD)) // cmp=1 + { + mask = (SR_BP0 | SR_BP1 | SR_BP2); + status1 |= mask; + } + else // cmp=0 + { + mask = (uint8_t) ~(SR_BP0 | SR_BP1 | SR_BP2); + status1 &= mask; + } + if (write_sr_and_check(nor, status1, mask) != 0) + { + pr_emerg("clear_sr_bp sr err!:%x\n", status1); + return -1; + } + return 0; + } + return -1; +} + /* Used when the "_ext_id" is two bytes at most */ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ .id = { \ @@ -2575,11 +2914,11 @@ static const struct flash_info spi_nor_ids[] = { { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, /* PUYA */ - { "py25q128ha", INFO(0x852018, 0, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_IO_MODE | USE_RX_DTR) }, + { "py25q128ha", INFO(0x852018, 0, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_IO_MODE | USE_RX_DTR | SPI_NOR_HAS_LOCK) }, { "p25q128", INFO(0x856018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "p25q64h", INFO(0x856017, 0x0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "p25q32h", INFO(0x856016, 0x0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "py25q256hb", INFO(0x852019, 0x0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_IO_MODE | USE_RX_DTR) }, + { "py25q256hb", INFO(0x852019, 0x0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_IO_MODE | USE_RX_DTR | SPI_NOR_HAS_LOCK) }, /*Zetta*/ { "zd25q64b", INFO(0xba3217, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, @@ -4696,7 +5035,16 @@ static void spi_nor_late_init_params(struct spi_nor *nor) * the default ones. */ if (nor->flags & SNOR_F_HAS_LOCK && !nor->params.locking_ops) - nor->params.locking_ops = &stm_locking_ops; + { + if (JEDEC_MFR(nor->info) == SNOR_MFR_PUYA) + { + nor->params.locking_ops = &puya_locking_ops; + } + else // use default + { + nor->params.locking_ops = &stm_locking_ops; + } + } } /** @@ -4773,6 +5121,7 @@ static int spi_nor_init(struct spi_nor *nor) { int err; +#ifndef CONFIG_SPI_FLASH_DEFAULT_LOCK if (nor->clear_sr_bp) { if (nor->params.quad_enable == spansion_quad_enable) nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp; @@ -4784,12 +5133,17 @@ static int spi_nor_init(struct spi_nor *nor) return err; } } +#endif err = spi_nor_quad_enable(nor); if (err) { dev_err(nor->dev, "quad mode not supported\n"); return err; } +#if defined(CONFIG_SPI_FLASH_DEFAULT_LOCK) + /* 上电就给flash特定区域上锁 */ + nor->mtd._lock(&nor->mtd, 0, 0); +#endif if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) { /* @@ -5419,11 +5773,23 @@ int spif_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up * with the software protection bits set. */ - if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || - JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || - JEDEC_MFR(nor->info) == SNOR_MFR_SST || - nor->info->flags & SPI_NOR_HAS_LOCK) - nor->clear_sr_bp = spi_nor_clear_sr_bp; + if (nor->info->flags & SPI_NOR_HAS_LOCK) + { + if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || + JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || + JEDEC_MFR(nor->info) == SNOR_MFR_SST) + { + nor->clear_sr_bp = spi_nor_clear_sr_bp; + } + else if (JEDEC_MFR(nor->info) == SNOR_MFR_PUYA) + { + nor->clear_sr_bp = spi_nor_puya_clear_sr_bp; + } + else + { + nor->clear_sr_bp = NULL; + } + } /* Init flash parameters based on flash_info struct and SFDP */ spi_nor_init_params(nor); diff --git a/target/allwinner/v851s3-fastboot_sl100_front/busybox-init-base-ota-files/APP/SL100FRONTPANEL b/target/allwinner/v851s3-fastboot_sl100_front/busybox-init-base-ota-files/APP/SL100FRONTPANEL index 8384e85e6dbfa5a569da17b825887c4af095e438..812cc564a88be28a86af828ae6cbdd3988d733b5 100755 GIT binary patch delta 13423 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