[U-Boot] [PATCH] AT91SAM9XE add embedded flash support

Reinhard Meyer (-VC) reinhard.meyer at emk-elektronik.de
Fri Jul 16 09:35:59 CEST 2010


Add support for the embedded flash in the AT91SAM9XE128/256/512 SoCs:
- Environment can be put into that flash
- U-Boot can be in that flash
- Commands "cp" and "protect" are supported

Signed-off-by: Reinhard Meyer <reinhard.meyer at emk-elektronik.de>
---
 arch/arm/cpu/arm926ejs/at91/Makefile       |    1 +
 arch/arm/cpu/arm926ejs/at91/eflash.c       |  277
++++++++++++++++++++++++++++
 arch/arm/include/asm/arch-at91/at91_dbu.h  |   41 ++++
 arch/arm/include/asm/arch-at91/at91_eefc.h |   51 +++++
 4 files changed, 370 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/cpu/arm926ejs/at91/eflash.c
 create mode 100644 arch/arm/include/asm/arch-at91/at91_dbu.h
 create mode 100644 arch/arm/include/asm/arch-at91/at91_eefc.h

diff --git a/arch/arm/cpu/arm926ejs/at91/Makefile
b/arch/arm/cpu/arm926ejs/at91/Makefile
index 4f467be..def3980 100644
--- a/arch/arm/cpu/arm926ejs/at91/Makefile
+++ b/arch/arm/cpu/arm926ejs/at91/Makefile
@@ -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
diff --git a/arch/arm/cpu/arm926ejs/at91/eflash.c
b/arch/arm/cpu/arm926ejs/at91/eflash.c
new file mode 100644
index 0000000..83dce7d
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/at91/eflash.c
@@ -0,0 +1,277 @@
+/*
+ * (C) Copyright 2010
+ * Reinhard Meyer, EMK Elektronik, reinhard.meyer at 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!
+ */
+
+#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
+
+/*#define DEBUG_FLASH*/
+
+#ifdef DEBUG_FLASH
+#define DEBUGF(fmt,args...) printf("eflash: "fmt ,##args)
+#else
+#define DEBUGF(fmt,args...)
+#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;
+
+#ifdef DEBUG_FLASH
+    puts("eflash: init\n");
+#endif
+
+    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) {
+#ifdef DEBUG_FLASH
+        puts("eflash: not an AT91SAM9XE\n");
+#endif
+        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 */
+    DEBUGF("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 */
+    DEBUGF("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");
+    if (info->size >= (1 << 20)) {
+        i = 20;
+    } else {
+        i = 10;
+    }
+    printf("  Size: %ld %cB in %d Sectors\n",
+        info->size >> i,
+        (i == 20) ? 'M' : 'k',
+        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;
+
+    DEBUGF("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 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;
+
+    DEBUGF("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)
+{
+    DEBUGF("erase first=%d last=%d\n", s_first, s_last);
+    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;
+
+    DEBUGF("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;
+}
+
diff --git a/arch/arm/include/asm/arch-at91/at91_dbu.h
b/arch/arm/include/asm/arch-at91/at91_dbu.h
new file mode 100644
index 0000000..669d933
--- /dev/null
+++ b/arch/arm/include/asm/arch-at91/at91_dbu.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010
+ * Reinhard Meyer, reinhard.meyer at 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
diff --git a/arch/arm/include/asm/arch-at91/at91_eefc.h
b/arch/arm/include/asm/arch-at91/at91_eefc.h
new file mode 100644
index 0000000..9bb3ea2
--- /dev/null
+++ b/arch/arm/include/asm/arch-at91/at91_eefc.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010
+ * Reinhard Meyer, reinhard.meyer at 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
-- 
1.5.6.5




More information about the U-Boot mailing list