blob: 3f3926428919b487c9ab0795bc61df1a2bd202a5 [file] [log] [blame]
Reinhard Meyerd88bebe2010-07-27 15:18:38 +02001/*
2 * (C) Copyright 2010
3 * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de
4 *
Wolfgang Denk1a459662013-07-08 09:37:19 +02005 * SPDX-License-Identifier: GPL-2.0+
Reinhard Meyerd88bebe2010-07-27 15:18:38 +02006 */
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
Robert P. J. Day1bce2ae2013-09-16 07:15:45 -040031 * it does use partial page writes. Make sure the environment spans
Reinhard Meyerd88bebe2010-07-27 15:18:38 +020032 * 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>
Reinhard Meyer86592f62010-11-07 13:26:14 +010047#include <asm/io.h>
Reinhard Meyerd88bebe2010-07-27 15:18:38 +020048#include <asm/arch/hardware.h>
Reinhard Meyerd88bebe2010-07-27 15:18:38 +020049#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 */
59flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
60static u32 pagesize;
61
62unsigned long flash_init (void)
63{
Reinhard Meyer9f3fe902010-11-03 15:39:55 +010064 at91_eefc_t *eefc = (at91_eefc_t *) ATMEL_BASE_EEFC;
65 at91_dbu_t *dbu = (at91_dbu_t *) ATMEL_BASE_DBGU;
Reinhard Meyerd88bebe2010-07-27 15:18:38 +020066 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
Reinhard Meyer9f3fe902010-11-03 15:39:55 +0100106 addr = ATMEL_BASE_FLASH;
Reinhard Meyerd88bebe2010-07-27 15:18:38 +0200107 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
131void 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
152int flash_real_protect (flash_info_t *info, long sector, int prot)
153{
Reinhard Meyer9f3fe902010-11-03 15:39:55 +0100154 at91_eefc_t *eefc = (at91_eefc_t *) ATMEL_BASE_EEFC;
155 u32 pagenum = (info->start[sector]-ATMEL_BASE_FLASH)/pagesize;
Reinhard Meyerd88bebe2010-07-27 15:18:38 +0200156 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
190static u32 erase_write_page (u32 pagenum)
191{
Reinhard Meyer9f3fe902010-11-03 15:39:55 +0100192 at91_eefc_t *eefc = (at91_eefc_t *) ATMEL_BASE_EEFC;
Reinhard Meyerd88bebe2010-07-27 15:18:38 +0200193
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
206int 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
219int 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 */
Reinhard Meyer9f3fe902010-11-03 15:39:55 +0100236 pagenum = (addr-ATMEL_BASE_FLASH)/pagesize;
Reinhard Meyerd88bebe2010-07-27 15:18:38 +0200237 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}