summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/arm926ejs
diff options
context:
space:
mode:
authorWolfgang Denk <wd@denx.de>2010-09-08 00:42:00 +0200
committerWolfgang Denk <wd@denx.de>2010-09-08 00:42:00 +0200
commitcf64fda38e3d71e9077e11c673bd9be449c680da (patch)
treea847fb6cd48330482642ff92e34a7329b339acd2 /arch/arm/cpu/arm926ejs
parent09b4a9cf4003599f2cd609587dfa5f0b754640ed (diff)
parent5c3dab97c751e43103813472283305cbfe70d3f0 (diff)
downloadu-boot-imx-cf64fda38e3d71e9077e11c673bd9be449c680da.zip
u-boot-imx-cf64fda38e3d71e9077e11c673bd9be449c680da.tar.gz
u-boot-imx-cf64fda38e3d71e9077e11c673bd9be449c680da.tar.bz2
Merge branch 'at91' of git://git.denx.de/u-boot-atmel
Diffstat (limited to 'arch/arm/cpu/arm926ejs')
-rw-r--r--arch/arm/cpu/arm926ejs/at91/Makefile1
-rw-r--r--arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c56
-rw-r--r--arch/arm/cpu/arm926ejs/at91/eflash.c271
-rw-r--r--arch/arm/cpu/arm926ejs/at91/reset.c21
-rw-r--r--arch/arm/cpu/arm926ejs/at91/timer.c5
5 files changed, 330 insertions, 24 deletions
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/at91sam9260_devices.c b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
index 77d49ab..87b0442 100644
--- a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
+++ b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
@@ -28,12 +28,27 @@
#include <asm/arch/gpio.h>
#include <asm/arch/io.h>
+/*
+ * if CONFIG_AT91_GPIO_PULLUP ist set, keep pullups on on all
+ * peripheral pins. Good to have if hardware is soldered optionally
+ * or in case of SPI no slave is selected. Avoid lines to float
+ * needlessly. Use a short local PUP define.
+ *
+ * Due to errata "TXD floats when CTS is inactive" pullups are always
+ * on for TXD pins.
+ */
+#ifdef CONFIG_AT91_GPIO_PULLUP
+# define PUP CONFIG_AT91_GPIO_PULLUP
+#else
+# define PUP 0
+#endif
+
void at91_serial0_hw_init(void)
{
at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE;
at91_set_a_periph(AT91_PIO_PORTB, 4, 1); /* TXD0 */
- at91_set_a_periph(AT91_PIO_PORTB, 5, 0); /* RXD0 */
+ at91_set_a_periph(AT91_PIO_PORTB, 5, PUP); /* RXD0 */
writel(1 << AT91SAM9260_ID_US0, &pmc->pcer);
}
@@ -42,7 +57,7 @@ void at91_serial1_hw_init(void)
at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE;
at91_set_a_periph(AT91_PIO_PORTB, 6, 1); /* TXD1 */
- at91_set_a_periph(AT91_PIO_PORTB, 7, 0); /* RXD1 */
+ at91_set_a_periph(AT91_PIO_PORTB, 7, PUP); /* RXD1 */
writel(1 << AT91SAM9260_ID_US1, &pmc->pcer);
}
@@ -51,7 +66,7 @@ void at91_serial2_hw_init(void)
at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE;
at91_set_a_periph(AT91_PIO_PORTB, 8, 1); /* TXD2 */
- at91_set_a_periph(AT91_PIO_PORTB, 9, 0); /* RXD2 */
+ at91_set_a_periph(AT91_PIO_PORTB, 9, PUP); /* RXD2 */
writel(1 << AT91SAM9260_ID_US2, &pmc->pcer);
}
@@ -59,7 +74,7 @@ void at91_serial3_hw_init(void)
{
at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE;
- at91_set_a_periph(AT91_PIO_PORTB, 14, 0); /* DRXD */
+ at91_set_a_periph(AT91_PIO_PORTB, 14, PUP); /* DRXD */
at91_set_a_periph(AT91_PIO_PORTB, 15, 1); /* DTXD */
writel(1 << AT91_ID_SYS, &pmc->pcer);
}
@@ -88,9 +103,9 @@ void at91_spi0_hw_init(unsigned long cs_mask)
{
at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE;
- at91_set_a_periph(AT91_PIO_PORTA, 0, 0); /* SPI0_MISO */
- at91_set_a_periph(AT91_PIO_PORTA, 1, 0); /* SPI0_MOSI */
- at91_set_a_periph(AT91_PIO_PORTA, 2, 0); /* SPI0_SPCK */
+ at91_set_a_periph(AT91_PIO_PORTA, 0, PUP); /* SPI0_MISO */
+ at91_set_a_periph(AT91_PIO_PORTA, 1, PUP); /* SPI0_MOSI */
+ at91_set_a_periph(AT91_PIO_PORTA, 2, PUP); /* SPI0_SPCK */
/* Enable clock */
writel(1 << AT91SAM9260_ID_SPI0, &pmc->pcer);
@@ -125,9 +140,9 @@ void at91_spi1_hw_init(unsigned long cs_mask)
{
at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE;
- at91_set_a_periph(AT91_PIO_PORTB, 0, 0); /* SPI1_MISO */
- at91_set_a_periph(AT91_PIO_PORTB, 1, 0); /* SPI1_MOSI */
- at91_set_a_periph(AT91_PIO_PORTB, 2, 0); /* SPI1_SPCK */
+ at91_set_a_periph(AT91_PIO_PORTB, 0, PUP); /* SPI1_MISO */
+ at91_set_a_periph(AT91_PIO_PORTB, 1, PUP); /* SPI1_MOSI */
+ at91_set_a_periph(AT91_PIO_PORTB, 2, PUP); /* SPI1_SPCK */
/* Enable clock */
writel(1 << AT91SAM9260_ID_SPI1, &pmc->pcer);
@@ -194,3 +209,24 @@ void at91_macb_hw_init(void)
#endif
}
#endif
+
+#if defined(CONFIG_ATMEL_MCI) || defined(CONFIG_GENERIC_ATMEL_MCI)
+void at91_mci_hw_init(void)
+{
+ at91_set_a_periph(AT91_PIO_PORTA, 8, 1); /* MCCK */
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+ at91_set_b_periph(AT91_PIO_PORTA, 1, 1); /* MCCDB */
+ at91_set_b_periph(AT91_PIO_PORTA, 0, 1); /* MCDB0 */
+ at91_set_b_periph(AT91_PIO_PORTA, 5, 1); /* MCDB1 */
+ at91_set_b_periph(AT91_PIO_PORTA, 4, 1); /* MCDB2 */
+ at91_set_b_periph(AT91_PIO_PORTA, 3, 1); /* MCDB3 */
+#else
+ at91_set_a_periph(AT91_PIO_PORTA, 7, 1); /* MCCDA */
+ at91_set_a_periph(AT91_PIO_PORTA, 6, 1); /* MCDA0 */
+ at91_set_a_periph(AT91_PIO_PORTA, 9, 1); /* MCDA1 */
+ at91_set_a_periph(AT91_PIO_PORTA, 10, 1); /* MCDA2 */
+ at91_set_a_periph(AT91_PIO_PORTA, 11, 1); /* MCDA3 */
+#endif
+}
+#endif
+
diff --git a/arch/arm/cpu/arm926ejs/at91/eflash.c b/arch/arm/cpu/arm926ejs/at91/eflash.c
new file mode 100644
index 0000000..2e851db
--- /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;
+}
+
diff --git a/arch/arm/cpu/arm926ejs/at91/reset.c b/arch/arm/cpu/arm926ejs/at91/reset.c
index 1b67e77..d2569d8 100644
--- a/arch/arm/cpu/arm926ejs/at91/reset.c
+++ b/arch/arm/cpu/arm926ejs/at91/reset.c
@@ -27,18 +27,19 @@
#include <asm/arch/at91_rstc.h>
#include <asm/arch/io.h>
-/*
- * Reset the cpu by setting up the watchdog timer and let him time out.
- */
+/* Reset the cpu by telling the reset controller to do so */
void reset_cpu(ulong ignored)
{
at91_rstc_t *rstc = (at91_rstc_t *) AT91_RSTC_BASE;
- /* this is the way Linux does it */
-
- writel(AT91_RSTC_KEY | AT91_RSTC_CR_PROCRST | AT91_RSTC_CR_PERRST,
- &rstc->cr);
-
- while (1);
- /* Never reached */
+ writel(AT91_RSTC_KEY
+ | AT91_RSTC_CR_PROCRST /* Processor Reset */
+ | AT91_RSTC_CR_PERRST /* Peripheral Reset */
+#ifdef CONFIG_AT91RESET_EXTRST
+ | AT91_RSTC_CR_EXTRST /* External Reset (assert nRST pin) */
+#endif
+ , &rstc->cr);
+ /* never reached */
+ while (1)
+ ;
}
diff --git a/arch/arm/cpu/arm926ejs/at91/timer.c b/arch/arm/cpu/arm926ejs/at91/timer.c
index d21eebf..8efc34b 100644
--- a/arch/arm/cpu/arm926ejs/at91/timer.c
+++ b/arch/arm/cpu/arm926ejs/at91/timer.c
@@ -138,8 +138,5 @@ ulong get_timer(ulong base)
*/
ulong get_tbclk(void)
{
- ulong tbclk;
-
- tbclk = CONFIG_SYS_HZ;
- return tbclk;
+ return timer_freq;
}