xref: /linux/drivers/mtd/spi-nor/atmel.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
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 
11*31ad3effSMichael Walle #define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2)
12*31ad3effSMichael 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  */
198c174d15SMichael Walle static int atmel_at25fs_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
208c174d15SMichael Walle {
218c174d15SMichael Walle 	return -EOPNOTSUPP;
228c174d15SMichael Walle }
238c174d15SMichael Walle 
248c174d15SMichael Walle static int atmel_at25fs_unlock(struct spi_nor *nor, loff_t ofs, uint64_t 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 
408c174d15SMichael Walle static int atmel_at25fs_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
418c174d15SMichael Walle {
428c174d15SMichael Walle 	return -EOPNOTSUPP;
438c174d15SMichael Walle }
448c174d15SMichael Walle 
458c174d15SMichael Walle static const struct spi_nor_locking_ops atmel_at25fs_locking_ops = {
468c174d15SMichael Walle 	.lock = atmel_at25fs_lock,
478c174d15SMichael Walle 	.unlock = atmel_at25fs_unlock,
488c174d15SMichael Walle 	.is_locked = atmel_at25fs_is_locked,
498c174d15SMichael Walle };
508c174d15SMichael Walle 
518c174d15SMichael Walle static void atmel_at25fs_default_init(struct spi_nor *nor)
528c174d15SMichael Walle {
538c174d15SMichael Walle 	nor->params->locking_ops = &atmel_at25fs_locking_ops;
548c174d15SMichael Walle }
558c174d15SMichael Walle 
568c174d15SMichael Walle static const struct spi_nor_fixups atmel_at25fs_fixups = {
578c174d15SMichael Walle 	.default_init = atmel_at25fs_default_init,
588c174d15SMichael Walle };
598c174d15SMichael Walle 
60*31ad3effSMichael Walle /**
61*31ad3effSMichael Walle  * atmel_set_global_protection - Do a Global Protect or Unprotect command
62*31ad3effSMichael Walle  * @nor:	pointer to 'struct spi_nor'
63*31ad3effSMichael Walle  * @ofs:	offset in bytes
64*31ad3effSMichael Walle  * @len:	len in bytes
65*31ad3effSMichael Walle  * @is_protect:	if true do a Global Protect otherwise it is a Global Unprotect
66*31ad3effSMichael Walle  *
67*31ad3effSMichael Walle  * Return: 0 on success, -error otherwise.
68*31ad3effSMichael Walle  */
69*31ad3effSMichael Walle static int atmel_set_global_protection(struct spi_nor *nor, loff_t ofs,
70*31ad3effSMichael Walle 				       uint64_t len, bool is_protect)
71*31ad3effSMichael Walle {
72*31ad3effSMichael Walle 	int ret;
73*31ad3effSMichael Walle 	u8 sr;
74*31ad3effSMichael Walle 
75*31ad3effSMichael Walle 	/* We only support locking the whole flash array */
76*31ad3effSMichael Walle 	if (ofs || len != nor->params->size)
77*31ad3effSMichael Walle 		return -EINVAL;
78*31ad3effSMichael Walle 
79*31ad3effSMichael Walle 	ret = spi_nor_read_sr(nor, nor->bouncebuf);
80*31ad3effSMichael Walle 	if (ret)
81*31ad3effSMichael Walle 		return ret;
82*31ad3effSMichael Walle 
83*31ad3effSMichael Walle 	sr = nor->bouncebuf[0];
84*31ad3effSMichael Walle 
85*31ad3effSMichael Walle 	/* SRWD bit needs to be cleared, otherwise the protection doesn't change */
86*31ad3effSMichael Walle 	if (sr & SR_SRWD) {
87*31ad3effSMichael Walle 		sr &= ~SR_SRWD;
88*31ad3effSMichael Walle 		ret = spi_nor_write_sr_and_check(nor, sr);
89*31ad3effSMichael Walle 		if (ret) {
90*31ad3effSMichael Walle 			dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
91*31ad3effSMichael Walle 			return ret;
92*31ad3effSMichael Walle 		}
93*31ad3effSMichael Walle 	}
94*31ad3effSMichael Walle 
95*31ad3effSMichael Walle 	if (is_protect) {
96*31ad3effSMichael Walle 		sr |= ATMEL_SR_GLOBAL_PROTECT_MASK;
97*31ad3effSMichael Walle 		/*
98*31ad3effSMichael Walle 		 * Set the SRWD bit again as soon as we are protecting
99*31ad3effSMichael Walle 		 * anything. This will ensure that the WP# pin is working
100*31ad3effSMichael Walle 		 * correctly. By doing this we also behave the same as
101*31ad3effSMichael Walle 		 * spi_nor_sr_lock(), which sets SRWD if any block protection
102*31ad3effSMichael Walle 		 * is active.
103*31ad3effSMichael Walle 		 */
104*31ad3effSMichael Walle 		sr |= SR_SRWD;
105*31ad3effSMichael Walle 	} else {
106*31ad3effSMichael Walle 		sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK;
107*31ad3effSMichael Walle 	}
108*31ad3effSMichael Walle 
109*31ad3effSMichael Walle 	nor->bouncebuf[0] = sr;
110*31ad3effSMichael Walle 
111*31ad3effSMichael Walle 	/*
112*31ad3effSMichael Walle 	 * We cannot use the spi_nor_write_sr_and_check() because this command
113*31ad3effSMichael Walle 	 * isn't really setting any bits, instead it is an pseudo command for
114*31ad3effSMichael Walle 	 * "Global Unprotect" or "Global Protect"
115*31ad3effSMichael Walle 	 */
116*31ad3effSMichael Walle 	return spi_nor_write_sr(nor, nor->bouncebuf, 1);
117*31ad3effSMichael Walle }
118*31ad3effSMichael Walle 
119*31ad3effSMichael Walle static int atmel_global_protect(struct spi_nor *nor, loff_t ofs, uint64_t len)
120*31ad3effSMichael Walle {
121*31ad3effSMichael Walle 	return atmel_set_global_protection(nor, ofs, len, true);
122*31ad3effSMichael Walle }
123*31ad3effSMichael Walle 
124*31ad3effSMichael Walle static int atmel_global_unprotect(struct spi_nor *nor, loff_t ofs, uint64_t len)
125*31ad3effSMichael Walle {
126*31ad3effSMichael Walle 	return atmel_set_global_protection(nor, ofs, len, false);
127*31ad3effSMichael Walle }
128*31ad3effSMichael Walle 
129*31ad3effSMichael Walle static int atmel_is_global_protected(struct spi_nor *nor, loff_t ofs, uint64_t len)
130*31ad3effSMichael Walle {
131*31ad3effSMichael Walle 	int ret;
132*31ad3effSMichael Walle 
133*31ad3effSMichael Walle 	if (ofs >= nor->params->size || (ofs + len) > nor->params->size)
134*31ad3effSMichael Walle 		return -EINVAL;
135*31ad3effSMichael Walle 
136*31ad3effSMichael Walle 	ret = spi_nor_read_sr(nor, nor->bouncebuf);
137*31ad3effSMichael Walle 	if (ret)
138*31ad3effSMichael Walle 		return ret;
139*31ad3effSMichael Walle 
140*31ad3effSMichael Walle 	return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
141*31ad3effSMichael Walle }
142*31ad3effSMichael Walle 
143*31ad3effSMichael Walle static const struct spi_nor_locking_ops atmel_global_protection_ops = {
144*31ad3effSMichael Walle 	.lock = atmel_global_protect,
145*31ad3effSMichael Walle 	.unlock = atmel_global_unprotect,
146*31ad3effSMichael Walle 	.is_locked = atmel_is_global_protected,
147*31ad3effSMichael Walle };
148*31ad3effSMichael Walle 
149*31ad3effSMichael Walle static void atmel_global_protection_default_init(struct spi_nor *nor)
150*31ad3effSMichael Walle {
151*31ad3effSMichael Walle 	nor->params->locking_ops = &atmel_global_protection_ops;
152*31ad3effSMichael Walle }
153*31ad3effSMichael Walle 
154*31ad3effSMichael Walle static const struct spi_nor_fixups atmel_global_protection_fixups = {
155*31ad3effSMichael Walle 	.default_init = atmel_global_protection_default_init,
156*31ad3effSMichael Walle };
157*31ad3effSMichael Walle 
158f7242bfcSBoris Brezillon static const struct flash_info atmel_parts[] = {
159f7242bfcSBoris Brezillon 	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
1608c174d15SMichael Walle 	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K | SPI_NOR_HAS_LOCK)
1618c174d15SMichael Walle 		.fixups = &atmel_at25fs_fixups },
1628c174d15SMichael Walle 	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K | SPI_NOR_HAS_LOCK)
1638c174d15SMichael Walle 		.fixups = &atmel_at25fs_fixups },
164f7242bfcSBoris Brezillon 
165*31ad3effSMichael Walle 	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8,
166*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
167*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
168*31ad3effSMichael Walle 	{ "at25df321",  INFO(0x1f4700, 0, 64 * 1024,  64,
169*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
170*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
171*31ad3effSMichael Walle 	{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64,
172*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
173*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
174*31ad3effSMichael Walle 	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128,
175*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
176*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
177f7242bfcSBoris Brezillon 
178f7242bfcSBoris Brezillon 	{ "at25sl321",	INFO(0x1f4216, 0, 64 * 1024, 64,
179f7242bfcSBoris Brezillon 			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
180f7242bfcSBoris Brezillon 
181f7242bfcSBoris Brezillon 	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
182*31ad3effSMichael Walle 	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16,
183*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
184*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
185*31ad3effSMichael Walle 	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32,
186*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
187*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
188*31ad3effSMichael Walle 	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64,
189*31ad3effSMichael Walle 			     SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
190*31ad3effSMichael Walle 			.fixups = &atmel_global_protection_fixups },
191f7242bfcSBoris Brezillon 
192f7242bfcSBoris Brezillon 	{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
193f7242bfcSBoris Brezillon };
194f7242bfcSBoris Brezillon 
195f7242bfcSBoris Brezillon const struct spi_nor_manufacturer spi_nor_atmel = {
196f7242bfcSBoris Brezillon 	.name = "atmel",
197f7242bfcSBoris Brezillon 	.parts = atmel_parts,
198f7242bfcSBoris Brezillon 	.nparts = ARRAY_SIZE(atmel_parts),
199f7242bfcSBoris Brezillon };
200