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
1131ad3effSMichael Walle #define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2)
1231ad3effSMichael 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 */
at25fs_nor_lock(struct spi_nor * nor,loff_t ofs,u64 len)19*075ede8dSTudor Ambarus static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)
208c174d15SMichael Walle {
218c174d15SMichael Walle return -EOPNOTSUPP;
228c174d15SMichael Walle }
238c174d15SMichael Walle
at25fs_nor_unlock(struct spi_nor * nor,loff_t ofs,u64 len)24*075ede8dSTudor Ambarus static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 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
at25fs_nor_is_locked(struct spi_nor * nor,loff_t ofs,u64 len)40*075ede8dSTudor Ambarus static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
418c174d15SMichael Walle {
428c174d15SMichael Walle return -EOPNOTSUPP;
438c174d15SMichael Walle }
448c174d15SMichael Walle
452394770bSMichael Walle static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
462394770bSMichael Walle .lock = at25fs_nor_lock,
472394770bSMichael Walle .unlock = at25fs_nor_unlock,
482394770bSMichael Walle .is_locked = at25fs_nor_is_locked,
498c174d15SMichael Walle };
508c174d15SMichael Walle
at25fs_nor_late_init(struct spi_nor * nor)51d534fd97STakahiro Kuwano static int at25fs_nor_late_init(struct spi_nor *nor)
528c174d15SMichael Walle {
532394770bSMichael Walle nor->params->locking_ops = &at25fs_nor_locking_ops;
54d534fd97STakahiro Kuwano
55d534fd97STakahiro Kuwano return 0;
568c174d15SMichael Walle }
578c174d15SMichael Walle
582394770bSMichael Walle static const struct spi_nor_fixups at25fs_nor_fixups = {
592394770bSMichael Walle .late_init = at25fs_nor_late_init,
608c174d15SMichael Walle };
618c174d15SMichael Walle
6231ad3effSMichael Walle /**
632394770bSMichael Walle * atmel_nor_set_global_protection - Do a Global Protect or Unprotect command
6431ad3effSMichael Walle * @nor: pointer to 'struct spi_nor'
6531ad3effSMichael Walle * @ofs: offset in bytes
6631ad3effSMichael Walle * @len: len in bytes
6731ad3effSMichael Walle * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect
6831ad3effSMichael Walle *
6931ad3effSMichael Walle * Return: 0 on success, -error otherwise.
7031ad3effSMichael Walle */
atmel_nor_set_global_protection(struct spi_nor * nor,loff_t ofs,u64 len,bool is_protect)712394770bSMichael Walle static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
72*075ede8dSTudor Ambarus u64 len, bool is_protect)
7331ad3effSMichael Walle {
7431ad3effSMichael Walle int ret;
7531ad3effSMichael Walle u8 sr;
7631ad3effSMichael Walle
7731ad3effSMichael Walle /* We only support locking the whole flash array */
7831ad3effSMichael Walle if (ofs || len != nor->params->size)
7931ad3effSMichael Walle return -EINVAL;
8031ad3effSMichael Walle
8131ad3effSMichael Walle ret = spi_nor_read_sr(nor, nor->bouncebuf);
8231ad3effSMichael Walle if (ret)
8331ad3effSMichael Walle return ret;
8431ad3effSMichael Walle
8531ad3effSMichael Walle sr = nor->bouncebuf[0];
8631ad3effSMichael Walle
8731ad3effSMichael Walle /* SRWD bit needs to be cleared, otherwise the protection doesn't change */
8831ad3effSMichael Walle if (sr & SR_SRWD) {
8931ad3effSMichael Walle sr &= ~SR_SRWD;
9031ad3effSMichael Walle ret = spi_nor_write_sr_and_check(nor, sr);
9131ad3effSMichael Walle if (ret) {
9231ad3effSMichael Walle dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
9331ad3effSMichael Walle return ret;
9431ad3effSMichael Walle }
9531ad3effSMichael Walle }
9631ad3effSMichael Walle
9731ad3effSMichael Walle if (is_protect) {
9831ad3effSMichael Walle sr |= ATMEL_SR_GLOBAL_PROTECT_MASK;
9931ad3effSMichael Walle /*
10031ad3effSMichael Walle * Set the SRWD bit again as soon as we are protecting
10131ad3effSMichael Walle * anything. This will ensure that the WP# pin is working
10231ad3effSMichael Walle * correctly. By doing this we also behave the same as
10331ad3effSMichael Walle * spi_nor_sr_lock(), which sets SRWD if any block protection
10431ad3effSMichael Walle * is active.
10531ad3effSMichael Walle */
10631ad3effSMichael Walle sr |= SR_SRWD;
10731ad3effSMichael Walle } else {
10831ad3effSMichael Walle sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK;
10931ad3effSMichael Walle }
11031ad3effSMichael Walle
11131ad3effSMichael Walle nor->bouncebuf[0] = sr;
11231ad3effSMichael Walle
11331ad3effSMichael Walle /*
11431ad3effSMichael Walle * We cannot use the spi_nor_write_sr_and_check() because this command
11531ad3effSMichael Walle * isn't really setting any bits, instead it is an pseudo command for
11631ad3effSMichael Walle * "Global Unprotect" or "Global Protect"
11731ad3effSMichael Walle */
11831ad3effSMichael Walle return spi_nor_write_sr(nor, nor->bouncebuf, 1);
11931ad3effSMichael Walle }
12031ad3effSMichael Walle
atmel_nor_global_protect(struct spi_nor * nor,loff_t ofs,u64 len)121*075ede8dSTudor Ambarus static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len)
12231ad3effSMichael Walle {
1232394770bSMichael Walle return atmel_nor_set_global_protection(nor, ofs, len, true);
12431ad3effSMichael Walle }
12531ad3effSMichael Walle
atmel_nor_global_unprotect(struct spi_nor * nor,loff_t ofs,u64 len)126*075ede8dSTudor Ambarus static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len)
12731ad3effSMichael Walle {
1282394770bSMichael Walle return atmel_nor_set_global_protection(nor, ofs, len, false);
12931ad3effSMichael Walle }
13031ad3effSMichael Walle
atmel_nor_is_global_protected(struct spi_nor * nor,loff_t ofs,u64 len)1312394770bSMichael Walle static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
132*075ede8dSTudor Ambarus u64 len)
13331ad3effSMichael Walle {
13431ad3effSMichael Walle int ret;
13531ad3effSMichael Walle
13631ad3effSMichael Walle if (ofs >= nor->params->size || (ofs + len) > nor->params->size)
13731ad3effSMichael Walle return -EINVAL;
13831ad3effSMichael Walle
13931ad3effSMichael Walle ret = spi_nor_read_sr(nor, nor->bouncebuf);
14031ad3effSMichael Walle if (ret)
14131ad3effSMichael Walle return ret;
14231ad3effSMichael Walle
14331ad3effSMichael Walle return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
14431ad3effSMichael Walle }
14531ad3effSMichael Walle
1462394770bSMichael Walle static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
1472394770bSMichael Walle .lock = atmel_nor_global_protect,
1482394770bSMichael Walle .unlock = atmel_nor_global_unprotect,
1492394770bSMichael Walle .is_locked = atmel_nor_is_global_protected,
15031ad3effSMichael Walle };
15131ad3effSMichael Walle
atmel_nor_global_protection_late_init(struct spi_nor * nor)152d534fd97STakahiro Kuwano static int atmel_nor_global_protection_late_init(struct spi_nor *nor)
15331ad3effSMichael Walle {
1542394770bSMichael Walle nor->params->locking_ops = &atmel_nor_global_protection_ops;
155d534fd97STakahiro Kuwano
156d534fd97STakahiro Kuwano return 0;
15731ad3effSMichael Walle }
15831ad3effSMichael Walle
1592394770bSMichael Walle static const struct spi_nor_fixups atmel_nor_global_protection_fixups = {
1602394770bSMichael Walle .late_init = atmel_nor_global_protection_late_init,
16131ad3effSMichael Walle };
16231ad3effSMichael Walle
1632394770bSMichael Walle static const struct flash_info atmel_nor_parts[] = {
164f9d52efbSMichael Walle {
165a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x04, 0x00),
166a16ae250SMichael Walle .name = "at26f004",
167f9d52efbSMichael Walle .size = SZ_512K,
168f9d52efbSMichael Walle .no_sfdp_flags = SECT_4K,
169a16ae250SMichael Walle }, {
170a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x25, 0x00),
171a16ae250SMichael Walle .name = "at45db081d",
172a16ae250SMichael Walle .size = SZ_1M,
173a16ae250SMichael Walle .no_sfdp_flags = SECT_4K,
174a16ae250SMichael Walle }, {
175a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x42, 0x16),
176a16ae250SMichael Walle .name = "at25sl321",
177a16ae250SMichael Walle .size = SZ_4M,
178a16ae250SMichael Walle .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
179f9d52efbSMichael Walle }, {
180f9d52efbSMichael Walle .id = SNOR_ID(0x1f, 0x44, 0x01),
181f9d52efbSMichael Walle .name = "at25df041a",
182f9d52efbSMichael Walle .size = SZ_512K,
183f9d52efbSMichael Walle .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
184f9d52efbSMichael Walle .no_sfdp_flags = SECT_4K,
185f9d52efbSMichael Walle .fixups = &atmel_nor_global_protection_fixups,
186f9d52efbSMichael Walle }, {
187a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x45, 0x01),
188a16ae250SMichael Walle .name = "at26df081a",
189a16ae250SMichael Walle .size = SZ_1M,
190a16ae250SMichael Walle .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
191a16ae250SMichael Walle .no_sfdp_flags = SECT_4K,
192a16ae250SMichael Walle .fixups = &atmel_nor_global_protection_fixups
193a16ae250SMichael Walle }, {
194a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x46, 0x01),
195a16ae250SMichael Walle .name = "at26df161a",
196a16ae250SMichael Walle .size = SZ_2M,
197a16ae250SMichael Walle .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
198a16ae250SMichael Walle .no_sfdp_flags = SECT_4K,
199a16ae250SMichael Walle .fixups = &atmel_nor_global_protection_fixups
200a16ae250SMichael Walle }, {
201f9d52efbSMichael Walle .id = SNOR_ID(0x1f, 0x47, 0x00),
202f9d52efbSMichael Walle .name = "at25df321",
203f9d52efbSMichael Walle .size = SZ_4M,
204f9d52efbSMichael Walle .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
205f9d52efbSMichael Walle .no_sfdp_flags = SECT_4K,
206f9d52efbSMichael Walle .fixups = &atmel_nor_global_protection_fixups
207f9d52efbSMichael Walle }, {
208f9d52efbSMichael Walle .id = SNOR_ID(0x1f, 0x47, 0x01),
209f9d52efbSMichael Walle .name = "at25df321a",
210f9d52efbSMichael Walle .size = SZ_4M,
211f9d52efbSMichael Walle .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
212f9d52efbSMichael Walle .no_sfdp_flags = SECT_4K,
213f9d52efbSMichael Walle .fixups = &atmel_nor_global_protection_fixups
214f9d52efbSMichael Walle }, {
2158f407edaSNicolas Ferre .id = SNOR_ID(0x1f, 0x47, 0x08),
2168f407edaSNicolas Ferre .name = "at25ff321a",
2178f407edaSNicolas Ferre .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
2188f407edaSNicolas Ferre .fixups = &atmel_nor_global_protection_fixups
2198f407edaSNicolas Ferre }, {
220f9d52efbSMichael Walle .id = SNOR_ID(0x1f, 0x48, 0x00),
221f9d52efbSMichael Walle .name = "at25df641",
222f9d52efbSMichael Walle .size = SZ_8M,
223f9d52efbSMichael Walle .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
224f9d52efbSMichael Walle .no_sfdp_flags = SECT_4K,
225f9d52efbSMichael Walle .fixups = &atmel_nor_global_protection_fixups
226f9d52efbSMichael Walle }, {
227a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x66, 0x01),
228a16ae250SMichael Walle .name = "at25fs010",
229a16ae250SMichael Walle .sector_size = SZ_32K,
230a16ae250SMichael Walle .size = SZ_128K,
231a16ae250SMichael Walle .flags = SPI_NOR_HAS_LOCK,
232a16ae250SMichael Walle .no_sfdp_flags = SECT_4K,
233a16ae250SMichael Walle .fixups = &at25fs_nor_fixups
234f9d52efbSMichael Walle }, {
235a16ae250SMichael Walle .id = SNOR_ID(0x1f, 0x66, 0x04),
236a16ae250SMichael Walle .name = "at25fs040",
237f9d52efbSMichael Walle .size = SZ_512K,
238a16ae250SMichael Walle .flags = SPI_NOR_HAS_LOCK,
239f9d52efbSMichael Walle .no_sfdp_flags = SECT_4K,
240a16ae250SMichael Walle .fixups = &at25fs_nor_fixups
241f9d52efbSMichael Walle },
242f7242bfcSBoris Brezillon };
243f7242bfcSBoris Brezillon
244f7242bfcSBoris Brezillon const struct spi_nor_manufacturer spi_nor_atmel = {
245f7242bfcSBoris Brezillon .name = "atmel",
2462394770bSMichael Walle .parts = atmel_nor_parts,
2472394770bSMichael Walle .nparts = ARRAY_SIZE(atmel_nor_parts),
248f7242bfcSBoris Brezillon };
249