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