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, uint64_t len) 20 { 21 return -EOPNOTSUPP; 22 } 23 24 static int at25fs_nor_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 at25fs_nor_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 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 uint64_t 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, 122 uint64_t len) 123 { 124 return atmel_nor_set_global_protection(nor, ofs, len, true); 125 } 126 127 static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, 128 uint64_t len) 129 { 130 return atmel_nor_set_global_protection(nor, ofs, len, false); 131 } 132 133 static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, 134 uint64_t len) 135 { 136 int ret; 137 138 if (ofs >= nor->params->size || (ofs + len) > nor->params->size) 139 return -EINVAL; 140 141 ret = spi_nor_read_sr(nor, nor->bouncebuf); 142 if (ret) 143 return ret; 144 145 return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); 146 } 147 148 static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { 149 .lock = atmel_nor_global_protect, 150 .unlock = atmel_nor_global_unprotect, 151 .is_locked = atmel_nor_is_global_protected, 152 }; 153 154 static int atmel_nor_global_protection_late_init(struct spi_nor *nor) 155 { 156 nor->params->locking_ops = &atmel_nor_global_protection_ops; 157 158 return 0; 159 } 160 161 static const struct spi_nor_fixups atmel_nor_global_protection_fixups = { 162 .late_init = atmel_nor_global_protection_late_init, 163 }; 164 165 static const struct flash_info atmel_nor_parts[] = { 166 /* Atmel -- some are (confusingly) marketed as "DataFlash" */ 167 { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4) 168 FLAGS(SPI_NOR_HAS_LOCK) 169 NO_SFDP_FLAGS(SECT_4K) 170 .fixups = &at25fs_nor_fixups }, 171 { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8) 172 FLAGS(SPI_NOR_HAS_LOCK) 173 NO_SFDP_FLAGS(SECT_4K) 174 .fixups = &at25fs_nor_fixups }, 175 { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8) 176 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 177 NO_SFDP_FLAGS(SECT_4K) 178 .fixups = &atmel_nor_global_protection_fixups }, 179 { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64) 180 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 181 NO_SFDP_FLAGS(SECT_4K) 182 .fixups = &atmel_nor_global_protection_fixups }, 183 { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64) 184 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 185 NO_SFDP_FLAGS(SECT_4K) 186 .fixups = &atmel_nor_global_protection_fixups }, 187 { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128) 188 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 189 NO_SFDP_FLAGS(SECT_4K) 190 .fixups = &atmel_nor_global_protection_fixups }, 191 { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64) 192 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, 193 { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8) 194 NO_SFDP_FLAGS(SECT_4K) }, 195 { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16) 196 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 197 NO_SFDP_FLAGS(SECT_4K) 198 .fixups = &atmel_nor_global_protection_fixups }, 199 { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32) 200 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 201 NO_SFDP_FLAGS(SECT_4K) 202 .fixups = &atmel_nor_global_protection_fixups }, 203 { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64) 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 { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16) 208 NO_SFDP_FLAGS(SECT_4K) }, 209 }; 210 211 const struct spi_nor_manufacturer spi_nor_atmel = { 212 .name = "atmel", 213 .parts = atmel_nor_parts, 214 .nparts = ARRAY_SIZE(atmel_nor_parts), 215 }; 216