1
2
3
4
5
6
7
8#include <linux/mtd/mtd.h>
9#include <linux/mtd/spi-nor.h>
10
11#include "core.h"
12
13static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor)
14{
15 u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
16
17 if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6)
18 return mask | SR_BP3_BIT6;
19
20 if (nor->flags & SNOR_F_HAS_4BIT_BP)
21 return mask | SR_BP3;
22
23 return mask;
24}
25
26static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor)
27{
28 if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
29 return SR_TB_BIT6;
30 else
31 return SR_TB_BIT5;
32}
33
34static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
35{
36 unsigned int bp_slots, bp_slots_needed;
37 u8 mask = spi_nor_get_sr_bp_mask(nor);
38
39
40 bp_slots = (1 << hweight8(mask)) - 2;
41 bp_slots_needed = ilog2(nor->info->n_sectors);
42
43 if (bp_slots_needed > bp_slots)
44 return nor->info->sector_size <<
45 (bp_slots_needed - bp_slots);
46 else
47 return nor->info->sector_size;
48}
49
50static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
51 uint64_t *len)
52{
53 struct mtd_info *mtd = &nor->mtd;
54 u64 min_prot_len;
55 u8 mask = spi_nor_get_sr_bp_mask(nor);
56 u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
57 u8 bp, val = sr & mask;
58
59 if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6)
60 val = (val & ~SR_BP3_BIT6) | SR_BP3;
61
62 bp = val >> SR_BP_SHIFT;
63
64 if (!bp) {
65
66 *ofs = 0;
67 *len = 0;
68 return;
69 }
70
71 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
72 *len = min_prot_len << (bp - 1);
73
74 if (*len > mtd->size)
75 *len = mtd->size;
76
77 if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask)
78 *ofs = 0;
79 else
80 *ofs = mtd->size - *len;
81}
82
83
84
85
86
87static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs,
88 uint64_t len, u8 sr, bool locked)
89{
90 loff_t lock_offs, lock_offs_max, offs_max;
91 uint64_t lock_len;
92
93 if (!len)
94 return true;
95
96 spi_nor_get_locked_range_sr(nor, sr, &lock_offs, &lock_len);
97
98 lock_offs_max = lock_offs + lock_len;
99 offs_max = ofs + len;
100
101 if (locked)
102
103 return (offs_max <= lock_offs_max) && (ofs >= lock_offs);
104 else
105
106 return (ofs >= lock_offs_max) || (offs_max <= lock_offs);
107}
108
109static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
110 u8 sr)
111{
112 return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true);
113}
114
115static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs,
116 uint64_t len, u8 sr)
117{
118 return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false);
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
154static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
155{
156 struct mtd_info *mtd = &nor->mtd;
157 u64 min_prot_len;
158 int ret, status_old, status_new;
159 u8 mask = spi_nor_get_sr_bp_mask(nor);
160 u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
161 u8 pow, val;
162 loff_t lock_len;
163 bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
164 bool use_top;
165
166 ret = spi_nor_read_sr(nor, nor->bouncebuf);
167 if (ret)
168 return ret;
169
170 status_old = nor->bouncebuf[0];
171
172
173 if (spi_nor_is_locked_sr(nor, ofs, len, status_old))
174 return 0;
175
176
177 if (!spi_nor_is_locked_sr(nor, 0, ofs, status_old))
178 can_be_bottom = false;
179
180
181 if (!spi_nor_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
182 status_old))
183 can_be_top = false;
184
185 if (!can_be_bottom && !can_be_top)
186 return -EINVAL;
187
188
189 use_top = can_be_top;
190
191
192 if (use_top)
193 lock_len = mtd->size - ofs;
194 else
195 lock_len = ofs + len;
196
197 if (lock_len == mtd->size) {
198 val = mask;
199 } else {
200 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
201 pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
202 val = pow << SR_BP_SHIFT;
203
204 if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
205 val = (val & ~SR_BP3) | SR_BP3_BIT6;
206
207 if (val & ~mask)
208 return -EINVAL;
209
210
211 if (!(val & mask))
212 return -EINVAL;
213 }
214
215 status_new = (status_old & ~mask & ~tb_mask) | val;
216
217
218 status_new |= SR_SRWD;
219
220 if (!use_top)
221 status_new |= tb_mask;
222
223
224 if (status_new == status_old)
225 return 0;
226
227
228 if ((status_new & mask) < (status_old & mask))
229 return -EINVAL;
230
231 return spi_nor_write_sr_and_check(nor, status_new);
232}
233
234
235
236
237
238
239static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
240{
241 struct mtd_info *mtd = &nor->mtd;
242 u64 min_prot_len;
243 int ret, status_old, status_new;
244 u8 mask = spi_nor_get_sr_bp_mask(nor);
245 u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
246 u8 pow, val;
247 loff_t lock_len;
248 bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
249 bool use_top;
250
251 ret = spi_nor_read_sr(nor, nor->bouncebuf);
252 if (ret)
253 return ret;
254
255 status_old = nor->bouncebuf[0];
256
257
258 if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old))
259 return 0;
260
261
262 if (!spi_nor_is_unlocked_sr(nor, 0, ofs, status_old))
263 can_be_top = false;
264
265
266 if (!spi_nor_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
267 status_old))
268 can_be_bottom = false;
269
270 if (!can_be_bottom && !can_be_top)
271 return -EINVAL;
272
273
274 use_top = can_be_top;
275
276
277 if (use_top)
278 lock_len = mtd->size - (ofs + len);
279 else
280 lock_len = ofs;
281
282 if (lock_len == 0) {
283 val = 0;
284 } else {
285 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
286 pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
287 val = pow << SR_BP_SHIFT;
288
289 if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
290 val = (val & ~SR_BP3) | SR_BP3_BIT6;
291
292
293 if (val & ~mask)
294 return -EINVAL;
295 }
296
297 status_new = (status_old & ~mask & ~tb_mask) | val;
298
299
300 if (lock_len == 0)
301 status_new &= ~SR_SRWD;
302
303 if (!use_top)
304 status_new |= tb_mask;
305
306
307 if (status_new == status_old)
308 return 0;
309
310
311 if ((status_new & mask) > (status_old & mask))
312 return -EINVAL;
313
314 return spi_nor_write_sr_and_check(nor, status_new);
315}
316
317
318
319
320
321
322
323
324static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
325{
326 int ret;
327
328 ret = spi_nor_read_sr(nor, nor->bouncebuf);
329 if (ret)
330 return ret;
331
332 return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]);
333}
334
335static const struct spi_nor_locking_ops spi_nor_sr_locking_ops = {
336 .lock = spi_nor_sr_lock,
337 .unlock = spi_nor_sr_unlock,
338 .is_locked = spi_nor_sr_is_locked,
339};
340
341void spi_nor_init_default_locking_ops(struct spi_nor *nor)
342{
343 nor->params->locking_ops = &spi_nor_sr_locking_ops;
344}
345
346static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
347{
348 struct spi_nor *nor = mtd_to_spi_nor(mtd);
349 int ret;
350
351 ret = spi_nor_lock_and_prep(nor);
352 if (ret)
353 return ret;
354
355 ret = nor->params->locking_ops->lock(nor, ofs, len);
356
357 spi_nor_unlock_and_unprep(nor);
358 return ret;
359}
360
361static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
362{
363 struct spi_nor *nor = mtd_to_spi_nor(mtd);
364 int ret;
365
366 ret = spi_nor_lock_and_prep(nor);
367 if (ret)
368 return ret;
369
370 ret = nor->params->locking_ops->unlock(nor, ofs, len);
371
372 spi_nor_unlock_and_unprep(nor);
373 return ret;
374}
375
376static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
377{
378 struct spi_nor *nor = mtd_to_spi_nor(mtd);
379 int ret;
380
381 ret = spi_nor_lock_and_prep(nor);
382 if (ret)
383 return ret;
384
385 ret = nor->params->locking_ops->is_locked(nor, ofs, len);
386
387 spi_nor_unlock_and_unprep(nor);
388 return ret;
389}
390
391
392
393
394
395
396
397
398
399
400
401
402
403void spi_nor_try_unlock_all(struct spi_nor *nor)
404{
405 int ret;
406
407 if (!(nor->flags & SNOR_F_HAS_LOCK))
408 return;
409
410 dev_dbg(nor->dev, "Unprotecting entire flash array\n");
411
412 ret = spi_nor_unlock(&nor->mtd, 0, nor->params->size);
413 if (ret)
414 dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n");
415}
416
417void spi_nor_register_locking_ops(struct spi_nor *nor)
418{
419 struct mtd_info *mtd = &nor->mtd;
420
421 if (!nor->params->locking_ops)
422 return;
423
424 mtd->_lock = spi_nor_lock;
425 mtd->_unlock = spi_nor_unlock;
426 mtd->_is_locked = spi_nor_is_locked;
427}
428