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