treewide: remove FSF address
[osmocom-bb.git] / src / target / firmware / flash / cfi_flash.c
blobe34fa77ff7584f2e6237ccd52c1b5540aee9faf2
1 /* NOR Flash Driver for Intel 28F160C3 NOR flash */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
5 * All Rights Reserved
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.
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.
19 #include <debug.h>
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <errno.h>
23 #include <memory.h>
24 #include <defines.h>
25 #include <flash/cfi_flash.h>
27 /* XXX: strings must always be in ram */
28 #if 0
29 #define puts(...)
30 #define printf(...)
31 #endif
33 /* global definitions */
34 #define CFI_FLASH_MAX_ERASE_REGIONS 4
36 /* structure of erase region descriptor */
37 struct cfi_region {
38 uint16_t b_count;
39 uint16_t b_size;
40 } __attribute__ ((packed));
42 /* structure of cfi query response */
43 struct cfi_query {
44 uint8_t qry[3];
45 uint16_t p_id;
46 uint16_t p_adr;
47 uint16_t a_id;
48 uint16_t a_adr;
49 uint8_t vcc_min;
50 uint8_t vcc_max;
51 uint8_t vpp_min;
52 uint8_t vpp_max;
53 uint8_t word_write_timeout_typ;
54 uint8_t buf_write_timeout_typ;
55 uint8_t block_erase_timeout_typ;
56 uint8_t chip_erase_timeout_typ;
57 uint8_t word_write_timeout_max;
58 uint8_t buf_write_timeout_max;
59 uint8_t block_erase_timeout_max;
60 uint8_t chip_erase_timeout_max;
61 uint8_t dev_size;
62 uint16_t interface_desc;
63 uint16_t max_buf_write_size;
64 uint8_t num_erase_regions;
65 struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
66 } __attribute__ ((packed));
68 /* algorithm ids */
69 enum cfi_algo {
70 CFI_ALGO_INTEL_3 = 0x03
73 /* various command bytes */
74 enum cfi_flash_cmd {
75 CFI_CMD_RESET = 0xff,
76 CFI_CMD_RESET_TO_READ_MODE = 0xF0,
77 CFI_CMD_READ_ID = 0x90,
78 CFI_CMD_CFI = 0x98,
79 CFI_CMD_READ_STATUS = 0x70,
80 CFI_CMD_CLEAR_STATUS = 0x50,
81 CFI_CMD_WRITE = 0x40,
82 CFI_CMD_BLOCK_ERASE = 0x20,
83 CFI_CMD_ERASE_CONFIRM = 0xD0,
84 CFI_CMD_PROTECT = 0x60,
85 CFI_CMD_UNLOCK1 = 0xAA,
86 CFI_CMD_UNLOCK2 = 0x55,
89 /* protection commands */
90 enum flash_prot_cmd {
91 CFI_PROT_LOCK = 0x01,
92 CFI_PROT_UNLOCK = 0xD0,
93 CFI_PROT_LOCKDOWN = 0x2F
96 /* offsets from base */
97 enum flash_offset {
98 CFI_OFFSET_MANUFACTURER_ID = 0x00,
99 CFI_OFFSET_DEVICE_ID = 0x01,
100 CFI_OFFSET_EXT_DEVICE_ID1 = 0x0E,
101 CFI_OFFSET_EXT_DEVICE_ID2 = 0x0F,
102 CFI_OFFSET_INTEL_PROTECTION = 0x81,
103 CFI_OFFSET_CFI_RESP = 0x10
106 /* offsets from block base */
107 enum flash_block_offset {
108 CFI_OFFSET_BLOCK_LOCKSTATE = 0x02
111 /* status masks */
112 enum flash_status {
113 CFI_STATUS_READY = 0x80,
114 CFI_STATUS_ERASE_SUSPENDED = 0x40,
115 CFI_STATUS_ERASE_ERROR = 0x20,
116 CFI_STATUS_PROGRAM_ERROR = 0x10,
117 CFI_STATUS_VPP_LOW = 0x08,
118 CFI_STATUS_PROGRAM_SUSPENDED = 0x04,
119 CFI_STATUS_LOCKED_ERROR = 0x02,
120 CFI_STATUS_RESERVED = 0x01
123 #define CFI_CMD_ADDR1 0xAAA
124 #define CFI_CMD_ADDR2 0x555
126 __ramtext
127 static inline void flash_write_cmd(const void *base_addr, uint16_t cmd)
129 writew(cmd, base_addr);
132 __ramtext
133 static inline uint16_t flash_read16(const void *base_addr, uint32_t offset)
135 return readw(base_addr + (offset << 1));
138 __ramtext
139 static char flash_protected(uint32_t block_offset)
141 #ifdef CONFIG_FLASH_WRITE
142 # ifdef CONFIG_FLASH_WRITE_LOADER
143 return 0;
144 # else
145 return block_offset <= 0xFFFF;
146 # endif
147 #else
148 return 1;
149 #endif
152 __ramtext
153 flash_lock_t flash_block_getlock(flash_t * flash, uint32_t block_offset)
155 const void *base_addr = flash->f_base;
157 uint8_t lockstate;
158 flash_write_cmd(base_addr, CFI_CMD_READ_ID);
159 lockstate =
160 flash_read16(base_addr,
161 (block_offset >> 1) + CFI_OFFSET_BLOCK_LOCKSTATE);
162 flash_write_cmd(base_addr, CFI_CMD_RESET);
164 if (lockstate & 0x2) {
165 return FLASH_LOCKED_DOWN;
166 } else if (lockstate & 0x01) {
167 return FLASH_LOCKED;
168 } else {
169 return FLASH_UNLOCKED;
173 __ramtext
174 int flash_block_unlock(flash_t * flash, uint32_t block_offset)
176 const void *base_addr = flash->f_base;
178 if (block_offset >= flash->f_size) {
179 return -EINVAL;
182 if (flash_protected(block_offset)) {
183 return -EPERM;
186 printf("Unlocking block at 0x%08lx, meaning %p\n",
187 block_offset, base_addr + block_offset);
189 flash_write_cmd(base_addr, CFI_CMD_PROTECT);
190 flash_write_cmd(base_addr + block_offset, CFI_PROT_UNLOCK);
191 flash_write_cmd(base_addr, CFI_CMD_RESET);
193 return 0;
196 __ramtext
197 int flash_block_lock(flash_t * flash, uint32_t block_offset)
199 const void *base_addr = flash->f_base;
201 if (block_offset >= flash->f_size) {
202 return -EINVAL;
205 printf("Locking block at 0x%08lx\n", block_offset);
207 flash_write_cmd(base_addr, CFI_CMD_PROTECT);
208 flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCK);
209 flash_write_cmd(base_addr, CFI_CMD_RESET);
211 return 0;
214 __ramtext
215 int flash_block_lockdown(flash_t * flash, uint32_t block_offset)
217 const void *base_addr = flash->f_base;
219 if (block_offset >= flash->f_size) {
220 return -EINVAL;
223 printf("Locking down block at 0x%08lx\n", block_offset);
225 flash_write_cmd(base_addr, CFI_CMD_PROTECT);
226 flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCKDOWN);
227 flash_write_cmd(base_addr, CFI_CMD_RESET);
229 return 0;
232 __ramtext
233 int flash_block_erase(flash_t * flash, uint32_t block_offset)
235 const void *base_addr = flash->f_base;
237 if (block_offset >= flash->f_size) {
238 return -EINVAL;
241 if (flash_protected(block_offset)) {
242 return -EPERM;
245 printf("Erasing block 0x%08lx...", block_offset);
247 void *block_addr = ((uint8_t *) base_addr) + block_offset;
249 flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
251 flash_write_cmd(block_addr, CFI_CMD_BLOCK_ERASE);
252 flash_write_cmd(block_addr, CFI_CMD_ERASE_CONFIRM);
254 flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
255 uint16_t status;
256 do {
257 status = flash_read16(base_addr, 0);
258 } while (!(status & CFI_STATUS_READY));
260 int res = 0;
261 if (status & CFI_STATUS_ERASE_ERROR) {
262 puts("error: ");
263 if (status & CFI_STATUS_VPP_LOW) {
264 puts("vpp insufficient\n");
265 res = -EFAULT;
266 } else if (status & CFI_STATUS_LOCKED_ERROR) {
267 puts("block is lock-protected\n");
268 res = -EPERM;
269 } else {
270 puts("unknown fault\n");
271 res = -EFAULT;
273 } else {
274 puts("done\n");
277 flash_write_cmd(base_addr, CFI_CMD_RESET);
279 return res;
283 __ramtext
284 int flash_program(flash_t * flash, uint32_t dst, void *src, uint32_t nbytes)
286 const void *base_addr = flash->f_base;
287 int res = 0;
288 uint32_t i;
290 /* check destination bounds */
291 if (dst >= flash->f_size) {
292 return -EINVAL;
294 if (dst + nbytes > flash->f_size) {
295 return -EINVAL;
298 /* check alignments */
299 if (((uint32_t) src) % 2) {
300 return -EINVAL;
302 if (dst % 2) {
303 return -EINVAL;
305 if (nbytes % 2) {
306 return -EINVAL;
309 /* check permissions */
310 if (flash_protected(dst)) {
311 return -EPERM;
314 /* say something */
315 printf("Programming %lu bytes to 0x%08lx from 0x%p...", nbytes, dst, src);
317 /* clear status register */
318 flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
320 /* write the words */
321 puts("writing...");
322 for (i = 0; i < nbytes; i += 2) {
323 uint16_t *src_addr = (uint16_t *) (src + i);
324 uint16_t *dst_addr = (uint16_t *) (base_addr + dst + i);
326 uint16_t data = *src_addr;
328 flash_write_cmd(dst_addr, CFI_CMD_WRITE);
329 flash_write_cmd(dst_addr, data);
331 flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
332 uint16_t status;
333 do {
334 status = flash_read16(base_addr, 0);
335 } while (!(status & CFI_STATUS_READY));
337 if (status & CFI_STATUS_PROGRAM_ERROR) {
338 puts("error: ");
339 if (status & CFI_STATUS_VPP_LOW) {
340 puts("vpp insufficient");
341 res = -EFAULT;
342 } else if (status & CFI_STATUS_LOCKED_ERROR) {
343 puts("block is lock-protected");
344 res = -EPERM;
345 } else {
346 puts("unknown fault");
347 res = -EFAULT;
349 goto err_reset;
353 flash_write_cmd(base_addr, CFI_CMD_RESET);
355 /* verify the result */
356 puts("verifying...");
357 for (i = 0; i < nbytes; i += 2) {
358 uint16_t *src_addr = (uint16_t *) (src + i);
359 uint16_t *dst_addr = (uint16_t *) (base_addr + dst + i);
360 if (*src_addr != *dst_addr) {
361 puts("error: verification failed");
362 res = -EFAULT;
363 goto err;
367 puts("done\n");
369 return res;
371 err_reset:
372 flash_write_cmd(base_addr, CFI_CMD_RESET);
374 err:
375 printf(" at offset 0x%lx\n", i);
377 return res;
380 /* retrieve manufacturer and extended device id from id space */
381 __ramtext
382 int flash_get_id(void *base_addr,
383 uint16_t * manufacturer_id, uint16_t * device_id)
385 flash_write_cmd(base_addr, CFI_CMD_RESET_TO_READ_MODE);
387 flash_write_cmd(base_addr + CFI_CMD_ADDR1, CFI_CMD_UNLOCK1);
388 flash_write_cmd(base_addr + CFI_CMD_ADDR2, CFI_CMD_UNLOCK2);
389 flash_write_cmd(base_addr + CFI_CMD_ADDR1, CFI_CMD_READ_ID);
391 if (manufacturer_id)
392 *manufacturer_id = flash_read16(base_addr, CFI_OFFSET_MANUFACTURER_ID);
394 if (device_id) {
395 device_id[0] = flash_read16(base_addr, CFI_OFFSET_DEVICE_ID);
396 device_id[1] = flash_read16(base_addr, CFI_OFFSET_EXT_DEVICE_ID1);
397 device_id[2] = flash_read16(base_addr, CFI_OFFSET_EXT_DEVICE_ID2);
400 flash_write_cmd(base_addr, CFI_CMD_RESET_TO_READ_MODE);
402 return 0;
405 /* Internal: retrieve cfi query response data */
406 __ramtext
407 static int get_query(void *base_addr, struct cfi_query *query)
409 int res = 0;
410 unsigned int i;
412 flash_write_cmd(base_addr, CFI_CMD_CFI);
414 for (i = 0; i < sizeof(struct cfi_query); i++) {
415 uint16_t byte =
416 flash_read16(base_addr, CFI_OFFSET_CFI_RESP + i);
417 *(((volatile unsigned char *)query) + i) = byte;
420 if (query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') {
421 res = -ENOENT;
424 flash_write_cmd(base_addr, CFI_CMD_RESET);
426 return res;
429 #if 0
431 /* Internal: retrieve intel protection data */
432 __ramtext
433 static int get_intel_protection(void *base_addr,
434 uint16_t * lockp, uint8_t protp[8])
436 int i;
438 /* check args */
439 if (!lockp) {
440 return -EINVAL;
442 if (!protp) {
443 return -EINVAL;
446 /* enter read id mode */
447 flash_write_cmd(base_addr, CFI_CMD_READ_ID);
449 /* get lock */
450 *lockp = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION);
452 /* get data */
453 for (i = 0; i < 8; i++) {
454 protp[i] = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION + 1 + i);
457 /* leave read id mode */
458 flash_write_cmd(base_addr, CFI_CMD_RESET);
460 return 0;
463 static void dump_intel_protection(uint16_t lock, uint8_t data[8])
465 printf
466 (" protection lock 0x%4.4x data 0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
467 lock, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
470 static void dump_query_algorithms(struct cfi_query *qry)
472 printf(" primary algorithm 0x%4.4x\n", qry->p_id);
473 printf(" primary extended query 0x%4.4x\n", qry->p_adr);
474 printf(" alternate algorithm 0x%4.4x\n", qry->a_id);
475 printf(" alternate extended query 0x%4.4x\n", qry->a_adr);
478 static void dump_query_timing(struct cfi_query *qry)
480 uint32_t block_erase_typ = 1 << qry->block_erase_timeout_typ;
481 uint32_t block_erase_max =
482 (1 << qry->block_erase_timeout_max) * block_erase_typ;
483 uint32_t word_program_typ = 1 << qry->word_write_timeout_typ;
484 uint32_t word_program_max =
485 (1 << qry->word_write_timeout_max) * word_program_typ;
486 printf(" block erase typ %u ms\n", block_erase_typ);
487 printf(" block erase max %u ms\n", block_erase_max);
488 printf(" word program typ %u us\n", word_program_typ);
489 printf(" word program max %u us\n", word_program_max);
492 void flash_dump_info(flash_t * flash)
494 int i;
495 printf("flash at 0x%p of %d bytes with %d regions\n",
496 flash->f_base, flash->f_size, flash->f_nregions);
498 uint16_t m_id, d_id;
499 if (get_id(flash->f_base, &m_id, &d_id)) {
500 puts(" failed to get id\n");
501 } else {
502 printf(" manufacturer 0x%4.4x device 0x%4.4x\n", m_id, d_id);
505 uint16_t plock;
506 uint8_t pdata[8];
507 if (get_intel_protection(flash->f_base, &plock, pdata)) {
508 puts(" failed to get protection data\n");
509 } else {
510 dump_intel_protection(plock, pdata);
513 struct cfi_query qry;
514 if (get_query(flash->f_base, &qry)) {
515 puts(" failed to get cfi query response\n");
516 } else {
517 dump_query_algorithms(&qry);
518 dump_query_timing(&qry);
521 for (i = 0; i < flash->f_nregions; i++) {
522 flash_region_t *fr = &flash->f_regions[i];
523 printf(" region %d: %d blocks of %d bytes at 0x%p\n",
524 i, fr->fr_bnum, fr->fr_bsize, fr->fr_base);
528 #endif
530 __ramtext
531 int flash_init(flash_t * flash, void *base_addr)
533 int res;
534 unsigned u;
535 uint16_t m_id, d_id[3];
536 uint32_t base;
537 struct cfi_query qry;
539 /* retrieve and check manufacturer and device id */
540 res = flash_get_id(base_addr, &m_id, d_id);
541 if (res) {
542 return res;
544 if (m_id != CFI_MANUF_INTEL && m_id != CFI_MANUF_ST) {
545 return -ENOTSUP;
548 /* retrieve and check query response */
549 res = get_query(base_addr, &qry);
550 if (res) {
551 return res;
553 if (qry.p_id != CFI_ALGO_INTEL_3) {
554 /* we only support algo 3 */
555 return -ENOTSUP;
557 if (qry.num_erase_regions > FLASH_MAX_REGIONS) {
558 /* we have a hard limit on the number of regions */
559 return -ENOTSUP;
562 /* fill in basic information */
563 flash->f_base = base_addr;
564 flash->f_size = 1 << qry.dev_size;
566 /* determine number of erase regions */
567 flash->f_nregions = qry.num_erase_regions;
569 /* compute actual erase region info from cfi junk */
570 base = 0;
571 for (u = 0; u < flash->f_nregions; u++) {
572 flash_region_t *fr = &flash->f_regions[u];
574 fr->fr_base = (void *)base;
575 fr->fr_bnum = qry.erase_regions[u].b_count + 1;
576 fr->fr_bsize = qry.erase_regions[u].b_size * 256;
578 base += fr->fr_bnum * fr->fr_bsize;
581 return 0;