mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-18 06:58:54 +00:00
AT91SAM9XE: add embedded flash support
Signed-off-by: Reinhard Meyer <u-boot@emk-elektronik.de>
This commit is contained in:
parent
c7260d1539
commit
d88bebe16d
4 changed files with 364 additions and 0 deletions
|
@ -34,6 +34,7 @@ COBJS-$(CONFIG_AT91SAM9263) += at91sam9263_devices.o
|
|||
COBJS-$(CONFIG_AT91SAM9RL) += at91sam9rl_devices.o
|
||||
COBJS-$(CONFIG_AT91SAM9M10G45) += at91sam9m10g45_devices.o
|
||||
COBJS-$(CONFIG_AT91SAM9G45) += at91sam9m10g45_devices.o
|
||||
COBJS-$(CONFIG_AT91_EFLASH) += eflash.o
|
||||
COBJS-$(CONFIG_AT91_LED) += led.o
|
||||
COBJS-y += clock.o
|
||||
COBJS-y += cpu.o
|
||||
|
|
271
arch/arm/cpu/arm926ejs/at91/eflash.c
Normal file
271
arch/arm/cpu/arm926ejs/at91/eflash.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
41
arch/arm/include/asm/arch-at91/at91_dbu.h
Normal file
41
arch/arm/include/asm/arch-at91/at91_dbu.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2010
|
||||
* Reinhard Meyer, reinhard.meyer@emk-elektronik.de
|
||||
*
|
||||
* Debug Unit
|
||||
* Based on AT91SAM9XE datasheet
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef AT91_DBU_H
|
||||
#define AT91_DBU_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
typedef struct at91_dbu {
|
||||
u32 cr; /* Control Register WO */
|
||||
u32 mr; /* Mode Register RW */
|
||||
u32 ier; /* Interrupt Enable Register WO */
|
||||
u32 idr; /* Interrupt Disable Register WO */
|
||||
u32 imr; /* Interrupt Mask Register RO */
|
||||
u32 sr; /* Status Register RO */
|
||||
u32 rhr; /* Receive Holding Register RO */
|
||||
u32 thr; /* Transmit Holding Register WO */
|
||||
u32 brgr; /* Baud Rate Generator Register RW */
|
||||
u32 res1[7];/* 0x0024 - 0x003C Reserved */
|
||||
u32 cidr; /* Chip ID Register RO */
|
||||
u32 exid; /* Chip ID Extension Register RO */
|
||||
u32 fnr; /* Force NTRST Register RW */
|
||||
} at91_dbu_t;
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#define AT91_DBU_CID_ARCH_MASK 0x0ff00000
|
||||
#define AT91_DBU_CID_ARCH_9xx 0x01900000
|
||||
#define AT91_DBU_CID_ARCH_9XExx 0x02900000
|
||||
|
||||
#endif
|
51
arch/arm/include/asm/arch-at91/at91_eefc.h
Normal file
51
arch/arm/include/asm/arch-at91/at91_eefc.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2010
|
||||
* Reinhard Meyer, reinhard.meyer@emk-elektronik.de
|
||||
*
|
||||
* Enhanced Embedded Flash Controller
|
||||
* Based on AT91SAM9XE datasheet
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef AT91_EEFC_H
|
||||
#define AT91_EEFC_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
typedef struct at91_eefc {
|
||||
u32 fmr; /* Flash Mode Register RW */
|
||||
u32 fcr; /* Flash Command Register WO */
|
||||
u32 fsr; /* Flash Status Register RO */
|
||||
u32 frr; /* Flash Result Register RO */
|
||||
} at91_eefc_t;
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#define AT91_EEFC_FMR_FWS_MASK 0x00000f00
|
||||
#define AT91_EEFC_FMR_FRDY_BIT 0x00000001
|
||||
|
||||
#define AT91_EEFC_FCR_KEY 0x5a000000
|
||||
#define AT91_EEFC_FCR_FARG_MASK 0x00ffff00
|
||||
#define AT91_EEFC_FCR_FARG_SHIFT 8
|
||||
#define AT91_EEFC_FCR_FCMD_GETD 0x0
|
||||
#define AT91_EEFC_FCR_FCMD_WP 0x1
|
||||
#define AT91_EEFC_FCR_FCMD_WPL 0x2
|
||||
#define AT91_EEFC_FCR_FCMD_EWP 0x3
|
||||
#define AT91_EEFC_FCR_FCMD_EWPL 0x4
|
||||
#define AT91_EEFC_FCR_FCMD_EA 0x5
|
||||
#define AT91_EEFC_FCR_FCMD_SLB 0x8
|
||||
#define AT91_EEFC_FCR_FCMD_CLB 0x9
|
||||
#define AT91_EEFC_FCR_FCMD_GLB 0xA
|
||||
#define AT91_EEFC_FCR_FCMD_SGPB 0xB
|
||||
#define AT91_EEFC_FCR_FCMD_CGPB 0xC
|
||||
#define AT91_EEFC_FCR_FCMD_GGPB 0xD
|
||||
|
||||
#define AT91_EEFC_FSR_FRDY 1
|
||||
#define AT91_EEFC_FSR_FCMDE 2
|
||||
#define AT91_EEFC_FSR_FLOCKE 4
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue