1f7242bfcSBoris Brezillon // SPDX-License-Identifier: GPL-2.0 2f7242bfcSBoris Brezillon /* 3f7242bfcSBoris Brezillon * Copyright (C) 2005, Intec Automation Inc. 4f7242bfcSBoris Brezillon * Copyright (C) 2014, Freescale Semiconductor, Inc. 5f7242bfcSBoris Brezillon */ 6f7242bfcSBoris Brezillon 7f7242bfcSBoris Brezillon #include <linux/mtd/spi-nor.h> 8f7242bfcSBoris Brezillon 9f7242bfcSBoris Brezillon #include "core.h" 10f7242bfcSBoris Brezillon 11*31ad3effSMichael Walle #define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2) 12*31ad3effSMichael Walle 138c174d15SMichael Walle /* 148c174d15SMichael Walle * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the 158c174d15SMichael Walle * block protection bits. We don't support them. But legacy behavior in linux 168c174d15SMichael Walle * is to unlock the whole flash array on startup. Therefore, we have to support 178c174d15SMichael Walle * exactly this operation. 188c174d15SMichael Walle */ 198c174d15SMichael Walle static int atmel_at25fs_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) 208c174d15SMichael Walle { 218c174d15SMichael Walle return -EOPNOTSUPP; 228c174d15SMichael Walle } 238c174d15SMichael Walle 248c174d15SMichael Walle static int atmel_at25fs_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) 258c174d15SMichael Walle { 268c174d15SMichael Walle int ret; 278c174d15SMichael Walle 288c174d15SMichael Walle /* We only support unlocking the whole flash array */ 298c174d15SMichael Walle if (ofs || len != nor->params->size) 308c174d15SMichael Walle return -EINVAL; 318c174d15SMichael Walle 328c174d15SMichael Walle /* Write 0x00 to the status register to disable write protection */ 338c174d15SMichael Walle ret = spi_nor_write_sr_and_check(nor, 0); 348c174d15SMichael Walle if (ret) 358c174d15SMichael Walle dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n"); 368c174d15SMichael Walle 378c174d15SMichael Walle return ret; 388c174d15SMichael Walle } 398c174d15SMichael Walle 408c174d15SMichael Walle static int atmel_at25fs_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) 418c174d15SMichael Walle { 428c174d15SMichael Walle return -EOPNOTSUPP; 438c174d15SMichael Walle } 448c174d15SMichael Walle 458c174d15SMichael Walle static const struct spi_nor_locking_ops atmel_at25fs_locking_ops = { 468c174d15SMichael Walle .lock = atmel_at25fs_lock, 478c174d15SMichael Walle .unlock = atmel_at25fs_unlock, 488c174d15SMichael Walle .is_locked = atmel_at25fs_is_locked, 498c174d15SMichael Walle }; 508c174d15SMichael Walle 518c174d15SMichael Walle static void atmel_at25fs_default_init(struct spi_nor *nor) 528c174d15SMichael Walle { 538c174d15SMichael Walle nor->params->locking_ops = &atmel_at25fs_locking_ops; 548c174d15SMichael Walle } 558c174d15SMichael Walle 568c174d15SMichael Walle static const struct spi_nor_fixups atmel_at25fs_fixups = { 578c174d15SMichael Walle .default_init = atmel_at25fs_default_init, 588c174d15SMichael Walle }; 598c174d15SMichael Walle 60*31ad3effSMichael Walle /** 61*31ad3effSMichael Walle * atmel_set_global_protection - Do a Global Protect or Unprotect command 62*31ad3effSMichael Walle * @nor: pointer to 'struct spi_nor' 63*31ad3effSMichael Walle * @ofs: offset in bytes 64*31ad3effSMichael Walle * @len: len in bytes 65*31ad3effSMichael Walle * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect 66*31ad3effSMichael Walle * 67*31ad3effSMichael Walle * Return: 0 on success, -error otherwise. 68*31ad3effSMichael Walle */ 69*31ad3effSMichael Walle static int atmel_set_global_protection(struct spi_nor *nor, loff_t ofs, 70*31ad3effSMichael Walle uint64_t len, bool is_protect) 71*31ad3effSMichael Walle { 72*31ad3effSMichael Walle int ret; 73*31ad3effSMichael Walle u8 sr; 74*31ad3effSMichael Walle 75*31ad3effSMichael Walle /* We only support locking the whole flash array */ 76*31ad3effSMichael Walle if (ofs || len != nor->params->size) 77*31ad3effSMichael Walle return -EINVAL; 78*31ad3effSMichael Walle 79*31ad3effSMichael Walle ret = spi_nor_read_sr(nor, nor->bouncebuf); 80*31ad3effSMichael Walle if (ret) 81*31ad3effSMichael Walle return ret; 82*31ad3effSMichael Walle 83*31ad3effSMichael Walle sr = nor->bouncebuf[0]; 84*31ad3effSMichael Walle 85*31ad3effSMichael Walle /* SRWD bit needs to be cleared, otherwise the protection doesn't change */ 86*31ad3effSMichael Walle if (sr & SR_SRWD) { 87*31ad3effSMichael Walle sr &= ~SR_SRWD; 88*31ad3effSMichael Walle ret = spi_nor_write_sr_and_check(nor, sr); 89*31ad3effSMichael Walle if (ret) { 90*31ad3effSMichael Walle dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); 91*31ad3effSMichael Walle return ret; 92*31ad3effSMichael Walle } 93*31ad3effSMichael Walle } 94*31ad3effSMichael Walle 95*31ad3effSMichael Walle if (is_protect) { 96*31ad3effSMichael Walle sr |= ATMEL_SR_GLOBAL_PROTECT_MASK; 97*31ad3effSMichael Walle /* 98*31ad3effSMichael Walle * Set the SRWD bit again as soon as we are protecting 99*31ad3effSMichael Walle * anything. This will ensure that the WP# pin is working 100*31ad3effSMichael Walle * correctly. By doing this we also behave the same as 101*31ad3effSMichael Walle * spi_nor_sr_lock(), which sets SRWD if any block protection 102*31ad3effSMichael Walle * is active. 103*31ad3effSMichael Walle */ 104*31ad3effSMichael Walle sr |= SR_SRWD; 105*31ad3effSMichael Walle } else { 106*31ad3effSMichael Walle sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK; 107*31ad3effSMichael Walle } 108*31ad3effSMichael Walle 109*31ad3effSMichael Walle nor->bouncebuf[0] = sr; 110*31ad3effSMichael Walle 111*31ad3effSMichael Walle /* 112*31ad3effSMichael Walle * We cannot use the spi_nor_write_sr_and_check() because this command 113*31ad3effSMichael Walle * isn't really setting any bits, instead it is an pseudo command for 114*31ad3effSMichael Walle * "Global Unprotect" or "Global Protect" 115*31ad3effSMichael Walle */ 116*31ad3effSMichael Walle return spi_nor_write_sr(nor, nor->bouncebuf, 1); 117*31ad3effSMichael Walle } 118*31ad3effSMichael Walle 119*31ad3effSMichael Walle static int atmel_global_protect(struct spi_nor *nor, loff_t ofs, uint64_t len) 120*31ad3effSMichael Walle { 121*31ad3effSMichael Walle return atmel_set_global_protection(nor, ofs, len, true); 122*31ad3effSMichael Walle } 123*31ad3effSMichael Walle 124*31ad3effSMichael Walle static int atmel_global_unprotect(struct spi_nor *nor, loff_t ofs, uint64_t len) 125*31ad3effSMichael Walle { 126*31ad3effSMichael Walle return atmel_set_global_protection(nor, ofs, len, false); 127*31ad3effSMichael Walle } 128*31ad3effSMichael Walle 129*31ad3effSMichael Walle static int atmel_is_global_protected(struct spi_nor *nor, loff_t ofs, uint64_t len) 130*31ad3effSMichael Walle { 131*31ad3effSMichael Walle int ret; 132*31ad3effSMichael Walle 133*31ad3effSMichael Walle if (ofs >= nor->params->size || (ofs + len) > nor->params->size) 134*31ad3effSMichael Walle return -EINVAL; 135*31ad3effSMichael Walle 136*31ad3effSMichael Walle ret = spi_nor_read_sr(nor, nor->bouncebuf); 137*31ad3effSMichael Walle if (ret) 138*31ad3effSMichael Walle return ret; 139*31ad3effSMichael Walle 140*31ad3effSMichael Walle return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); 141*31ad3effSMichael Walle } 142*31ad3effSMichael Walle 143*31ad3effSMichael Walle static const struct spi_nor_locking_ops atmel_global_protection_ops = { 144*31ad3effSMichael Walle .lock = atmel_global_protect, 145*31ad3effSMichael Walle .unlock = atmel_global_unprotect, 146*31ad3effSMichael Walle .is_locked = atmel_is_global_protected, 147*31ad3effSMichael Walle }; 148*31ad3effSMichael Walle 149*31ad3effSMichael Walle static void atmel_global_protection_default_init(struct spi_nor *nor) 150*31ad3effSMichael Walle { 151*31ad3effSMichael Walle nor->params->locking_ops = &atmel_global_protection_ops; 152*31ad3effSMichael Walle } 153*31ad3effSMichael Walle 154*31ad3effSMichael Walle static const struct spi_nor_fixups atmel_global_protection_fixups = { 155*31ad3effSMichael Walle .default_init = atmel_global_protection_default_init, 156*31ad3effSMichael Walle }; 157*31ad3effSMichael Walle 158f7242bfcSBoris Brezillon static const struct flash_info atmel_parts[] = { 159f7242bfcSBoris Brezillon /* Atmel -- some are (confusingly) marketed as "DataFlash" */ 1608c174d15SMichael Walle { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) 1618c174d15SMichael Walle .fixups = &atmel_at25fs_fixups }, 1628c174d15SMichael Walle { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) 1638c174d15SMichael Walle .fixups = &atmel_at25fs_fixups }, 164f7242bfcSBoris Brezillon 165*31ad3effSMichael Walle { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, 166*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 167*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 168*31ad3effSMichael Walle { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, 169*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 170*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 171*31ad3effSMichael Walle { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, 172*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 173*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 174*31ad3effSMichael Walle { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, 175*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 176*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 177f7242bfcSBoris Brezillon 178f7242bfcSBoris Brezillon { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, 179f7242bfcSBoris Brezillon SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, 180f7242bfcSBoris Brezillon 181f7242bfcSBoris Brezillon { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, 182*31ad3effSMichael Walle { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, 183*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 184*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 185*31ad3effSMichael Walle { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, 186*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 187*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 188*31ad3effSMichael Walle { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, 189*31ad3effSMichael Walle SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 190*31ad3effSMichael Walle .fixups = &atmel_global_protection_fixups }, 191f7242bfcSBoris Brezillon 192f7242bfcSBoris Brezillon { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, 193f7242bfcSBoris Brezillon }; 194f7242bfcSBoris Brezillon 195f7242bfcSBoris Brezillon const struct spi_nor_manufacturer spi_nor_atmel = { 196f7242bfcSBoris Brezillon .name = "atmel", 197f7242bfcSBoris Brezillon .parts = atmel_parts, 198f7242bfcSBoris Brezillon .nparts = ARRAY_SIZE(atmel_parts), 199f7242bfcSBoris Brezillon }; 200