]>
Commit | Line | Data |
---|---|---|
a6e34f76 VK |
1 | /* |
2 | * (C) Copyright 2009 | |
3 | * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. | |
4 | * | |
5 | * See file CREDITS for list of people who contributed to this | |
6 | * project. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation; either version 2 of | |
11 | * the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | * MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | #include <common.h> | |
25 | #include <flash.h> | |
26 | #include <linux/err.h> | |
27 | ||
28 | #include <asm/io.h> | |
29 | #include <asm/arch/hardware.h> | |
30 | #include <asm/arch/spr_smi.h> | |
31 | ||
32 | #if !defined(CONFIG_SYS_NO_FLASH) | |
33 | ||
34 | static struct smi_regs *const smicntl = | |
35 | (struct smi_regs * const)CONFIG_SYS_SMI_BASE; | |
36 | static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = | |
37 | CONFIG_SYS_FLASH_ADDR_BASE; | |
38 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; | |
39 | ||
40 | #define ST_M25Pxx_ID 0x00002020 | |
41 | ||
42 | static struct flash_dev flash_ids[] = { | |
43 | {0x10, 0x10000, 2}, /* 64K Byte */ | |
44 | {0x11, 0x20000, 4}, /* 128K Byte */ | |
45 | {0x12, 0x40000, 4}, /* 256K Byte */ | |
46 | {0x13, 0x80000, 8}, /* 512K Byte */ | |
47 | {0x14, 0x100000, 16}, /* 1M Byte */ | |
48 | {0x15, 0x200000, 32}, /* 2M Byte */ | |
49 | {0x16, 0x400000, 64}, /* 4M Byte */ | |
50 | {0x17, 0x800000, 128}, /* 8M Byte */ | |
51 | {0x18, 0x1000000, 64}, /* 16M Byte */ | |
52 | {0x00,} | |
53 | }; | |
54 | ||
55 | /* | |
56 | * smi_wait_xfer_finish - Wait until TFF is set in status register | |
57 | * @timeout: timeout in milliseconds | |
58 | * | |
59 | * Wait until TFF is set in status register | |
60 | */ | |
61 | static void smi_wait_xfer_finish(int timeout) | |
62 | { | |
63 | while (timeout--) { | |
64 | if (readl(&smicntl->smi_sr) & TFF) | |
65 | break; | |
66 | udelay(1000); | |
67 | } | |
68 | } | |
69 | ||
70 | /* | |
71 | * smi_read_id - Read flash id | |
72 | * @info: flash_info structure pointer | |
73 | * @banknum: bank number | |
74 | * | |
75 | * Read the flash id present at bank #banknum | |
76 | */ | |
77 | static unsigned int smi_read_id(flash_info_t *info, int banknum) | |
78 | { | |
79 | unsigned int value; | |
80 | ||
81 | writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); | |
82 | writel(READ_ID, &smicntl->smi_tr); | |
83 | writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, | |
84 | &smicntl->smi_cr2); | |
85 | smi_wait_xfer_finish(XFER_FINISH_TOUT); | |
86 | ||
87 | value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); | |
88 | ||
89 | writel(readl(&smicntl->smi_sr) & ~TFF, &smicntl->smi_sr); | |
90 | writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); | |
91 | ||
92 | return value; | |
93 | } | |
94 | ||
95 | /* | |
96 | * flash_get_size - Detect the SMI flash by reading the ID. | |
97 | * @base: Base address of the flash area bank #banknum | |
98 | * @banknum: Bank number | |
99 | * | |
100 | * Detect the SMI flash by reading the ID. Initializes the flash_info structure | |
101 | * with size, sector count etc. | |
102 | */ | |
103 | static ulong flash_get_size(ulong base, int banknum) | |
104 | { | |
105 | flash_info_t *info = &flash_info[banknum]; | |
106 | struct flash_dev *dev; | |
107 | unsigned int value; | |
108 | unsigned int density; | |
109 | int i; | |
110 | ||
111 | value = smi_read_id(info, banknum); | |
112 | density = (value >> 16) & 0xff; | |
113 | ||
114 | for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; | |
115 | i++, dev = &flash_ids[i]) { | |
116 | if (dev->density == density) { | |
117 | info->size = dev->size; | |
118 | info->sector_count = dev->sector_count; | |
119 | break; | |
120 | } | |
121 | } | |
122 | ||
123 | if (dev->density == 0x0) | |
124 | return 0; | |
125 | ||
126 | info->flash_id = value & 0xffff; | |
127 | info->start[0] = base; | |
128 | ||
129 | return info->size; | |
130 | } | |
131 | ||
132 | /* | |
133 | * smi_read_sr - Read status register of SMI | |
134 | * @bank: bank number | |
135 | * | |
136 | * This routine will get the status register of the flash chip present at the | |
137 | * given bank | |
138 | */ | |
139 | static unsigned int smi_read_sr(int bank) | |
140 | { | |
141 | u32 ctrlreg1; | |
142 | ||
143 | /* store the CTRL REG1 state */ | |
144 | ctrlreg1 = readl(&smicntl->smi_cr1); | |
145 | ||
146 | /* Program SMI in HW Mode */ | |
147 | writel(readl(&smicntl->smi_cr1) & ~(SW_MODE | WB_MODE), | |
148 | &smicntl->smi_cr1); | |
149 | ||
150 | /* Performing a RSR instruction in HW mode */ | |
151 | writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); | |
152 | ||
153 | smi_wait_xfer_finish(XFER_FINISH_TOUT); | |
154 | ||
155 | /* Restore the CTRL REG1 state */ | |
156 | writel(ctrlreg1, &smicntl->smi_cr1); | |
157 | ||
158 | return readl(&smicntl->smi_sr); | |
159 | } | |
160 | ||
161 | /* | |
162 | * smi_wait_till_ready - Wait till last operation is over. | |
163 | * @bank: bank number shifted. | |
164 | * @timeout: timeout in milliseconds. | |
165 | * | |
166 | * This routine checks for WIP(write in progress)bit in Status register(SMSR-b0) | |
167 | * The routine checks for #timeout loops, each at interval of 1 milli-second. | |
168 | * If successful the routine returns 0. | |
169 | */ | |
170 | static int smi_wait_till_ready(int bank, int timeout) | |
171 | { | |
172 | int count; | |
173 | unsigned int sr; | |
174 | ||
175 | /* One chip guarantees max 5 msec wait here after page writes, | |
176 | but potentially three seconds (!) after page erase. */ | |
177 | for (count = 0; count < timeout; count++) { | |
178 | ||
179 | sr = smi_read_sr(bank); | |
180 | if (sr < 0) | |
181 | break; | |
182 | else if (!(sr & WIP_BIT)) | |
183 | return 0; | |
184 | ||
185 | /* Try again after 1m-sec */ | |
186 | udelay(1000); | |
187 | } | |
188 | printf("SMI controller is still in wait, timeout=%d\n", timeout); | |
189 | return -EIO; | |
190 | } | |
191 | ||
192 | /* | |
193 | * smi_write_enable - Enable the flash to do write operation | |
194 | * @bank: bank number | |
195 | * | |
196 | * Set write enable latch with Write Enable command. | |
197 | * Returns negative if error occurred. | |
198 | */ | |
199 | static int smi_write_enable(int bank) | |
200 | { | |
201 | u32 ctrlreg1; | |
202 | int timeout = WMODE_TOUT; | |
203 | ||
204 | /* Store the CTRL REG1 state */ | |
205 | ctrlreg1 = readl(&smicntl->smi_cr1); | |
206 | ||
207 | /* Program SMI in H/W Mode */ | |
208 | writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); | |
209 | ||
210 | /* Give the Flash, Write Enable command */ | |
211 | writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); | |
212 | ||
213 | smi_wait_xfer_finish(XFER_FINISH_TOUT); | |
214 | ||
215 | /* Restore the CTRL REG1 state */ | |
216 | writel(ctrlreg1, &smicntl->smi_cr1); | |
217 | ||
218 | while (timeout--) { | |
219 | if (smi_read_sr(bank) & (1 << (bank + WM_SHIFT))) | |
220 | break; | |
221 | udelay(1000); | |
222 | } | |
223 | ||
224 | if (timeout) | |
225 | return 0; | |
226 | ||
227 | return -1; | |
228 | } | |
229 | ||
230 | /* | |
231 | * smi_init - SMI initialization routine | |
232 | * | |
233 | * SMI initialization routine. Sets SMI control register1. | |
234 | */ | |
235 | static void smi_init(void) | |
236 | { | |
237 | /* Setting the fast mode values. SMI working at 166/4 = 41.5 MHz */ | |
238 | writel(HOLD1 | FAST_MODE | BANK_EN | DSEL_TIME | PRESCAL4, | |
239 | &smicntl->smi_cr1); | |
240 | } | |
241 | ||
242 | /* | |
243 | * smi_sector_erase - Erase flash sector | |
244 | * @info: flash_info structure pointer | |
245 | * @sector: sector number | |
246 | * | |
247 | * Set write enable latch with Write Enable command. | |
248 | * Returns negative if error occurred. | |
249 | */ | |
250 | static int smi_sector_erase(flash_info_t *info, unsigned int sector) | |
251 | { | |
252 | int bank; | |
253 | unsigned int sect_add; | |
254 | unsigned int instruction; | |
255 | ||
256 | switch (info->start[0]) { | |
257 | case SMIBANK0_BASE: | |
258 | bank = BANK0; | |
259 | break; | |
260 | case SMIBANK1_BASE: | |
261 | bank = BANK1; | |
262 | break; | |
263 | case SMIBANK2_BASE: | |
264 | bank = BANK2; | |
265 | break; | |
266 | case SMIBANK3_BASE: | |
267 | bank = BANK3; | |
268 | break; | |
269 | default: | |
270 | return -1; | |
271 | } | |
272 | ||
273 | sect_add = sector * (info->size / info->sector_count); | |
274 | instruction = ((sect_add >> 8) & 0x0000FF00) | SECTOR_ERASE; | |
275 | ||
276 | writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); | |
277 | ||
278 | if (info->flash_id == ST_M25Pxx_ID) { | |
279 | /* Wait until finished previous write command. */ | |
280 | if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) | |
281 | return -EBUSY; | |
282 | ||
283 | /* Send write enable, before erase commands. */ | |
284 | if (smi_write_enable(bank)) | |
285 | return -EIO; | |
286 | ||
287 | /* Put SMI in SW mode */ | |
288 | writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); | |
289 | ||
290 | /* Send Sector Erase command in SW Mode */ | |
291 | writel(instruction, &smicntl->smi_tr); | |
292 | writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, | |
293 | &smicntl->smi_cr2); | |
294 | smi_wait_xfer_finish(XFER_FINISH_TOUT); | |
295 | ||
296 | if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) | |
297 | return -EBUSY; | |
298 | ||
299 | /* Put SMI in HW mode */ | |
300 | writel(readl(&smicntl->smi_cr1) & ~SW_MODE, | |
301 | &smicntl->smi_cr1); | |
302 | ||
303 | return 0; | |
304 | } else { | |
305 | /* Put SMI in HW mode */ | |
306 | writel(readl(&smicntl->smi_cr1) & ~SW_MODE, | |
307 | &smicntl->smi_cr1); | |
308 | return -EINVAL; | |
309 | } | |
310 | } | |
311 | ||
312 | /* | |
313 | * smi_write - Write to SMI flash | |
314 | * @src_addr: source buffer | |
315 | * @dst_addr: destination buffer | |
316 | * @length: length to write in words | |
317 | * @bank: bank base address | |
318 | * | |
319 | * Write to SMI flash | |
320 | */ | |
321 | static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, | |
322 | unsigned int length, ulong bank_addr) | |
323 | { | |
324 | int banknum; | |
a6e34f76 VK |
325 | |
326 | switch (bank_addr) { | |
327 | case SMIBANK0_BASE: | |
328 | banknum = BANK0; | |
a6e34f76 VK |
329 | break; |
330 | case SMIBANK1_BASE: | |
331 | banknum = BANK1; | |
a6e34f76 VK |
332 | break; |
333 | case SMIBANK2_BASE: | |
334 | banknum = BANK2; | |
a6e34f76 VK |
335 | break; |
336 | case SMIBANK3_BASE: | |
337 | banknum = BANK3; | |
a6e34f76 VK |
338 | break; |
339 | default: | |
340 | return -1; | |
341 | } | |
342 | ||
343 | if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) | |
344 | return -EBUSY; | |
345 | ||
346 | /* Set SMI in Hardware Mode */ | |
347 | writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); | |
348 | ||
349 | if (smi_write_enable(banknum)) | |
350 | return -EIO; | |
351 | ||
352 | /* Perform the write command */ | |
353 | while (length--) { | |
354 | if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { | |
355 | if (smi_wait_till_ready(banknum, | |
356 | CONFIG_SYS_FLASH_WRITE_TOUT)) | |
357 | return -EBUSY; | |
358 | ||
359 | if (smi_write_enable(banknum)) | |
360 | return -EIO; | |
361 | } | |
362 | ||
363 | *dst_addr++ = *src_addr++; | |
364 | ||
365 | if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) | |
366 | return -EIO; | |
367 | } | |
368 | ||
369 | if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) | |
370 | return -EBUSY; | |
371 | ||
372 | writel(readl(&smicntl->smi_sr) & ~(WCF), &smicntl->smi_sr); | |
373 | ||
374 | return 0; | |
375 | } | |
376 | ||
377 | /* | |
378 | * write_buff - Write to SMI flash | |
379 | * @info: flash info structure | |
380 | * @src: source buffer | |
381 | * @dest_addr: destination buffer | |
382 | * @length: length to write in words | |
383 | * | |
384 | * Write to SMI flash | |
385 | */ | |
386 | int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) | |
387 | { | |
388 | return smi_write((unsigned int *)src, (unsigned int *)dest_addr, | |
389 | (length + 3) / 4, info->start[0]); | |
390 | } | |
391 | ||
392 | /* | |
393 | * flash_init - SMI flash initialization | |
394 | * | |
395 | * SMI flash initialization | |
396 | */ | |
397 | unsigned long flash_init(void) | |
398 | { | |
399 | unsigned long size = 0; | |
400 | int i, j; | |
401 | ||
402 | smi_init(); | |
403 | ||
404 | for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { | |
405 | flash_info[i].flash_id = FLASH_UNKNOWN; | |
406 | size += flash_info[i].size = flash_get_size(bank_base[i], i); | |
407 | } | |
408 | ||
409 | for (j = 0; j < CONFIG_SYS_MAX_FLASH_BANKS; j++) { | |
410 | for (i = 1; i < flash_info[j].sector_count; i++) | |
411 | flash_info[j].start[i] = | |
412 | flash_info[j].start[i - 1] + | |
413 | flash_info->size / flash_info->sector_count; | |
414 | ||
415 | } | |
416 | ||
417 | return size; | |
418 | } | |
419 | ||
420 | /* | |
421 | * flash_print_info - Print SMI flash information | |
422 | * | |
423 | * Print SMI flash information | |
424 | */ | |
425 | void flash_print_info(flash_info_t *info) | |
426 | { | |
427 | int i; | |
428 | if (info->flash_id == FLASH_UNKNOWN) { | |
429 | puts("missing or unknown FLASH type\n"); | |
430 | return; | |
431 | } | |
432 | printf(" Size: %ld MB in %d Sectors\n", | |
433 | info->size >> 20, info->sector_count); | |
434 | ||
435 | puts(" Sector Start Addresses:"); | |
436 | for (i = 0; i < info->sector_count; ++i) { | |
437 | #ifdef CONFIG_SYS_FLASH_EMPTY_INFO | |
438 | int size; | |
439 | int erased; | |
440 | u32 *flash; | |
441 | ||
442 | /* | |
443 | * Check if whole sector is erased | |
444 | */ | |
445 | size = (info->size) / (info->sector_count); | |
446 | flash = (u32 *) info->start[i]; | |
447 | size = size / sizeof(int); | |
448 | ||
449 | while ((size--) && (*flash++ == ~0)) | |
450 | ; | |
451 | ||
452 | size++; | |
453 | if (size) | |
454 | erased = 0; | |
455 | else | |
456 | erased = 1; | |
457 | ||
458 | if ((i % 5) == 0) | |
459 | printf("\n"); | |
460 | ||
461 | printf(" %08lX%s%s", | |
462 | info->start[i], | |
463 | erased ? " E" : " ", info->protect[i] ? "RO " : " "); | |
464 | #else | |
465 | if ((i % 5) == 0) | |
466 | printf("\n "); | |
467 | printf(" %08lX%s", | |
468 | info->start[i], info->protect[i] ? " (RO) " : " "); | |
469 | #endif | |
470 | } | |
471 | putc('\n'); | |
472 | return; | |
473 | } | |
474 | ||
475 | /* | |
476 | * flash_erase - Erase SMI flash | |
477 | * | |
478 | * Erase SMI flash | |
479 | */ | |
480 | int flash_erase(flash_info_t *info, int s_first, int s_last) | |
481 | { | |
482 | int rcode = 0; | |
483 | int prot = 0; | |
484 | flash_sect_t sect; | |
485 | ||
486 | if (info->flash_id != ST_M25Pxx_ID) { | |
487 | puts("Can't erase unknown flash type - aborted\n"); | |
488 | return 1; | |
489 | } | |
490 | ||
491 | if ((s_first < 0) || (s_first > s_last)) { | |
492 | puts("- no sectors to erase\n"); | |
493 | return 1; | |
494 | } | |
495 | ||
496 | for (sect = s_first; sect <= s_last; ++sect) { | |
497 | if (info->protect[sect]) | |
498 | prot++; | |
499 | } | |
500 | if (prot) { | |
501 | printf("- Warning: %d protected sectors will not be erased!\n", | |
502 | prot); | |
503 | } else { | |
504 | putc('\n'); | |
505 | } | |
506 | ||
507 | for (sect = s_first; sect <= s_last; sect++) { | |
508 | if (info->protect[sect] == 0) { | |
509 | if (smi_sector_erase(info, sect)) | |
510 | rcode = 1; | |
511 | else | |
512 | putc('.'); | |
513 | } | |
514 | } | |
515 | puts(" done\n"); | |
516 | return rcode; | |
517 | } | |
518 | #endif |