255 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * (C) Copyright 2010
 | 
						|
 * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier:	GPL-2.0+
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 environment 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/io.h>
 | 
						|
#include <asm/arch/hardware.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 *) ATMEL_BASE_EEFC;
 | 
						|
	at91_dbu_t *dbu = (at91_dbu_t *) ATMEL_BASE_DBGU;
 | 
						|
	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 = ATMEL_BASE_FLASH;
 | 
						|
	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 *) ATMEL_BASE_EEFC;
 | 
						|
	u32 pagenum = (info->start[sector]-ATMEL_BASE_FLASH)/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 *) ATMEL_BASE_EEFC;
 | 
						|
 | 
						|
	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-ATMEL_BASE_FLASH)/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;
 | 
						|
}
 |