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