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 at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) 20 { 21 return -EOPNOTSUPP; 22 } 23 24 static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 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 at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) 41 { 42 return -EOPNOTSUPP; 43 } 44 45 static const struct spi_nor_locking_ops at25fs_nor_locking_ops = { 46 .lock = at25fs_nor_lock, 47 .unlock = at25fs_nor_unlock, 48 .is_locked = at25fs_nor_is_locked, 49 }; 50 51 static int at25fs_nor_late_init(struct spi_nor *nor) 52 { 53 nor->params->locking_ops = &at25fs_nor_locking_ops; 54 55 return 0; 56 } 57 58 static const struct spi_nor_fixups at25fs_nor_fixups = { 59 .late_init = at25fs_nor_late_init, 60 }; 61 62 /** 63 * atmel_nor_set_global_protection - Do a Global Protect or Unprotect command 64 * @nor: pointer to 'struct spi_nor' 65 * @ofs: offset in bytes 66 * @len: len in bytes 67 * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect 68 * 69 * Return: 0 on success, -error otherwise. 70 */ 71 static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, 72 u64 len, bool is_protect) 73 { 74 int ret; 75 u8 sr; 76 77 /* We only support locking the whole flash array */ 78 if (ofs || len != nor->params->size) 79 return -EINVAL; 80 81 ret = spi_nor_read_sr(nor, nor->bouncebuf); 82 if (ret) 83 return ret; 84 85 sr = nor->bouncebuf[0]; 86 87 /* SRWD bit needs to be cleared, otherwise the protection doesn't change */ 88 if (sr & SR_SRWD) { 89 sr &= ~SR_SRWD; 90 ret = spi_nor_write_sr_and_check(nor, sr); 91 if (ret) { 92 dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); 93 return ret; 94 } 95 } 96 97 if (is_protect) { 98 sr |= ATMEL_SR_GLOBAL_PROTECT_MASK; 99 /* 100 * Set the SRWD bit again as soon as we are protecting 101 * anything. This will ensure that the WP# pin is working 102 * correctly. By doing this we also behave the same as 103 * spi_nor_sr_lock(), which sets SRWD if any block protection 104 * is active. 105 */ 106 sr |= SR_SRWD; 107 } else { 108 sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK; 109 } 110 111 nor->bouncebuf[0] = sr; 112 113 /* 114 * We cannot use the spi_nor_write_sr_and_check() because this command 115 * isn't really setting any bits, instead it is an pseudo command for 116 * "Global Unprotect" or "Global Protect" 117 */ 118 return spi_nor_write_sr(nor, nor->bouncebuf, 1); 119 } 120 121 static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len) 122 { 123 return atmel_nor_set_global_protection(nor, ofs, len, true); 124 } 125 126 static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len) 127 { 128 return atmel_nor_set_global_protection(nor, ofs, len, false); 129 } 130 131 static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, 132 u64 len) 133 { 134 int ret; 135 136 if (ofs >= nor->params->size || (ofs + len) > nor->params->size) 137 return -EINVAL; 138 139 ret = spi_nor_read_sr(nor, nor->bouncebuf); 140 if (ret) 141 return ret; 142 143 return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); 144 } 145 146 static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { 147 .lock = atmel_nor_global_protect, 148 .unlock = atmel_nor_global_unprotect, 149 .is_locked = atmel_nor_is_global_protected, 150 }; 151 152 static int atmel_nor_global_protection_late_init(struct spi_nor *nor) 153 { 154 nor->params->locking_ops = &atmel_nor_global_protection_ops; 155 156 return 0; 157 } 158 159 static const struct spi_nor_fixups atmel_nor_global_protection_fixups = { 160 .late_init = atmel_nor_global_protection_late_init, 161 }; 162 163 static const struct flash_info atmel_nor_parts[] = { 164 { 165 .id = SNOR_ID(0x1f, 0x04, 0x00), 166 .name = "at26f004", 167 .size = SZ_512K, 168 .no_sfdp_flags = SECT_4K, 169 }, { 170 .id = SNOR_ID(0x1f, 0x25, 0x00), 171 .name = "at45db081d", 172 .size = SZ_1M, 173 .no_sfdp_flags = SECT_4K, 174 }, { 175 .id = SNOR_ID(0x1f, 0x42, 0x16), 176 .name = "at25sl321", 177 .size = SZ_4M, 178 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, 179 }, { 180 .id = SNOR_ID(0x1f, 0x44, 0x01), 181 .name = "at25df041a", 182 .size = SZ_512K, 183 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 184 .no_sfdp_flags = SECT_4K, 185 .fixups = &atmel_nor_global_protection_fixups, 186 }, { 187 .id = SNOR_ID(0x1f, 0x45, 0x01), 188 .name = "at26df081a", 189 .size = SZ_1M, 190 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 191 .no_sfdp_flags = SECT_4K, 192 .fixups = &atmel_nor_global_protection_fixups 193 }, { 194 .id = SNOR_ID(0x1f, 0x46, 0x01), 195 .name = "at26df161a", 196 .size = SZ_2M, 197 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 198 .no_sfdp_flags = SECT_4K, 199 .fixups = &atmel_nor_global_protection_fixups 200 }, { 201 .id = SNOR_ID(0x1f, 0x47, 0x00), 202 .name = "at25df321", 203 .size = SZ_4M, 204 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 205 .no_sfdp_flags = SECT_4K, 206 .fixups = &atmel_nor_global_protection_fixups 207 }, { 208 .id = SNOR_ID(0x1f, 0x47, 0x01), 209 .name = "at25df321a", 210 .size = SZ_4M, 211 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 212 .no_sfdp_flags = SECT_4K, 213 .fixups = &atmel_nor_global_protection_fixups 214 }, { 215 .id = SNOR_ID(0x1f, 0x47, 0x08), 216 .name = "at25ff321a", 217 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 218 .fixups = &atmel_nor_global_protection_fixups 219 }, { 220 .id = SNOR_ID(0x1f, 0x48, 0x00), 221 .name = "at25df641", 222 .size = SZ_8M, 223 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, 224 .no_sfdp_flags = SECT_4K, 225 .fixups = &atmel_nor_global_protection_fixups 226 }, { 227 .id = SNOR_ID(0x1f, 0x66, 0x01), 228 .name = "at25fs010", 229 .sector_size = SZ_32K, 230 .size = SZ_128K, 231 .flags = SPI_NOR_HAS_LOCK, 232 .no_sfdp_flags = SECT_4K, 233 .fixups = &at25fs_nor_fixups 234 }, { 235 .id = SNOR_ID(0x1f, 0x66, 0x04), 236 .name = "at25fs040", 237 .size = SZ_512K, 238 .flags = SPI_NOR_HAS_LOCK, 239 .no_sfdp_flags = SECT_4K, 240 .fixups = &at25fs_nor_fixups 241 }, 242 }; 243 244 const struct spi_nor_manufacturer spi_nor_atmel = { 245 .name = "atmel", 246 .parts = atmel_nor_parts, 247 .nparts = ARRAY_SIZE(atmel_nor_parts), 248 }; 249