aboutsummaryrefslogtreecommitdiffstats
path: root/package/boot/uboot-layerscape/patches/0086-driver-spi-add-spansion-s25fs-s-family-protect-unpro.patch
blob: 008b6012d06511130330f6e31261a37817e195cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
From 986172ece10eee928ce66597d76f3f40ac3d25f7 Mon Sep 17 00:00:00 2001
From: Yunhui Cui <yunhui.cui@nxp.com>
Date: Mon, 8 Aug 2016 14:24:13 +0800
Subject: [PATCH 86/93] driver: spi: add spansion s25fs-s family
 protect/unprotect

In order to support spansion s25fs512s flash protect/unprotect:

[1] Fill callbak flash->lock/unlock/is_locked by spansion_lock/
unlock/is_locked.

[2] Achieve protect/unprotected by operating sr1nv, cr1nv.

Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
---
 drivers/mtd/spi/spi_flash.c |  194 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 194 insertions(+)

diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index e04bd55..87a92e9 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -877,6 +877,193 @@ int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len)
 }
 #endif
 
+#if defined(CONFIG_SPI_FLASH_SPANSION)
+/*
+ * Return 1 if the entire region is locked, 0 otherwise
+ */
+static int spansion_is_locked_sr(struct spi_flash *flash, u32 ofs, u32 len,
+			    u8 sr)
+{
+	loff_t lock_offs;
+	u32 lock_len;
+
+	stm_get_locked_range(flash, sr, &lock_offs, &lock_len);
+
+	return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+}
+
+/*
+ * Check if a region of the flash is (completely) locked. See spansion_lock() for
+ * more info.
+ *
+ * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
+ * negative on errors.
+ */
+int spansion_is_locked(struct spi_flash *flash, u32 ofs, size_t len)
+{
+	u8 cmd[4];
+	u32 sr1nv_offset = 0x0;
+	u8 sr1nv;
+	int ret;
+
+	cmd[0] = CMD_SPANSION_RDAR;
+	cmd[1] = sr1nv_offset >> 16;
+	cmd[2] = sr1nv_offset >> 8;
+	cmd[3] = sr1nv_offset >> 0;
+
+	ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
+	if (ret)
+		return -EIO;
+
+	return spansion_is_locked_sr(flash, ofs, len, sr1nv);
+}
+
+/*
+ * Lock a region of the flash. Compatible with Spansion s25fs-s family flash.
+ * Supports only the block protection bits BP{0,1,2} in the Status Register-1
+ * Non-Volatile(SR1NV).
+ *
+ * Sample table portion for 64MB flash (S25FS512S):
+ * Configuration Register-1 Non-Volatile(CR1NV[5])== 0
+ *
+ *  |  BP2  |  BP1  |  BP0  |  Prot Length  | Protected Portion
+ *  ------------------------------------------------------------
+ *  |   0   |   0   |   0   |  NONE          | NONE
+ *  |   0   |   0   |   1   |  1  MB         | Upper 1/64
+ *  |   0   |   1   |   0   |  2  MB         | Upper 1/32
+ *  |   0   |   1   |   1   |  4  MB         | Upper 1/16
+ *  |   1   |   0   |   0   |  8  MB         | Upper 1/8
+ *  |   1   |   0   |   1   |  16 MB         | Upper 1/4
+ *  |   1   |   1   |   0   |  32 MB         | Upper 1/2
+ *  |   1   |   1   |   1   |  64 MB         | ALL
+ *
+ * When CR1NV[5] == 1, the Lower memory array are protected.
+ *
+ * Returns negative on errors, 0 on success.
+ */
+int spansion_lock(struct spi_flash *flash, u32 ofs, size_t len)
+{
+	u8 status_old, status_new;
+	u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+	u8 shift = ffs(mask) - 1, pow, val;
+	int ret;
+	u8 cmd[4];
+	u32 sr1nv_offset = 0x0;
+	u8 sr1nv;
+
+	cmd[0] = CMD_SPANSION_RDAR;
+	cmd[1] = sr1nv_offset >> 16;
+	cmd[2] = sr1nv_offset >> 8;
+	cmd[3] = sr1nv_offset >> 0;
+
+	ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
+	if (ret)
+		return -EIO;
+	status_old = sr1nv;
+
+	/* SPI NOR always locks to the end */
+	if (ofs + len != flash->size) {
+		/* Does combined region extend to end? */
+		if (!stm_is_locked_sr(flash, ofs + len, flash->size - ofs - len,
+				      status_old))
+			return -EINVAL;
+		len = flash->size - ofs;
+	}
+
+	/*
+	 * Need smallest pow such that:
+	 *
+	 *   1 / (2^pow) <= (len / size)
+	 *
+	 * so (assuming power-of-2 size) we do:
+	 *
+	 *   pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
+	 */
+	pow = ilog2(flash->size) - ilog2(len);
+	val = mask - (pow << shift);
+	if (val & ~mask)
+		return -EINVAL;
+
+	/* Don't "lock" with no region! */
+	if (!(val & mask))
+		return -EINVAL;
+
+	status_new = (status_old & ~mask) | val;
+
+	/* Only modify protection if it will not unlock other areas */
+	if ((status_new & mask) <= (status_old & mask))
+		return -EINVAL;
+
+	cmd[0] = CMD_SPANSION_WRAR;
+	ret = spi_flash_cmd_write(flash->spi, cmd, 4, &status_new, 1);
+	if (ret)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Unlock a region of the flash. See spansion_lock() for more info
+ *
+ * Returns negative on errors, 0 on success.
+ */
+int spansion_unlock(struct spi_flash *flash, u32 ofs, size_t len)
+{
+	uint8_t status_old, status_new;
+	u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+	u8 shift = ffs(mask) - 1, pow, val;
+	int ret;
+
+	u8 cmd[4];
+	u32 sr1nv_offset = 0x0;
+	u8 sr1nv;
+
+	cmd[0] = CMD_SPANSION_RDAR;
+	cmd[1] = sr1nv_offset >> 16;
+	cmd[2] = sr1nv_offset >> 8;
+	cmd[3] = sr1nv_offset >> 0;
+
+	ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
+	if (ret)
+		return -EIO;
+	status_old = sr1nv;
+
+	/* Cannot unlock; would unlock larger region than requested */
+	if (spansion_is_locked_sr(flash, ofs - flash->erase_size, flash->erase_size,
+			     status_old))
+		return -EINVAL;
+	/*
+	 * Need largest pow such that:
+	 *
+	 *   1 / (2^pow) >= (len / size)
+	 *
+	 * so (assuming power-of-2 size) we do:
+	 *
+	 *   pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
+	 */
+	pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len));
+	if (ofs + len == flash->size) {
+		val = 0; /* fully unlocked */
+	} else {
+		val = mask - (pow << shift);
+		/* Some power-of-two sizes are not supported */
+		if (val & ~mask)
+			return -EINVAL;
+	}
+	status_new = (status_old & ~mask) | val;
+
+	/* Only modify protection if it will not lock other areas */
+	if ((status_new & mask) >= (status_old & mask))
+		return -EINVAL;
+
+	cmd[0] = CMD_SPANSION_WRAR;
+	ret = spi_flash_cmd_write(flash->spi, cmd, 4, &status_new, 1);
+	if (ret)
+		return -EIO;
+
+	return 0;
+}
+#endif
 
 #ifdef CONFIG_SPI_FLASH_MACRONIX
 static int spi_flash_set_qeb_mxic(struct spi_flash *flash)
@@ -1132,6 +1319,13 @@ int spi_flash_scan(struct spi_flash *flash)
 		flash->flash_is_locked = stm_is_locked;
 #endif
 		break;
+#if defined(CONFIG_SPI_FLASH_SPANSION)
+	case SPI_FLASH_CFI_MFR_SPANSION:
+		flash->flash_lock = spansion_lock;
+		flash->flash_unlock = spansion_unlock;
+		flash->flash_is_locked = spansion_is_locked;
+#endif
+		break;
 	default:
 		debug("SF: Lock ops not supported for %02x flash\n", idcode[0]);
 	}
-- 
1.7.9.5