Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2005, Intec Automation Inc.
0004  * Copyright (C) 2014, Freescale Semiconductor, Inc.
0005  */
0006 
0007 #include <linux/mtd/spi-nor.h>
0008 
0009 #include "core.h"
0010 
0011 #define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2)
0012 
0013 /*
0014  * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the
0015  * block protection bits. We don't support them. But legacy behavior in linux
0016  * is to unlock the whole flash array on startup. Therefore, we have to support
0017  * exactly this operation.
0018  */
0019 static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
0020 {
0021     return -EOPNOTSUPP;
0022 }
0023 
0024 static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
0025 {
0026     int ret;
0027 
0028     /* We only support unlocking the whole flash array */
0029     if (ofs || len != nor->params->size)
0030         return -EINVAL;
0031 
0032     /* Write 0x00 to the status register to disable write protection */
0033     ret = spi_nor_write_sr_and_check(nor, 0);
0034     if (ret)
0035         dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n");
0036 
0037     return ret;
0038 }
0039 
0040 static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
0041 {
0042     return -EOPNOTSUPP;
0043 }
0044 
0045 static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
0046     .lock = at25fs_nor_lock,
0047     .unlock = at25fs_nor_unlock,
0048     .is_locked = at25fs_nor_is_locked,
0049 };
0050 
0051 static void at25fs_nor_late_init(struct spi_nor *nor)
0052 {
0053     nor->params->locking_ops = &at25fs_nor_locking_ops;
0054 }
0055 
0056 static const struct spi_nor_fixups at25fs_nor_fixups = {
0057     .late_init = at25fs_nor_late_init,
0058 };
0059 
0060 /**
0061  * atmel_nor_set_global_protection - Do a Global Protect or Unprotect command
0062  * @nor:    pointer to 'struct spi_nor'
0063  * @ofs:    offset in bytes
0064  * @len:    len in bytes
0065  * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect
0066  *
0067  * Return: 0 on success, -error otherwise.
0068  */
0069 static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
0070                        uint64_t len, bool is_protect)
0071 {
0072     int ret;
0073     u8 sr;
0074 
0075     /* We only support locking the whole flash array */
0076     if (ofs || len != nor->params->size)
0077         return -EINVAL;
0078 
0079     ret = spi_nor_read_sr(nor, nor->bouncebuf);
0080     if (ret)
0081         return ret;
0082 
0083     sr = nor->bouncebuf[0];
0084 
0085     /* SRWD bit needs to be cleared, otherwise the protection doesn't change */
0086     if (sr & SR_SRWD) {
0087         sr &= ~SR_SRWD;
0088         ret = spi_nor_write_sr_and_check(nor, sr);
0089         if (ret) {
0090             dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
0091             return ret;
0092         }
0093     }
0094 
0095     if (is_protect) {
0096         sr |= ATMEL_SR_GLOBAL_PROTECT_MASK;
0097         /*
0098          * Set the SRWD bit again as soon as we are protecting
0099          * anything. This will ensure that the WP# pin is working
0100          * correctly. By doing this we also behave the same as
0101          * spi_nor_sr_lock(), which sets SRWD if any block protection
0102          * is active.
0103          */
0104         sr |= SR_SRWD;
0105     } else {
0106         sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK;
0107     }
0108 
0109     nor->bouncebuf[0] = sr;
0110 
0111     /*
0112      * We cannot use the spi_nor_write_sr_and_check() because this command
0113      * isn't really setting any bits, instead it is an pseudo command for
0114      * "Global Unprotect" or "Global Protect"
0115      */
0116     return spi_nor_write_sr(nor, nor->bouncebuf, 1);
0117 }
0118 
0119 static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs,
0120                     uint64_t len)
0121 {
0122     return atmel_nor_set_global_protection(nor, ofs, len, true);
0123 }
0124 
0125 static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs,
0126                       uint64_t len)
0127 {
0128     return atmel_nor_set_global_protection(nor, ofs, len, false);
0129 }
0130 
0131 static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
0132                      uint64_t len)
0133 {
0134     int ret;
0135 
0136     if (ofs >= nor->params->size || (ofs + len) > nor->params->size)
0137         return -EINVAL;
0138 
0139     ret = spi_nor_read_sr(nor, nor->bouncebuf);
0140     if (ret)
0141         return ret;
0142 
0143     return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
0144 }
0145 
0146 static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
0147     .lock = atmel_nor_global_protect,
0148     .unlock = atmel_nor_global_unprotect,
0149     .is_locked = atmel_nor_is_global_protected,
0150 };
0151 
0152 static void atmel_nor_global_protection_late_init(struct spi_nor *nor)
0153 {
0154     nor->params->locking_ops = &atmel_nor_global_protection_ops;
0155 }
0156 
0157 static const struct spi_nor_fixups atmel_nor_global_protection_fixups = {
0158     .late_init = atmel_nor_global_protection_late_init,
0159 };
0160 
0161 static const struct flash_info atmel_nor_parts[] = {
0162     /* Atmel -- some are (confusingly) marketed as "DataFlash" */
0163     { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4)
0164         FLAGS(SPI_NOR_HAS_LOCK)
0165         NO_SFDP_FLAGS(SECT_4K)
0166         .fixups = &at25fs_nor_fixups },
0167     { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8)
0168         FLAGS(SPI_NOR_HAS_LOCK)
0169         NO_SFDP_FLAGS(SECT_4K)
0170         .fixups = &at25fs_nor_fixups },
0171     { "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8)
0172         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0173         NO_SFDP_FLAGS(SECT_4K)
0174         .fixups = &atmel_nor_global_protection_fixups },
0175     { "at25df321",  INFO(0x1f4700, 0, 64 * 1024,  64)
0176         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0177         NO_SFDP_FLAGS(SECT_4K)
0178         .fixups = &atmel_nor_global_protection_fixups },
0179     { "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64)
0180         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0181         NO_SFDP_FLAGS(SECT_4K)
0182         .fixups = &atmel_nor_global_protection_fixups },
0183     { "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128)
0184         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0185         NO_SFDP_FLAGS(SECT_4K)
0186         .fixups = &atmel_nor_global_protection_fixups },
0187     { "at25sl321",  INFO(0x1f4216, 0, 64 * 1024, 64)
0188         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
0189     { "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8)
0190         NO_SFDP_FLAGS(SECT_4K) },
0191     { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16)
0192         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0193         NO_SFDP_FLAGS(SECT_4K)
0194         .fixups = &atmel_nor_global_protection_fixups },
0195     { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32)
0196         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0197         NO_SFDP_FLAGS(SECT_4K)
0198         .fixups = &atmel_nor_global_protection_fixups },
0199     { "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64)
0200         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
0201         NO_SFDP_FLAGS(SECT_4K)
0202         .fixups = &atmel_nor_global_protection_fixups },
0203     { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16)
0204         NO_SFDP_FLAGS(SECT_4K) },
0205 };
0206 
0207 const struct spi_nor_manufacturer spi_nor_atmel = {
0208     .name = "atmel",
0209     .parts = atmel_nor_parts,
0210     .nparts = ARRAY_SIZE(atmel_nor_parts),
0211 };