]>
Commit | Line | Data |
---|---|---|
d88bebe1 RM |
1 | /* |
2 | * (C) Copyright 2010 | |
3 | * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
d88bebe1 RM |
6 | */ |
7 | ||
8 | /* | |
9 | * this driver supports the enhanced embedded flash in the Atmel | |
10 | * AT91SAM9XE devices with the following geometry: | |
11 | * | |
12 | * AT91SAM9XE128: 1 plane of 8 regions of 32 pages (total 256 pages) | |
13 | * AT91SAM9XE256: 1 plane of 16 regions of 32 pages (total 512 pages) | |
14 | * AT91SAM9XE512: 1 plane of 32 regions of 32 pages (total 1024 pages) | |
15 | * (the exact geometry is read from the flash at runtime, so any | |
16 | * future devices should already be covered) | |
17 | * | |
18 | * Regions can be write/erase protected. | |
19 | * Whole (!) pages can be individually written with erase on the fly. | |
20 | * Writing partial pages will corrupt the rest of the page. | |
21 | * | |
22 | * The flash is presented to u-boot with each region being a sector, | |
23 | * having the following effects: | |
24 | * Each sector can be hardware protected (protect on/off). | |
25 | * Each page in a sector can be rewritten anytime. | |
26 | * Since pages are erased when written, the "erase" does nothing. | |
27 | * The first "CONFIG_EFLASH_PROTSECTORS" cannot be unprotected | |
28 | * by u-Boot commands. | |
29 | * | |
30 | * Note: Redundant environment will not work in this flash since | |
31 | * it does use partial page writes. Make sure the environent spans | |
32 | * whole pages! | |
33 | */ | |
34 | ||
35 | /* | |
36 | * optional TODOs (nice to have features): | |
37 | * | |
38 | * make the driver coexist with other NOR flash drivers | |
39 | * (use an index into flash_info[], requires work | |
40 | * in those other drivers, too) | |
41 | * Make the erase command fill the sectors with 0xff | |
42 | * (if the flashes grow larger in the future and | |
43 | * someone puts a jffs2 into them) | |
44 | * do a read-modify-write for partially programmed pages | |
45 | */ | |
46 | #include <common.h> | |
86592f60 | 47 | #include <asm/io.h> |
d88bebe1 | 48 | #include <asm/arch/hardware.h> |
d88bebe1 RM |
49 | #include <asm/arch/at91_common.h> |
50 | #include <asm/arch/at91_eefc.h> | |
51 | #include <asm/arch/at91_dbu.h> | |
52 | ||
53 | /* checks to detect configuration errors */ | |
54 | #if CONFIG_SYS_MAX_FLASH_BANKS!=1 | |
55 | #error eflash: this driver can only handle 1 bank | |
56 | #endif | |
57 | ||
58 | /* global structure */ | |
59 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; | |
60 | static u32 pagesize; | |
61 | ||
62 | unsigned long flash_init (void) | |
63 | { | |
9f3fe90f RM |
64 | at91_eefc_t *eefc = (at91_eefc_t *) ATMEL_BASE_EEFC; |
65 | at91_dbu_t *dbu = (at91_dbu_t *) ATMEL_BASE_DBGU; | |
d88bebe1 RM |
66 | u32 id, size, nplanes, planesize, nlocks; |
67 | u32 addr, i, tmp=0; | |
68 | ||
69 | debug("eflash: init\n"); | |
70 | ||
71 | flash_info[0].flash_id = FLASH_UNKNOWN; | |
72 | ||
73 | /* check if its an AT91ARM9XE SoC */ | |
74 | if ((readl(&dbu->cidr) & AT91_DBU_CID_ARCH_MASK) != AT91_DBU_CID_ARCH_9XExx) { | |
75 | puts("eflash: not an AT91SAM9XE\n"); | |
76 | return 0; | |
77 | } | |
78 | ||
79 | /* now query the eflash for its structure */ | |
80 | writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_GETD, &eefc->fcr); | |
81 | while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) | |
82 | ; | |
83 | id = readl(&eefc->frr); /* word 0 */ | |
84 | size = readl(&eefc->frr); /* word 1 */ | |
85 | pagesize = readl(&eefc->frr); /* word 2 */ | |
86 | nplanes = readl(&eefc->frr); /* word 3 */ | |
87 | planesize = readl(&eefc->frr); /* word 4 */ | |
88 | debug("id=%08x size=%u pagesize=%u planes=%u planesize=%u\n", | |
89 | id, size, pagesize, nplanes, planesize); | |
90 | for (i=1; i<nplanes; i++) { | |
91 | tmp = readl(&eefc->frr); /* words 5..4+nplanes-1 */ | |
92 | }; | |
93 | nlocks = readl(&eefc->frr); /* word 4+nplanes */ | |
94 | debug("nlocks=%u\n", nlocks); | |
95 | /* since we are going to use the lock regions as sectors, check count */ | |
96 | if (nlocks > CONFIG_SYS_MAX_FLASH_SECT) { | |
97 | printf("eflash: number of lock regions(%u) "\ | |
98 | "> CONFIG_SYS_MAX_FLASH_SECT. reducing...\n", | |
99 | nlocks); | |
100 | nlocks = CONFIG_SYS_MAX_FLASH_SECT; | |
101 | } | |
102 | flash_info[0].size = size; | |
103 | flash_info[0].sector_count = nlocks; | |
104 | flash_info[0].flash_id = id; | |
105 | ||
9f3fe90f | 106 | addr = ATMEL_BASE_FLASH; |
d88bebe1 RM |
107 | for (i=0; i<nlocks; i++) { |
108 | tmp = readl(&eefc->frr); /* words 4+nplanes+1.. */ | |
109 | flash_info[0].start[i] = addr; | |
110 | flash_info[0].protect[i] = 0; | |
111 | addr += tmp; | |
112 | }; | |
113 | ||
114 | /* now read the protection information for all regions */ | |
115 | writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_GLB, &eefc->fcr); | |
116 | while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) | |
117 | ; | |
118 | for (i=0; i<flash_info[0].sector_count; i++) { | |
119 | if (i%32 == 0) | |
120 | tmp = readl(&eefc->frr); | |
121 | flash_info[0].protect[i] = (tmp >> (i%32)) & 1; | |
122 | #if defined(CONFIG_EFLASH_PROTSECTORS) | |
123 | if (i < CONFIG_EFLASH_PROTSECTORS) | |
124 | flash_info[0].protect[i] = 1; | |
125 | #endif | |
126 | } | |
127 | ||
128 | return size; | |
129 | } | |
130 | ||
131 | void flash_print_info (flash_info_t *info) | |
132 | { | |
133 | int i; | |
134 | ||
135 | puts("AT91SAM9XE embedded flash\n Size: "); | |
136 | print_size(info->size, " in "); | |
137 | printf("%d Sectors\n", info->sector_count); | |
138 | ||
139 | printf(" Sector Start Addresses:"); | |
140 | for (i=0; i<info->sector_count; ++i) { | |
141 | if ((i % 5) == 0) | |
142 | printf("\n "); | |
143 | printf(" %08lX%s", | |
144 | info->start[i], | |
145 | info->protect[i] ? " (RO)" : " " | |
146 | ); | |
147 | } | |
148 | printf ("\n"); | |
149 | return; | |
150 | } | |
151 | ||
152 | int flash_real_protect (flash_info_t *info, long sector, int prot) | |
153 | { | |
9f3fe90f RM |
154 | at91_eefc_t *eefc = (at91_eefc_t *) ATMEL_BASE_EEFC; |
155 | u32 pagenum = (info->start[sector]-ATMEL_BASE_FLASH)/pagesize; | |
d88bebe1 RM |
156 | u32 i, tmp=0; |
157 | ||
158 | debug("protect sector=%ld prot=%d\n", sector, prot); | |
159 | ||
160 | #if defined(CONFIG_EFLASH_PROTSECTORS) | |
161 | if (sector < CONFIG_EFLASH_PROTSECTORS) { | |
162 | if (!prot) { | |
163 | printf("eflash: sector %lu cannot be unprotected\n", | |
164 | sector); | |
165 | } | |
166 | return 1; /* return anyway, caller does not care for result */ | |
167 | } | |
168 | #endif | |
169 | if (prot) { | |
170 | writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_SLB | | |
171 | (pagenum << AT91_EEFC_FCR_FARG_SHIFT), &eefc->fcr); | |
172 | } else { | |
173 | writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_CLB | | |
174 | (pagenum << AT91_EEFC_FCR_FARG_SHIFT), &eefc->fcr); | |
175 | } | |
176 | while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) | |
177 | ; | |
178 | /* now re-read the protection information for all regions */ | |
179 | writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_GLB, &eefc->fcr); | |
180 | while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) | |
181 | ; | |
182 | for (i=0; i<info->sector_count; i++) { | |
183 | if (i%32 == 0) | |
184 | tmp = readl(&eefc->frr); | |
185 | info->protect[i] = (tmp >> (i%32)) & 1; | |
186 | } | |
187 | return 0; | |
188 | } | |
189 | ||
190 | static u32 erase_write_page (u32 pagenum) | |
191 | { | |
9f3fe90f | 192 | at91_eefc_t *eefc = (at91_eefc_t *) ATMEL_BASE_EEFC; |
d88bebe1 RM |
193 | |
194 | debug("erase+write page=%u\n", pagenum); | |
195 | ||
196 | /* give erase and write page command */ | |
197 | writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_EWP | | |
198 | (pagenum << AT91_EEFC_FCR_FARG_SHIFT), &eefc->fcr); | |
199 | while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) | |
200 | ; | |
201 | /* return status */ | |
202 | return readl(&eefc->fsr) | |
203 | & (AT91_EEFC_FSR_FCMDE | AT91_EEFC_FSR_FLOCKE); | |
204 | } | |
205 | ||
206 | int flash_erase (flash_info_t *info, int s_first, int s_last) | |
207 | { | |
208 | debug("erase first=%d last=%d\n", s_first, s_last); | |
209 | puts("this flash does not need and support erasing!\n"); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | /* | |
214 | * Copy memory to flash, returns: | |
215 | * 0 - OK | |
216 | * 1 - write timeout | |
217 | */ | |
218 | ||
219 | int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) | |
220 | { | |
221 | u32 pagenum; | |
222 | u32 *src32, *dst32; | |
223 | u32 i; | |
224 | ||
225 | debug("write src=%08lx addr=%08lx cnt=%lx\n", | |
226 | (ulong)src, addr, cnt); | |
227 | ||
228 | /* REQUIRE addr to be on a page start, abort if not */ | |
229 | if (addr % pagesize) { | |
230 | printf ("eflash: start %08lx is not on page start\n"\ | |
231 | " write aborted\n", addr); | |
232 | return 1; | |
233 | } | |
234 | ||
235 | /* now start copying data */ | |
9f3fe90f | 236 | pagenum = (addr-ATMEL_BASE_FLASH)/pagesize; |
d88bebe1 RM |
237 | src32 = (u32 *) src; |
238 | dst32 = (u32 *) addr; | |
239 | while (cnt > 0) { | |
240 | i = pagesize / 4; | |
241 | /* fill page buffer */ | |
242 | while (i--) | |
243 | *dst32++ = *src32++; | |
244 | /* write page */ | |
245 | if (erase_write_page(pagenum)) | |
246 | return 1; | |
247 | pagenum++; | |
248 | if (cnt > pagesize) | |
249 | cnt -= pagesize; | |
250 | else | |
251 | cnt = 0; | |
252 | } | |
253 | return 0; | |
254 | } |