diff options
Diffstat (limited to 'arch/arm/cpu/arm926ejs/at91/eflash.c')
| -rw-r--r-- | arch/arm/cpu/arm926ejs/at91/eflash.c | 271 | 
1 files changed, 271 insertions, 0 deletions
| diff --git a/arch/arm/cpu/arm926ejs/at91/eflash.c b/arch/arm/cpu/arm926ejs/at91/eflash.c new file mode 100644 index 000000000..2e851dbd2 --- /dev/null +++ b/arch/arm/cpu/arm926ejs/at91/eflash.c @@ -0,0 +1,271 @@ +/* + * (C) Copyright 2010 + * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * this driver supports the enhanced embedded flash in the Atmel + * AT91SAM9XE devices with the following geometry: + * + * AT91SAM9XE128: 1 plane of  8 regions of 32 pages (total  256 pages) + * AT91SAM9XE256: 1 plane of 16 regions of 32 pages (total  512 pages) + * AT91SAM9XE512: 1 plane of 32 regions of 32 pages (total 1024 pages) + * (the exact geometry is read from the flash at runtime, so any + *  future devices should already be covered) + * + * Regions can be write/erase protected. + * Whole (!) pages can be individually written with erase on the fly. + * Writing partial pages will corrupt the rest of the page. + * + * The flash is presented to u-boot with each region being a sector, + * having the following effects: + * Each sector can be hardware protected (protect on/off). + * Each page in a sector can be rewritten anytime. + * Since pages are erased when written, the "erase" does nothing. + * The first "CONFIG_EFLASH_PROTSECTORS" cannot be unprotected + * by u-Boot commands. + * + * Note: Redundant environment will not work in this flash since + * it does use partial page writes. Make sure the environent spans + * whole pages! + */ + +/* + * optional TODOs (nice to have features): + * + * make the driver coexist with other NOR flash drivers + *	(use an index into flash_info[], requires work + *	in those other drivers, too) + * Make the erase command fill the sectors with 0xff + *	(if the flashes grow larger in the future and + *	someone puts a jffs2 into them) + * do a read-modify-write for partially programmed pages + */ +#include <common.h> +#include <asm/arch/hardware.h> +#include <asm/arch/io.h> +#include <asm/arch/at91_common.h> +#include <asm/arch/at91_eefc.h> +#include <asm/arch/at91_dbu.h> + +/* checks to detect configuration errors */ +#if CONFIG_SYS_MAX_FLASH_BANKS!=1 +#error eflash: this driver can only handle 1 bank +#endif + +/* global structure */ +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; +static u32 pagesize; + +unsigned long flash_init (void) +{ +	at91_eefc_t *eefc = (at91_eefc_t *) 0xfffffa00; +	at91_dbu_t *dbu = (at91_dbu_t *) 0xfffff200; +	u32 id, size, nplanes, planesize, nlocks; +	u32 addr, i, tmp=0; + +	debug("eflash: init\n"); + +	flash_info[0].flash_id = FLASH_UNKNOWN; + +	/* check if its an AT91ARM9XE SoC */ +	if ((readl(&dbu->cidr) & AT91_DBU_CID_ARCH_MASK) != AT91_DBU_CID_ARCH_9XExx) { +		puts("eflash: not an AT91SAM9XE\n"); +		return 0; +	} + +	/* now query the eflash for its structure */ +	writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_GETD, &eefc->fcr); +	while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) +		; +	id = readl(&eefc->frr);		/* word 0 */ +	size = readl(&eefc->frr);	/* word 1 */ +	pagesize = readl(&eefc->frr);	/* word 2 */ +	nplanes = readl(&eefc->frr);	/* word 3 */ +	planesize = readl(&eefc->frr);	/* word 4 */ +	debug("id=%08x size=%u pagesize=%u planes=%u planesize=%u\n", +		id, size, pagesize, nplanes, planesize); +	for (i=1; i<nplanes; i++) { +		tmp = readl(&eefc->frr);	/* words 5..4+nplanes-1 */ +	}; +	nlocks = readl(&eefc->frr);	/* word 4+nplanes */ +	debug("nlocks=%u\n", nlocks); +	/* since we are going to use the lock regions as sectors, check count */ +	if (nlocks > CONFIG_SYS_MAX_FLASH_SECT) { +		printf("eflash: number of lock regions(%u) "\ +			"> CONFIG_SYS_MAX_FLASH_SECT. reducing...\n", +			nlocks); +		nlocks = CONFIG_SYS_MAX_FLASH_SECT; +	} +	flash_info[0].size = size; +	flash_info[0].sector_count = nlocks; +	flash_info[0].flash_id = id; + +	addr = AT91SAM9XE_FLASH_BASE; +	for (i=0; i<nlocks; i++) { +		tmp = readl(&eefc->frr);	/* words 4+nplanes+1.. */ +		flash_info[0].start[i] = addr; +		flash_info[0].protect[i] = 0; +		addr += tmp; +	}; + +	/* now read the protection information for all regions */ +	writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_GLB, &eefc->fcr); +	while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) +		; +	for (i=0; i<flash_info[0].sector_count; i++) { +		if (i%32 == 0) +			tmp = readl(&eefc->frr); +		flash_info[0].protect[i] = (tmp >> (i%32)) & 1; +#if defined(CONFIG_EFLASH_PROTSECTORS) +		if (i < CONFIG_EFLASH_PROTSECTORS) +			flash_info[0].protect[i] = 1; +#endif +	} + +	return size; +} + +void flash_print_info (flash_info_t *info) +{ +	int i; + +	puts("AT91SAM9XE embedded flash\n  Size: "); +	print_size(info->size, " in "); +	printf("%d Sectors\n", info->sector_count); + +	printf("  Sector Start Addresses:"); +	for (i=0; i<info->sector_count; ++i) { +		if ((i % 5) == 0) +			printf("\n   "); +		printf(" %08lX%s", +			info->start[i], +			info->protect[i] ? " (RO)" : "     " +		); +	} +	printf ("\n"); +	return; +} + +int flash_real_protect (flash_info_t *info, long sector, int prot) +{ +	at91_eefc_t *eefc = (at91_eefc_t *) 0xfffffa00; +	u32 pagenum = (info->start[sector]-AT91SAM9XE_FLASH_BASE)/pagesize; +	u32 i, tmp=0; + +	debug("protect sector=%ld prot=%d\n", sector, prot); + +#if defined(CONFIG_EFLASH_PROTSECTORS) +	if (sector < CONFIG_EFLASH_PROTSECTORS) { +		if (!prot) { +			printf("eflash: sector %lu cannot be unprotected\n", +				sector); +		} +		return 1; /* return anyway, caller does not care for result */ +	} +#endif +	if (prot) { +		writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_SLB | +			(pagenum << AT91_EEFC_FCR_FARG_SHIFT), &eefc->fcr); +	} else { +		writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_CLB | +			(pagenum << AT91_EEFC_FCR_FARG_SHIFT), &eefc->fcr); +	} +	while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) +		; +	/* now re-read the protection information for all regions */ +	writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_GLB, &eefc->fcr); +	while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) +		; +	for (i=0; i<info->sector_count; i++) { +		if (i%32 == 0) +			tmp = readl(&eefc->frr); +		info->protect[i] = (tmp >> (i%32)) & 1; +	} +	return 0; +} + +static u32 erase_write_page (u32 pagenum) +{ +	at91_eefc_t *eefc = (at91_eefc_t *) 0xfffffa00; + +	debug("erase+write page=%u\n", pagenum); + +	/* give erase and write page command */ +	writel(AT91_EEFC_FCR_KEY | AT91_EEFC_FCR_FCMD_EWP | +		(pagenum << AT91_EEFC_FCR_FARG_SHIFT), &eefc->fcr); +	while ((readl(&eefc->fsr) & AT91_EEFC_FSR_FRDY) == 0) +		; +	/* return status */ +	return readl(&eefc->fsr) +		& (AT91_EEFC_FSR_FCMDE | AT91_EEFC_FSR_FLOCKE); +} + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ +	debug("erase first=%d last=%d\n", s_first, s_last); +	puts("this flash does not need and support erasing!\n"); +	return 0; +} + +/* + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ +	u32 pagenum; +	u32 *src32, *dst32; +	u32 i; + +	debug("write src=%08lx addr=%08lx cnt=%lx\n", +		(ulong)src, addr, cnt); + +	/* REQUIRE addr to be on a page start, abort if not */ +	if (addr % pagesize) { +		printf ("eflash: start %08lx is not on page start\n"\ +			"        write aborted\n", addr); +		return 1; +	} + +	/* now start copying data */ +	pagenum = (addr-AT91SAM9XE_FLASH_BASE)/pagesize; +	src32 = (u32 *) src; +	dst32 = (u32 *) addr; +	while (cnt > 0) { +		i = pagesize / 4; +		/* fill page buffer */ +		while (i--) +			*dst32++ = *src32++; +		/* write page */ +		if (erase_write_page(pagenum)) +			return 1; +		pagenum++; +		if (cnt > pagesize) +			cnt -= pagesize; +		else +			cnt = 0; +	} +	return 0; +} + |