|
8 | 8 |
|
9 | 9 | #include "core.h" |
10 | 10 |
|
| 11 | +#define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2) |
| 12 | + |
11 | 13 | /* |
12 | 14 | * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the |
13 | 15 | * block protection bits. We don't support them. But legacy behavior in linux |
@@ -55,25 +57,137 @@ static const struct spi_nor_fixups atmel_at25fs_fixups = { |
55 | 57 | .default_init = atmel_at25fs_default_init, |
56 | 58 | }; |
57 | 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 | + |
58 | 158 | static const struct flash_info atmel_parts[] = { |
59 | 159 | /* Atmel -- some are (confusingly) marketed as "DataFlash" */ |
60 | 160 | { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) |
61 | 161 | .fixups = &atmel_at25fs_fixups }, |
62 | 162 | { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) |
63 | 163 | .fixups = &atmel_at25fs_fixups }, |
64 | 164 |
|
65 | | - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, |
66 | | - { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, |
67 | | - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, |
68 | | - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) }, |
| 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 }, |
69 | 177 |
|
70 | 178 | { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, |
71 | 179 | SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, |
72 | 180 |
|
73 | 181 | { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, |
74 | | - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_HAS_LOCK) }, |
75 | | - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_HAS_LOCK) }, |
76 | | - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, |
| 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 }, |
77 | 191 |
|
78 | 192 | { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, |
79 | 193 | }; |
|
0 commit comments