d2556949a2f568e30f224775b5eee6e67286d2b2
[osmocom-bb.git] / src / target / firmware / flash / cfi_flash.c
1 /* NOR Flash Driver for Intel 28F160C3 NOR flash */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
5  * All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include <debug.h>
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <errno.h>
27 #include <memory.h>
28 #include <defines.h>
29 #include <flash/cfi_flash.h>
30
31 /* XXX: memdump_range() */
32 #include <calypso/misc.h>
33 #include <calypso/uart.h>
34 #include <comm/sercomm.h>
35
36 /* XXX: strings must always be in ram */
37 #if 0
38 #define puts(...)
39 #define printf(...)
40 #endif
41
42 /* global definitions */
43 #define CFI_FLASH_MAX_ERASE_REGIONS 4
44
45 /* structure of erase region descriptor */
46 struct cfi_region {
47         uint16_t b_count;
48         uint16_t b_size;
49 } __attribute__ ((packed));
50
51 /* structure of cfi query response */
52 struct cfi_query {
53         uint8_t qry[3];
54         uint16_t p_id;
55         uint16_t p_adr;
56         uint16_t a_id;
57         uint16_t a_adr;
58         uint8_t vcc_min;
59         uint8_t vcc_max;
60         uint8_t vpp_min;
61         uint8_t vpp_max;
62         uint8_t word_write_timeout_typ;
63         uint8_t buf_write_timeout_typ;
64         uint8_t block_erase_timeout_typ;
65         uint8_t chip_erase_timeout_typ;
66         uint8_t word_write_timeout_max;
67         uint8_t buf_write_timeout_max;
68         uint8_t block_erase_timeout_max;
69         uint8_t chip_erase_timeout_max;
70         uint8_t dev_size;
71         uint16_t interface_desc;
72         uint16_t max_buf_write_size;
73         uint8_t num_erase_regions;
74         struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
75 } __attribute__ ((packed));
76
77 /* manufacturer ids */
78 enum cfi_manuf {
79         CFI_MANUF_INTEL = 0x0089,
80 };
81
82 /* algorithm ids */
83 enum cfi_algo {
84         CFI_ALGO_INTEL_3 = 0x03
85 };
86
87 /* various command bytes */
88 enum cfi_flash_cmd {
89         CFI_CMD_RESET = 0xff,
90         CFI_CMD_READ_ID = 0x90,
91         CFI_CMD_CFI = 0x98,
92         CFI_CMD_READ_STATUS = 0x70,
93         CFI_CMD_CLEAR_STATUS = 0x50,
94         CFI_CMD_WRITE = 0x40,
95         CFI_CMD_BLOCK_ERASE = 0x20,
96         CFI_CMD_ERASE_CONFIRM = 0xD0,
97         CFI_CMD_PROTECT = 0x60,
98 };
99
100 /* protection commands */
101 enum flash_prot_cmd {
102         CFI_PROT_LOCK = 0x01,
103         CFI_PROT_UNLOCK = 0xD0,
104         CFI_PROT_LOCKDOWN = 0x2F
105 };
106
107 /* offsets from base */
108 enum flash_offset {
109         CFI_OFFSET_MANUFACTURER_ID = 0x00,
110         CFI_OFFSET_DEVICE_ID = 0x01,
111         CFI_OFFSET_INTEL_PROTECTION = 0x81,
112         CFI_OFFSET_CFI_RESP = 0x10
113 };
114
115 /* offsets from block base */
116 enum flash_block_offset {
117         CFI_OFFSET_BLOCK_LOCKSTATE = 0x02
118 };
119
120 /* status masks */
121 enum flash_status {
122         CFI_STATUS_READY = 0x80,
123         CFI_STATUS_ERASE_SUSPENDED = 0x40,
124         CFI_STATUS_ERASE_ERROR = 0x20,
125         CFI_STATUS_PROGRAM_ERROR = 0x10,
126         CFI_STATUS_VPP_LOW = 0x08,
127         CFI_STATUS_PROGRAM_SUSPENDED = 0x04,
128         CFI_STATUS_LOCKED_ERROR = 0x02,
129         CFI_STATUS_RESERVED = 0x01
130 };
131
132 __ramtext
133 static inline void flash_write_cmd(const void *base_addr, uint16_t cmd)
134 {
135         writew(cmd, base_addr);
136 }
137
138 __ramtext
139 static inline uint16_t flash_read16(const void *base_addr, uint32_t offset)
140 {
141         return readw(base_addr + (offset << 1));
142 }
143
144 __ramtext
145 static char flash_protected(uint32_t block_offset)
146 {
147 #ifdef CONFIG_FLASH_WRITE
148 #  ifdef CONFIG_FLASH_WRITE_LOADER
149         return 0;
150 #  else
151         return block_offset <= 0xFFFF;
152 #  endif
153 #else
154         return 1;
155 #endif
156 }
157
158 __ramtext
159 flash_lock_t flash_block_getlock(flash_t * flash, uint32_t block_offset)
160 {
161         const void *base_addr = flash->f_base;
162
163         uint8_t lockstate;
164         flash_write_cmd(base_addr, CFI_CMD_READ_ID);
165         lockstate =
166                 flash_read16(base_addr,
167                              (block_offset >> 1) + CFI_OFFSET_BLOCK_LOCKSTATE);
168         flash_write_cmd(base_addr, CFI_CMD_RESET);
169
170         if (lockstate & 0x2) {
171                 return FLASH_LOCKED_DOWN;
172         } else if (lockstate & 0x01) {
173                 return FLASH_LOCKED;
174         } else {
175                 return FLASH_UNLOCKED;
176         }
177 }
178
179 __ramtext
180 int flash_block_unlock(flash_t * flash, uint32_t block_offset)
181 {
182         const void *base_addr = flash->f_base;
183
184         if (block_offset >= flash->f_size) {
185                 return -EINVAL;
186         }
187
188         if (flash_protected(block_offset)) {
189                 return -EPERM;
190         }
191
192         printf("Unlocking block at 0x%08x, meaning %08x\n",
193                    block_offset, base_addr + block_offset);
194
195         flash_write_cmd(base_addr, CFI_CMD_PROTECT);
196         flash_write_cmd(base_addr + block_offset, CFI_PROT_UNLOCK);
197         flash_write_cmd(base_addr, CFI_CMD_RESET);
198
199         return 0;
200 }
201
202 __ramtext
203 int flash_block_lock(flash_t * flash, uint32_t block_offset)
204 {
205         const void *base_addr = flash->f_base;
206
207         if (block_offset >= flash->f_size) {
208                 return -EINVAL;
209         }
210
211         printf("Locking block at 0x%08x\n", block_offset);
212
213         flash_write_cmd(base_addr, CFI_CMD_PROTECT);
214         flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCK);
215         flash_write_cmd(base_addr, CFI_CMD_RESET);
216
217         return 0;
218 }
219
220 __ramtext
221 int flash_block_lockdown(flash_t * flash, uint32_t block_offset)
222 {
223         const void *base_addr = flash->f_base;
224
225         if (block_offset >= flash->f_size) {
226                 return -EINVAL;
227         }
228
229         printf("Locking down block at 0x%08x\n", block_offset);
230
231         flash_write_cmd(base_addr, CFI_CMD_PROTECT);
232         flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCKDOWN);
233         flash_write_cmd(base_addr, CFI_CMD_RESET);
234
235         return 0;
236 }
237
238 __ramtext
239 int flash_block_erase(flash_t * flash, uint32_t block_offset)
240 {
241         const void *base_addr = flash->f_base;
242
243         if (block_offset >= flash->f_size) {
244                 return -EINVAL;
245         }
246
247         if (flash_protected(block_offset)) {
248                 return -EPERM;
249         }
250
251         printf("Erasing block 0x%08x...", block_offset);
252
253         void *block_addr = ((uint8_t *) base_addr) + block_offset;
254
255         flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
256
257         flash_write_cmd(block_addr, CFI_CMD_BLOCK_ERASE);
258         flash_write_cmd(block_addr, CFI_CMD_ERASE_CONFIRM);
259
260         flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
261         uint16_t status;
262         do {
263                 status = flash_read16(base_addr, 0);
264         } while (!(status & CFI_STATUS_READY));
265
266         int res = 0;
267         if (status & CFI_STATUS_ERASE_ERROR) {
268                 puts("error: ");
269                 if (status & CFI_STATUS_VPP_LOW) {
270                         puts("vpp insufficient\n");
271                         res = -EFAULT;
272                 } else if (status & CFI_STATUS_LOCKED_ERROR) {
273                         puts("block is lock-protected\n");
274                         res = -EPERM;
275                 } else {
276                         puts("unknown fault\n");
277                         res = -EFAULT;
278                 }
279         } else {
280                 puts("done\n");
281         }
282
283         flash_write_cmd(base_addr, CFI_CMD_RESET);
284
285         return res;
286
287 }
288
289 __ramtext
290 int flash_program(flash_t * flash, uint32_t dst, void *src, uint32_t nbytes)
291 {
292         const void *base_addr = flash->f_base;
293         int res = 0;
294         uint32_t i;
295
296         /* check destination bounds */
297         if (dst >= flash->f_size) {
298                 return -EINVAL;
299         }
300         if (dst + nbytes > flash->f_size) {
301                 return -EINVAL;
302         }
303
304         /* check alignments */
305         if (((uint32_t) src) % 2) {
306                 return -EINVAL;
307         }
308         if (dst % 2) {
309                 return -EINVAL;
310         }
311         if (nbytes % 2) {
312                 return -EINVAL;
313         }
314
315         /* check permissions */
316         if (flash_protected(dst)) {
317                 return -EPERM;
318         }
319
320         /* say something */
321         printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src);
322
323         /* clear status register */
324         flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
325
326         /* write the words */
327         puts("writing...");
328         for (i = 0; i < nbytes; i += 2) {
329                 uint16_t *src_addr = (uint16_t *) (src + i);
330                 uint16_t *dst_addr = (uint16_t *) (base_addr + dst + i);
331
332                 uint16_t data = *src_addr;
333
334                 flash_write_cmd(dst_addr, CFI_CMD_WRITE);
335                 flash_write_cmd(dst_addr, data);
336
337                 flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
338                 uint16_t status;
339                 do {
340                         status = flash_read16(base_addr, 0);
341                 } while (!(status & CFI_STATUS_READY));
342
343                 if (status & CFI_STATUS_PROGRAM_ERROR) {
344                         puts("error: ");
345                         if (status & CFI_STATUS_VPP_LOW) {
346                                 puts("vpp insufficient");
347                                 res = -EFAULT;
348                         } else if (status & CFI_STATUS_LOCKED_ERROR) {
349                                 puts("block is lock-protected");
350                                 res = -EPERM;
351                         } else {
352                                 puts("unknown fault");
353                                 res = -EFAULT;
354                         }
355                         goto err_reset;
356                 }
357         }
358
359         flash_write_cmd(base_addr, CFI_CMD_RESET);
360
361         /* verify the result */
362         puts("verifying...");
363         for (i = 0; i < nbytes; i += 2) {
364                 uint16_t *src_addr = (uint16_t *) (src + i);
365                 uint16_t *dst_addr = (uint16_t *) (base_addr + dst + i);
366                 if (*src_addr != *dst_addr) {
367                         puts("error: verification failed");
368                         res = -EFAULT;
369                         goto err;
370                 }
371         }
372
373         puts("done\n");
374
375         return res;
376
377  err_reset:
378         flash_write_cmd(base_addr, CFI_CMD_RESET);
379
380  err:
381         printf(" at offset 0x%x\n", i);
382
383         return res;
384 }
385
386 /* Internal: retrieve manufacturer and device id from id space */
387 __ramtext
388 static int get_id(void *base_addr,
389                   uint16_t * manufacturer_id, uint16_t * device_id)
390 {
391         flash_write_cmd(base_addr, CFI_CMD_READ_ID);
392
393         *manufacturer_id = flash_read16(base_addr, CFI_OFFSET_MANUFACTURER_ID);
394         *device_id = flash_read16(base_addr, CFI_OFFSET_DEVICE_ID);
395
396         flash_write_cmd(base_addr, CFI_CMD_RESET);
397
398         return 0;
399 }
400
401 /* Internal: retrieve cfi query response data */
402 __ramtext
403 static int get_query(void *base_addr, struct cfi_query *query)
404 {
405         int res = 0;
406         int i;
407
408         flash_write_cmd(base_addr, CFI_CMD_CFI);
409
410         for (i = 0; i < sizeof(struct cfi_query); i++) {
411                 uint16_t byte =
412                         flash_read16(base_addr, CFI_OFFSET_CFI_RESP + i);
413                 *(((volatile unsigned char *)query) + i) = byte;
414         }
415
416         if (query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') {
417                 res = -ENOENT;
418         }
419
420         flash_write_cmd(base_addr, CFI_CMD_RESET);
421
422         return res;
423 }
424
425 #if 0
426
427 /* Internal: retrieve intel protection data */
428 __ramtext
429 static int get_intel_protection(void *base_addr,
430                                 uint16_t * lockp, uint8_t protp[8])
431 {
432         int i;
433
434         /* check args */
435         if (!lockp) {
436                 return -EINVAL;
437         }
438         if (!protp) {
439                 return -EINVAL;
440         }
441
442         /* enter read id mode */
443         flash_write_cmd(base_addr, CFI_CMD_READ_ID);
444
445         /* get lock */
446         *lockp = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION);
447
448         /* get data */
449         for (i = 0; i < 8; i++) {
450                 protp[i] = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION + 1 + i);
451         }
452
453         /* leave read id mode */
454         flash_write_cmd(base_addr, CFI_CMD_RESET);
455
456         return 0;
457 }
458
459 static void dump_intel_protection(uint16_t lock, uint8_t data[8])
460 {
461         printf
462                 ("  protection lock 0x%4.4x data 0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
463                  lock, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
464 }
465
466 static void dump_query_algorithms(struct cfi_query *qry)
467 {
468         printf("  primary algorithm 0x%4.4x\n", qry->p_id);
469         printf("  primary extended query 0x%4.4x\n", qry->p_adr);
470         printf("  alternate algorithm 0x%4.4x\n", qry->a_id);
471         printf("  alternate extended query 0x%4.4x\n", qry->a_adr);
472 }
473
474 static void dump_query_timing(struct cfi_query *qry)
475 {
476         uint32_t block_erase_typ = 1 << qry->block_erase_timeout_typ;
477         uint32_t block_erase_max =
478                 (1 << qry->block_erase_timeout_max) * block_erase_typ;
479         uint32_t word_program_typ = 1 << qry->word_write_timeout_typ;
480         uint32_t word_program_max =
481                 (1 << qry->word_write_timeout_max) * word_program_typ;
482         printf("  block erase typ %u ms\n", block_erase_typ);
483         printf("  block erase max %u ms\n", block_erase_max);
484         printf("  word program typ %u us\n", word_program_typ);
485         printf("  word program max %u us\n", word_program_max);
486 }
487
488 void flash_dump_info(flash_t * flash)
489 {
490         int i;
491         printf("flash at 0x%p of %d bytes with %d regions\n",
492                    flash->f_base, flash->f_size, flash->f_nregions);
493
494         uint16_t m_id, d_id;
495         if (get_id(flash->f_base, &m_id, &d_id)) {
496                 puts("  failed to get id\n");
497         } else {
498                 printf("  manufacturer 0x%4.4x device 0x%4.4x\n", m_id, d_id);
499         }
500
501         uint16_t plock;
502         uint8_t pdata[8];
503         if (get_intel_protection(flash->f_base, &plock, pdata)) {
504                 puts("  failed to get protection data\n");
505         } else {
506                 dump_intel_protection(plock, pdata);
507         }
508
509         struct cfi_query qry;
510         if (get_query(flash->f_base, &qry)) {
511                 puts("  failed to get cfi query response\n");
512         } else {
513                 dump_query_algorithms(&qry);
514                 dump_query_timing(&qry);
515         }
516
517         for (i = 0; i < flash->f_nregions; i++) {
518                 flash_region_t *fr = &flash->f_regions[i];
519                 printf("  region %d: %d blocks of %d bytes at 0x%p\n",
520                            i, fr->fr_bnum, fr->fr_bsize, fr->fr_base);
521         }
522 }
523
524 #endif
525
526 __ramtext
527 int flash_init(flash_t * flash, void *base_addr)
528 {
529         int res;
530         unsigned u;
531         uint16_t m_id, d_id;
532         uint32_t base;
533         struct cfi_query qry;
534
535         /* retrieve and check manufacturer and device id */
536         res = get_id(base_addr, &m_id, &d_id);
537         if (res) {
538                 return res;
539         }
540         if (m_id != CFI_MANUF_INTEL) {
541                 /* we only support intel devices */
542                 return -ENOTSUP;
543         }
544
545         /* retrieve and check query response */
546         res = get_query(base_addr, &qry);
547         if (res) {
548                 return res;
549         }
550         if (qry.p_id != CFI_ALGO_INTEL_3) {
551                 /* we only support algo 3 */
552                 return -ENOTSUP;
553         }
554         if (qry.num_erase_regions > FLASH_MAX_REGIONS) {
555                 /* we have a hard limit on the number of regions */
556                 return -ENOTSUP;
557         }
558
559         /* fill in basic information */
560         flash->f_base = base_addr;
561         flash->f_size = 1 << qry.dev_size;
562
563         /* determine number of erase regions */
564         flash->f_nregions = qry.num_erase_regions;
565
566         /* compute actual erase region info from cfi junk */
567         base = 0;
568         for (u = 0; u < flash->f_nregions; u++) {
569                 flash_region_t *fr = &flash->f_regions[u];
570
571                 fr->fr_base = (void *)base;
572                 fr->fr_bnum = qry.erase_regions[u].b_count + 1;
573                 fr->fr_bsize = qry.erase_regions[u].b_size * 256;
574
575                 base += fr->fr_bnum * fr->fr_bsize;
576         }
577
578         return 0;
579 }