summaryrefslogtreecommitdiff
path: root/cpu/arm926ejs/mx28
diff options
context:
space:
mode:
authorFrank Li <frank.li@freescale.com>2010-01-18 16:32:13 +0800
committerFrank Li <frank.li@freescale.com>2010-01-25 16:53:54 +0800
commitc77b09d0ede28f2d9ab940ed15bd16bdafb7ea26 (patch)
treed2bae95d336dcfb306f8607f15be01217511af87 /cpu/arm926ejs/mx28
parent8a42ad8f7feea158bf3589a90981f7499032a5cd (diff)
downloadu-boot-imx-c77b09d0ede28f2d9ab940ed15bd16bdafb7ea26.zip
u-boot-imx-c77b09d0ede28f2d9ab940ed15bd16bdafb7ea26.tar.gz
u-boot-imx-c77b09d0ede28f2d9ab940ed15bd16bdafb7ea26.tar.bz2
ENGR00120206 iMX28 Enable Ethernet and MMC boot supportrel_imx_2.6.31_10.02.00
Enable Ethernet and MMC boot support for imx28-evk Signed-off-by: Frank Li <frank.li@freescale.com>
Diffstat (limited to 'cpu/arm926ejs/mx28')
-rw-r--r--cpu/arm926ejs/mx28/Makefile2
-rw-r--r--cpu/arm926ejs/mx28/generic.c136
-rw-r--r--cpu/arm926ejs/mx28/mmcops.c579
-rw-r--r--cpu/arm926ejs/mx28/pinctrl.c133
-rw-r--r--cpu/arm926ejs/mx28/reset.S5
-rw-r--r--cpu/arm926ejs/mx28/serial.c108
-rw-r--r--cpu/arm926ejs/mx28/timer.c74
7 files changed, 992 insertions, 45 deletions
diff --git a/cpu/arm926ejs/mx28/Makefile b/cpu/arm926ejs/mx28/Makefile
index 3b23886..9f2d5e5 100644
--- a/cpu/arm926ejs/mx28/Makefile
+++ b/cpu/arm926ejs/mx28/Makefile
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(SOC).a
-COBJS = timer.o spi.o
+COBJS = timer.o serial.o generic.o pinctrl.o mmcops.o
SOBJS = reset.o
SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/cpu/arm926ejs/mx28/generic.c b/cpu/arm926ejs/mx28/generic.c
new file mode 100644
index 0000000..6ac75e0
--- /dev/null
+++ b/cpu/arm926ejs/mx28/generic.c
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <asm/errno.h>
+#include <asm/arch/mx28.h>
+#include <asm/arch/regs-clkctrl.h>
+
+#if defined(CONFIG_ARCH_CPU_INIT)
+int arch_cpu_init(void)
+{
+ icache_enable();
+ dcache_enable();
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_DISPLAY_CPUINFO)
+int print_cpuinfo(void)
+{
+ const u32 xtal = 24, ref = 480;
+ u32 cpu, bus, emi;
+ u32 clkfrac, clkdeq, clkctrl;
+ u32 frac, div;
+
+ clkfrac = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_FRAC0);
+ clkdeq = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_CLKSEQ);
+
+ /* CPU */
+ clkctrl = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_CPU);
+ if (clkctrl & (BM_CLKCTRL_CPU_DIV_XTAL_FRAC_EN |
+ BM_CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
+ /* No support of fractional divider calculation */
+ cpu = 0;
+ } else {
+ if (clkdeq & BM_CLKCTRL_CLKSEQ_BYPASS_CPU) {
+ /* xtal path */
+ div = (clkctrl & BM_CLKCTRL_CPU_DIV_XTAL) >>
+ BP_CLKCTRL_CPU_DIV_XTAL;
+ cpu = xtal / div;
+ } else {
+ /* ref path */
+ frac = (clkfrac & BM_CLKCTRL_FRAC0_CPUFRAC) >>
+ BP_CLKCTRL_FRAC0_CPUFRAC;
+ div = (clkctrl & BM_CLKCTRL_CPU_DIV_CPU) >>
+ BP_CLKCTRL_CPU_DIV_CPU;
+ cpu = (ref * 18 / frac) / div;
+ }
+ }
+
+ /* BUS */
+ clkctrl = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_HBUS);
+ if (clkctrl & BM_CLKCTRL_HBUS_DIV_FRAC_EN) {
+ /* No support of fractional divider calculation */
+ bus = 0;
+ } else {
+ div = (clkctrl & BM_CLKCTRL_HBUS_DIV) >>
+ BP_CLKCTRL_HBUS_DIV;
+ bus = cpu / div;
+ }
+
+ /* EMI */
+ clkctrl = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_EMI);
+ if (clkdeq & BM_CLKCTRL_CLKSEQ_BYPASS_EMI) {
+ /* xtal path */
+ div = (clkctrl & BM_CLKCTRL_EMI_DIV_XTAL) >>
+ BP_CLKCTRL_EMI_DIV_XTAL;
+ emi = xtal / div;
+ } else {
+ /* ref path */
+ frac = (clkfrac & BM_CLKCTRL_FRAC0_EMIFRAC) >>
+ BP_CLKCTRL_FRAC0_EMIFRAC;
+ div = (clkctrl & BM_CLKCTRL_EMI_DIV_EMI) >>
+ BP_CLKCTRL_EMI_DIV_EMI;
+ emi = (ref * 18 / frac) / div;
+ }
+
+ /* Print */
+ printf("Freescale i.MX28 family\n");
+ printf("CPU: %d MHz\n", cpu);
+ printf("BUS: %d MHz\n", bus);
+ printf("EMI: %d MHz\n", emi);
+
+ return 0;
+}
+#endif
+
+/*
+ * Initializes on-chip MMC controllers.
+ */
+#if defined(CONFIG_MXC_ENET)
+int imx_ssp_mmc_initialize(bd_t *bis);
+#endif
+int cpu_mmc_init(bd_t *bis)
+{
+ int rc = ENODEV;
+#ifdef CONFIG_IMX_SSP_MMC
+ rc = imx_ssp_mmc_initialize(bis);
+#endif
+ return rc;
+}
+
+/*
+ * Initializes on-chip ethernet controllers.
+ */
+#if defined(CONFIG_MXC_ENET)
+int mxc_enet_initialize(bd_t *bis);
+#endif
+int cpu_eth_init(bd_t *bis)
+{
+ int rc = ENODEV;
+#if defined(CONFIG_MXC_ENET)
+ rc = mxc_enet_initialize(bis);
+#endif
+ return rc;
+}
+
diff --git a/cpu/arm926ejs/mx28/mmcops.c b/cpu/arm926ejs/mx28/mmcops.c
new file mode 100644
index 0000000..eed2046
--- /dev/null
+++ b/cpu/arm926ejs/mx28/mmcops.c
@@ -0,0 +1,579 @@
+/*
+ * (C) Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+#include <common.h>
+#include <command.h>
+#include <exports.h>
+#include <mmc.h>
+
+#if defined(CONFIG_GENERIC_MMC) && defined(CONFIG_CUSTOMIZE_MMCOPS)
+#define MMCOPS_DEBUG
+
+#define MBR_SIGNATURE 0xaa55
+#define MBR_SECTOR_COUNT (CONFIG_ENV_OFFSET >> 9)
+/* 1. RSV partition (env and uImage) */
+#define RSVPART_SECTOR_OFFSET (MBR_SECTOR_COUNT)
+#define RSVPART_SECTOR_COUNT 0x3ffe /* 8 MB */
+/* 2. FAT partition */
+#define FATPART_FILESYS 0xb
+#define FATPART_SECTOR_OFFSET (RSVPART_SECTOR_OFFSET + RSVPART_SECTOR_COUNT)
+#define FATPART_SECTOR_COUNT 0x10000 /* 32 MB (minimal) */
+/* 3. EXT partition */
+#define EXTPART_FILESYS 0x83
+#define EXTPART_SECTOR_COUNT 0xc0000 /* 384 MB */
+/* 4. SB partition (uboot.sb or linux.sb) */
+#define SBPART_FILESYS 'S'
+#define SBPART_SECTOR_COUNT 0x4000 /* 8 MB */
+#define CB_SIGNATURE 0x00112233
+#define CB_SECTOR_COUNT 2
+
+#define MAX_CYLINDERS 1024
+#define MAX_HEADS 256
+#define MAX_SECTORS 63
+
+struct partition {
+ u8 boot_flag;
+ u8 start_head;
+ u8 start_sector;
+ u8 start_cylinder;
+ u8 file_system;
+ u8 end_head;
+ u8 end_sector;
+ u8 end_cylinder;
+ u32 sector_offset;
+ u32 sector_count;
+} __attribute__ ((__packed__));
+
+struct partition_table {
+ u8 reserved[446];
+ struct partition partitions[4];
+ u16 signature;
+};
+
+struct drive_info {
+ u32 chip_num;
+ u32 drive_type;
+ u32 drive_tag;
+ u32 sector_offset;
+ u32 sector_count;
+};
+
+struct config_block {
+ u32 signature;
+ u32 primary_tag;
+ u32 secondary_tag;
+ u32 num_copies;
+ struct drive_info drive_info[2];
+};
+
+static int mmc_format(int dev)
+{
+ int rc = 0;
+ u8 *buf = 0;
+ u32 i, cnt, total_sectors;
+ u32 offset[4];
+ struct config_block *cb;
+ struct partition_table *mbr;
+ struct mmc *mmc = find_mmc_device(dev);
+
+ /* Warning */
+ printf("WARN: Data on card will get lost with format.\n"
+ "Continue?(y/n)");
+ char ch = getc();
+ printf("\n");
+ if (ch != 'y') {
+ rc = -1;
+ goto out;
+ }
+
+ /* Allocate sector buffer */
+ buf = malloc(mmc->read_bl_len);
+ if (!buf) {
+ printf("%s[%d]: malloc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ memset(buf, 0, mmc->read_bl_len);
+
+ /* Erase the first sector of each partition */
+ cnt = mmc->block_dev.block_read(dev, 0, 1, buf);
+ if (cnt != 1) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ mbr = (struct partition_table *)buf;
+ if (mbr->signature == MBR_SIGNATURE) {
+ /* Get sector offset of each partition */
+ for (i = 0; i < 4; i++)
+ offset[i] = mbr->partitions[i].sector_offset;
+ /* Erase */
+ memset(buf, 0, mmc->read_bl_len);
+ for (i = 0; i < 4; i++) {
+ if (offset[i] > 0) {
+ cnt = mmc->block_dev.block_write(dev,
+ offset[i], 1, buf);
+ if (cnt != 1) {
+ printf("%s[%d]: write mmc error\n",
+ __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* Get total sectors */
+ total_sectors = mmc->capacity >> 9;
+ if (RSVPART_SECTOR_COUNT + SBPART_SECTOR_COUNT > total_sectors) {
+ printf("Card capacity is too low to format\n");
+ rc = -1;
+ goto out;
+ }
+
+ /* Write config block */
+ cb = (struct config_block *)buf;
+ cb->signature = CB_SIGNATURE;
+ cb->num_copies = 2;
+ cb->primary_tag = 0x1;
+ cb->secondary_tag = 0x2;
+ cb->drive_info[0].chip_num = 0;
+ cb->drive_info[0].drive_type = 0;
+ cb->drive_info[0].drive_tag = 0x1;
+ cb->drive_info[0].sector_count = SBPART_SECTOR_COUNT - CB_SECTOR_COUNT;
+ cb->drive_info[0].sector_offset =
+ total_sectors - cb->drive_info[0].sector_count;
+ cb->drive_info[1].chip_num = 0;
+ cb->drive_info[1].drive_type = 0;
+ cb->drive_info[1].drive_tag = 0x2;
+ cb->drive_info[1].sector_count = SBPART_SECTOR_COUNT - CB_SECTOR_COUNT;
+ cb->drive_info[1].sector_offset =
+ total_sectors - cb->drive_info[1].sector_count;
+
+ cnt = mmc->block_dev.block_write(dev,
+ total_sectors - SBPART_SECTOR_COUNT, 1, (void *)cb);
+ if (cnt != 1) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+
+ /* Prepare for MBR */
+ memset(buf, 0, mmc->read_bl_len);
+ mbr = (struct partition_table *)buf;
+
+ /* RSV partition */
+ mbr->partitions[0].sector_offset = RSVPART_SECTOR_OFFSET;
+ mbr->partitions[0].sector_count = RSVPART_SECTOR_COUNT;
+
+ /* SB partition */
+ mbr->partitions[3].file_system = SBPART_FILESYS;
+ mbr->partitions[3].sector_offset = total_sectors - SBPART_SECTOR_COUNT;
+ mbr->partitions[3].sector_count = SBPART_SECTOR_COUNT;
+
+ /* EXT partition */
+ if (EXTPART_SECTOR_COUNT + SBPART_SECTOR_COUNT +
+ RSVPART_SECTOR_COUNT > total_sectors) {
+#ifdef MMCOPS_DEBUG
+ printf("No room for EXT partition\n");
+#endif
+ } else {
+ mbr->partitions[2].file_system = EXTPART_FILESYS;
+ mbr->partitions[2].sector_offset = total_sectors -
+ SBPART_SECTOR_COUNT - EXTPART_SECTOR_COUNT;
+ mbr->partitions[2].sector_count = EXTPART_SECTOR_COUNT;
+ }
+
+ /* FAT partition */
+ if (FATPART_SECTOR_COUNT + MBR_SECTOR_COUNT +
+ mbr->partitions[0].sector_count +
+ mbr->partitions[2].sector_count +
+ mbr->partitions[3].sector_count > total_sectors) {
+#ifdef MMCOPS_DEBUG
+ printf("No room for FAT partition\n");
+#endif
+ goto out;
+ }
+ mbr->partitions[1].file_system = FATPART_FILESYS;
+ mbr->partitions[1].sector_offset = FATPART_SECTOR_OFFSET;
+ mbr->partitions[1].sector_count = total_sectors - MBR_SECTOR_COUNT -
+ mbr->partitions[0].sector_count -
+ mbr->partitions[2].sector_count -
+ mbr->partitions[3].sector_count;
+
+out:
+ if (rc == 0) {
+ /* Write MBR */
+ mbr->signature = MBR_SIGNATURE;
+ cnt = mmc->block_dev.block_write(dev, 0, 1, (void *)mbr);
+ if (cnt != 1) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ } else
+ printf("Done.\n");
+ }
+
+ if (!buf)
+ free(buf);
+ return rc;
+}
+
+static int install_sbimage(int dev, void *addr, u32 size)
+{
+ int rc = 0;
+ u8 *buf = 0;
+ u32 cnt, offset, cb_offset, sectors, not_format = 0;
+ struct config_block *cb;
+ struct partition_table *mbr;
+ struct mmc *mmc = find_mmc_device(dev);
+
+ /* Allocate sector buffer */
+ buf = malloc(mmc->read_bl_len);
+ if (!buf) {
+ printf("%s[%d]: malloc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+
+ /* Check partition */
+ offset = 0;
+ cnt = mmc->block_dev.block_read(dev, offset, 1, buf);
+ if (cnt != 1) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ mbr = (struct partition_table *)buf;
+ if ((mbr->signature != MBR_SIGNATURE) ||
+ (mbr->partitions[3].file_system != SBPART_FILESYS))
+ not_format = 1;
+
+ /* Check config block */
+ offset = mbr->partitions[3].sector_offset;
+ cb_offset = offset; /* Save for later use */
+ cnt = mmc->block_dev.block_read(dev, offset, 1, buf);
+ if (cnt != 1) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ cb = (struct config_block *)buf;
+ if (cb->signature != CB_SIGNATURE)
+ not_format = 1;
+
+ /* Not formatted */
+ if (not_format) {
+ printf("Card is not formatted yet\n");
+ rc = -1;
+ goto out;
+ }
+
+ /* Calculate sectors of image */
+ sectors = size / mmc->read_bl_len;
+ if (size % mmc->read_bl_len)
+ sectors++;
+
+ /* Write image */
+ offset = cb->drive_info[0].sector_offset;
+ cnt = mmc->block_dev.block_write(dev, offset, sectors, addr);
+ if (cnt != sectors) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ /* Verify */
+ cnt = mmc->block_dev.block_read(dev, offset, sectors,
+ addr + sectors * mmc->read_bl_len);
+ if (cnt != sectors) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ if (memcmp(addr, addr + sectors * mmc->read_bl_len,
+ sectors * mmc->read_bl_len)) {
+ printf("Verifying sbImage write fails");
+ rc = -1;
+ goto out;
+ }
+
+ /* Redundant one */
+ offset += sectors;
+ cnt = mmc->block_dev.block_write(dev, offset, sectors, addr);
+ if (cnt != sectors) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ /* Verify */
+ cnt = mmc->block_dev.block_read(dev, offset, sectors,
+ addr + sectors * mmc->read_bl_len);
+ if (cnt != sectors) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ if (memcmp(addr, addr + sectors * mmc->read_bl_len,
+ sectors * mmc->read_bl_len)) {
+ printf("Verifying redundant sbImage write fails");
+ rc = -1;
+ goto out;
+ }
+
+ /* Update config block */
+ cb->drive_info[0].sector_count = sectors;
+ cb->drive_info[1].sector_count = sectors;
+ cb->drive_info[1].sector_offset = offset;
+ cnt = mmc->block_dev.block_write(dev, cb_offset, 1, (void *)cb);
+ if (cnt != 1) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+
+ /* Done */
+ printf("Done: %d (%x hex) sectors written at %d (%x hex)\n",
+ sectors, sectors, offset - sectors, offset - sectors);
+
+out:
+ if (!buf)
+ free(buf);
+ return rc;
+}
+
+static int install_uimage(int dev, void *addr, u32 size)
+{
+ int rc = 0;
+ u8 *buf = 0;
+ u32 cnt, offset, sectors;
+ struct partition_table *mbr;
+ struct mmc *mmc = find_mmc_device(dev);
+
+ /* Calculate sectors of uImage */
+ sectors = size / mmc->read_bl_len;
+ if (size % mmc->read_bl_len)
+ sectors++;
+
+ /* Allocate sector buffer */
+ buf = malloc(mmc->read_bl_len);
+ if (!buf) {
+ printf("%s[%d]: malloc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+
+ /* Check partition */
+ offset = 0;
+ cnt = mmc->block_dev.block_read(dev, offset, 1, buf);
+ if (cnt != 1) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ mbr = (struct partition_table *)buf;
+ if (mbr->signature != MBR_SIGNATURE) {
+ printf("No valid partition table\n");
+ rc = -1;
+ goto out;
+ }
+ if (mbr->partitions[0].sector_count < sectors) {
+ printf("No enough uImage partition room\n");
+ rc = -1;
+ goto out;
+ }
+
+ /* Write uImage */
+ offset = mbr->partitions[0].sector_offset + (CONFIG_ENV_SIZE >> 9);
+ cnt = mmc->block_dev.block_write(dev, offset, sectors, addr);
+ if (cnt != sectors) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ /* Verify */
+ cnt = mmc->block_dev.block_read(dev, offset, sectors,
+ addr + sectors * mmc->read_bl_len);
+ if (cnt != sectors) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ if (memcmp(addr, addr + sectors * mmc->read_bl_len,
+ sectors * mmc->read_bl_len)) {
+ printf("Verifying uImage write fails");
+ rc = -1;
+ goto out;
+ }
+
+ /* Done */
+ printf("Done: %d (%x hex) sectors written at %d (%x hex)\n",
+ sectors, sectors, offset, offset);
+
+out:
+ if (!buf)
+ free(buf);
+ return rc;
+}
+
+static int install_rootfs(int dev, void *addr, u32 size)
+{
+ int rc = 0;
+ u8 *buf = 0;
+ u32 cnt, offset, sectors;
+ struct partition_table *mbr;
+ struct mmc *mmc = find_mmc_device(dev);
+
+ /* Calculate sectors of rootfs */
+ sectors = size / mmc->read_bl_len;
+ if (size % mmc->read_bl_len)
+ sectors++;
+
+ /* Allocate sector buffer */
+ buf = malloc(mmc->read_bl_len);
+ if (!buf) {
+ printf("%s[%d]: malloc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+
+ /* Check partition */
+ offset = 0;
+ cnt = mmc->block_dev.block_read(dev, offset, 1, buf);
+ if (cnt != 1) {
+ printf("%s[%d]: read mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ mbr = (struct partition_table *)buf;
+ if ((mbr->signature != MBR_SIGNATURE) ||
+ (mbr->partitions[2].file_system != EXTPART_FILESYS)) {
+ printf("No rootfs partition\n");
+ rc = -1;
+ goto out;
+ }
+ if (mbr->partitions[2].sector_count < sectors) {
+ printf("No enough rootfs partition room\n");
+ rc = -1;
+ goto out;
+ }
+
+ /* Write rootfs */
+ offset = mbr->partitions[2].sector_offset;
+ cnt = mmc->block_dev.block_write(dev, offset, sectors, addr);
+ if (cnt != sectors) {
+ printf("%s[%d]: write mmc error\n", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+
+ /* Done */
+ printf("Done: %d (%x hex) sectors written at %d (%x hex)\n",
+ sectors, sectors, offset, offset);
+
+out:
+ if (!buf)
+ free(buf);
+ return rc;
+}
+
+int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int dev = 0;
+ struct mmc *mmc;
+
+ if (argc < 2)
+ goto err_out;
+
+ if (strcmp(argv[1], "read") && strcmp(argv[1], "write") &&
+ strcmp(argv[1], "rescan") && strcmp(argv[1], "format") &&
+ strcmp(argv[1], "install") && strcmp(argv[1], "list"))
+ goto err_out;
+
+ if (argc == 2) { /* list */
+ print_mmc_devices('\n');
+ return 0;
+ }
+
+ /* Find and init mmc */
+ dev = simple_strtoul(argv[2], NULL, 10);
+ mmc = find_mmc_device(dev);
+ if (!mmc) {
+ printf("%s[%d]: find mmc error\n", __func__, __LINE__);
+ return -1;
+ }
+ if (mmc_init(mmc)) {
+ printf("%s[%d]: init mmc error\n", __func__, __LINE__);
+ return -1;
+ }
+
+ if (!strcmp(argv[1], "format"))
+ mmc_format(dev);
+ if (argc == 3) /* rescan (mmc_init) */
+ return 0;
+
+ if (argc != 6)
+ goto err_out;
+
+ if (!strcmp(argv[1], "read") || !strcmp(argv[1], "write")) {
+ void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ u32 blk = simple_strtoul(argv[4], NULL, 16);
+ u32 cnt = simple_strtoul(argv[5], NULL, 16);
+ u32 n;
+
+ printf("\nMMC %s: dev # %d, block # %d, count %d ... ",
+ argv[1], dev, blk, cnt);
+
+ if (!strcmp(argv[1], "read")) {
+ n = mmc->block_dev.block_read(dev, blk, cnt, addr);
+ /* flush cache after read */
+ flush_cache((ulong)addr, cnt * 512); /* FIXME */
+ } else /* write */
+ n = mmc->block_dev.block_write(dev, blk, cnt, addr);
+
+ printf("%d blocks %s: %s\n", n, argv[1],
+ (n == cnt) ? "OK" : "ERROR");
+ return (n == cnt) ? 0 : -1;
+ } else if (!strcmp(argv[1], "install")) {
+ void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ u32 size = simple_strtoul(argv[4], NULL, 16);
+
+ if (!strcmp(argv[5], "sbImage"))
+ return install_sbimage(dev, addr, size);
+ else if (!strcmp(argv[5], "uImage"))
+ return install_uimage(dev, addr, size);
+ else if (!strcmp(argv[5], "rootfs"))
+ return install_rootfs(dev, addr, size);
+ }
+
+err_out:
+ printf("Usage:\n%s\n", cmdtp->usage);
+ return -1;
+}
+
+U_BOOT_CMD(
+ mmc, 6, 1, do_mmcops,
+ "MMC sub system",
+ "mmc read <device num> addr blk# cnt\n"
+ "mmc write <device num> addr blk# cnt\n"
+ "mmc rescan <device num>\n"
+ "mmc format <device num>\n"
+ "mmc install <device num> addr size sbImage/uImage/rootfs\n"
+ "mmc list - lists available devices");
+#endif /* (CONFIG_GENERIC_MMC && CONFIG_CUSTOMIZE_MMCOPS) */
diff --git a/cpu/arm926ejs/mx28/pinctrl.c b/cpu/arm926ejs/mx28/pinctrl.c
new file mode 100644
index 0000000..88427c8
--- /dev/null
+++ b/cpu/arm926ejs/mx28/pinctrl.c
@@ -0,0 +1,133 @@
+/*
+ *
+ * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+#include <common.h>
+#include <asm/arch/mx28.h>
+#include <asm/arch/regs-pinctrl.h>
+#include <asm/arch/pinctrl.h>
+
+void pin_gpio_direction(u32 id, u32 output)
+{
+ u32 addr;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_DOE0;
+ addr += 0x10 * bank;
+ if (output)
+ REG_SET_ADDR(addr, 1 << pin);
+ else
+ REG_CLR_ADDR(addr, 1 << pin);
+}
+
+u32 pin_gpio_get(u32 id)
+{
+ u32 addr, val;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_DIN0;
+ addr += 0x10 * bank;
+ val = REG_RD_ADDR(addr);
+
+ return (val & (1 << pin)) >> pin;
+}
+
+void pin_gpio_set(u32 id, u32 val)
+{
+ u32 addr;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_DOUT0;
+ addr += 0x10 * bank;
+ if (val)
+ REG_SET_ADDR(addr, 1 << pin);
+ else
+ REG_CLR_ADDR(addr, 1 << pin);
+}
+
+void pin_set_strength(u32 id, enum pad_strength strength)
+{
+ u32 addr;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_DRIVE0;
+ addr += 0x40 * bank + 0x10 * (pin >> 3);
+ pin &= 0x7;
+ REG_CLR_ADDR(addr, 0x3 << (pin * 4));
+ REG_SET_ADDR(addr, strength << (pin * 4));
+}
+
+void pin_set_voltage(u32 id, enum pad_voltage volt)
+{
+ u32 addr;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_DRIVE0;
+ addr += 0x40 * bank + 0x10 * (pin >> 3);
+ pin &= 0x7;
+ if (volt == PAD_1V8)
+ REG_CLR_ADDR(addr, 1 << (pin * 4 + 2));
+ else
+ REG_SET_ADDR(addr, 1 << (pin * 4 + 2));
+}
+
+void pin_set_pullup(u32 id, u32 pullup)
+{
+ u32 addr;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_PULL0;
+ addr += 0x10 * bank;
+ if (pullup)
+ REG_SET_ADDR(addr, 1 << pin);
+ else
+ REG_CLR_ADDR(addr, 1 << pin);
+}
+
+void pin_set_type(u32 id, enum pin_fun cfg)
+{
+ u32 addr;
+ u32 bank = PINID_2_BANK(id);
+ u32 pin = PINID_2_PIN(id);
+
+ addr = REGS_PINCTRL_BASE + HW_PINCTRL_MUXSEL0;
+ addr += 0x20 * bank + 0x10 * (pin >> 4);
+ pin &= 0xf;
+ REG_CLR_ADDR(addr, 0x3 << (pin * 2));
+ REG_SET_ADDR(addr, cfg << (pin * 2));
+}
+
+void pin_set_group(struct pin_group *pin_group)
+{
+ u32 p;
+ struct pin_desc *pin;
+
+ for (p = 0; p < pin_group->nr_pins; p++) {
+ pin = &pin_group->pins[p];
+ pin_set_type(pin->id, pin->fun);
+ pin_set_strength(pin->id, pin->strength);
+ pin_set_voltage(pin->id, pin->voltage);
+ pin_set_pullup(pin->id, pin->pullup);
+ }
+}
+
diff --git a/cpu/arm926ejs/mx28/reset.S b/cpu/arm926ejs/mx28/reset.S
index 324c384..c8596e5 100644
--- a/cpu/arm926ejs/mx28/reset.S
+++ b/cpu/arm926ejs/mx28/reset.S
@@ -1,7 +1,8 @@
/*
- * Processor reset for Freescale MX28 SoC.
+ * Processor reset for Freescale i.MX28 SoC.
*
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
*
* -----------------------------------------------------
*
@@ -39,5 +40,5 @@ POWER_MINPWR:
POWER_CHARGE:
.word 0x80044030
CLKCTRL_RESET:
- .word 0x80040120
+ .word 0x800401e0
diff --git a/cpu/arm926ejs/mx28/serial.c b/cpu/arm926ejs/mx28/serial.c
new file mode 100644
index 0000000..58f7d2a
--- /dev/null
+++ b/cpu/arm926ejs/mx28/serial.c
@@ -0,0 +1,108 @@
+/*
+ * (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *
+ * 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
+ *
+ */
+#include <common.h>
+#include <asm/arch/mx28.h>
+#include <asm/arch/regs-uartdbg.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Set baud rate. The settings are always 8n1:
+ * 8 data bits, no parity, 1 stop bit
+ */
+void serial_setbrg(void)
+{
+ u32 cr;
+ u32 quot;
+
+ /* Disable everything */
+ cr = REG_RD(REGS_UARTDBG_BASE, HW_UARTDBGCR);
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGCR, 0);
+
+ /* Calculate and set baudrate */
+ quot = (CONFIG_DBGUART_CLK * 4) / gd->baudrate;
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGFBRD, quot & 0x3f);
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGIBRD, quot >> 6);
+
+ /* Set 8n1 mode, enable FIFOs */
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGLCR_H,
+ BM_UARTDBGLCR_H_WLEN | BM_UARTDBGLCR_H_FEN);
+
+ /* Enable Debug UART */
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGCR, cr);
+}
+
+int serial_init(void)
+{
+ /* Disable UART */
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGCR, 0);
+
+ /* Mask interrupts */
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGIMSC, 0);
+
+ /* Set default baudrate */
+ serial_setbrg();
+
+ /* Enable UART */
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGCR,
+ BM_UARTDBGCR_TXE | BM_UARTDBGCR_RXE | BM_UARTDBGCR_UARTEN);
+
+ return 0;
+}
+
+/* Send a character */
+void serial_putc(const char c)
+{
+ /* Wait for room in TX FIFO */
+ while (REG_RD(REGS_UARTDBG_BASE, HW_UARTDBGFR) & BM_UARTDBGFR_TXFF)
+ ;
+
+ /* Write the data byte */
+ REG_WR(REGS_UARTDBG_BASE, HW_UARTDBGDR, c);
+
+ if (c == '\n')
+ serial_putc('\r');
+}
+
+void serial_puts(const char *s)
+{
+ while (*s)
+ serial_putc(*s++);
+}
+
+/* Test whether a character is in TX buffer */
+int serial_tstc(void)
+{
+ /* Check if RX FIFO is not empty */
+ return !(REG_RD(REGS_UARTDBG_BASE, HW_UARTDBGFR) & BM_UARTDBGFR_RXFE);
+}
+
+/* Receive character */
+int serial_getc(void)
+{
+ /* Wait while TX FIFO is empty */
+ while (REG_RD(REGS_UARTDBG_BASE, HW_UARTDBGFR) & BM_UARTDBGFR_RXFE)
+ ;
+
+ /* Read data byte */
+ return REG_RD(REGS_UARTDBG_BASE, HW_UARTDBGDR) & 0xff;
+}
+
diff --git a/cpu/arm926ejs/mx28/timer.c b/cpu/arm926ejs/mx28/timer.c
index ffad4d8..873b30c 100644
--- a/cpu/arm926ejs/mx28/timer.c
+++ b/cpu/arm926ejs/mx28/timer.c
@@ -16,7 +16,7 @@
* (C) Copyright 2004
* Philippe Robin, ARM Ltd. <philippe.robin@arm.com>
*
- * (C) Copyright 2009 Freescale Semiconductor, Inc.
+ * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
@@ -39,72 +39,60 @@
#include <common.h>
#include <asm/arch/mx28.h>
-#include <asm/arch/timrot.h>
-
-#define CONFIG_USE_TIMER0
-
-#if defined(CONFIG_USE_TIMER0)
-#define TIMCTRL TIMCTRL0
-#define TIMCOUNT TIMCOUNT0
-#elif defined(CONFIG_USE_TIMER1)
-#define TIMCTRL TIMCTRL1
-#define TIMCOUNT TIMCOUNT1
-#elif defined(CONFIG_USE_TIMER2)
-#define TIMCTRL TIMCTRL2
-#define TIMCOUNT TIMCOUNT2
-#elif defined(CONFIG_USE_TIMER3)
-#define TIMCTRL TIMCTRL3
-#define TIMCOUNT TIMCOUNT3
-#else
-#error "Define which STMP378x timer to use"
-#endif
-
-#define TIMER_LOAD_VAL 0x0000ffff
-
-/* macro to read the 16 bit timer */
-#define READ_TIMER ((REG_RD(TIMROT_BASE + TIMCOUNT) & 0xffff0000) >> 16)
+#include <asm/arch/regs-timrot.h>
+
+/*
+ * TIMROT gets 4 timer instances
+ * Define N as 0..3 to specify
+ */
+#define N 0
+
+/* Ticks per second */
+#define CONFIG_SYS_HZ 1000
+
+/* Maximum fixed count */
+#define TIMER_LOAD_VAL 0xffffffff
static ulong timestamp;
static ulong lastdec;
int timer_init(void)
{
- u32 val;
-
/*
* Reset Timers and Rotary Encoder module
*/
/* Clear SFTRST */
- REG_CLR(TIMROT_BASE + ROTCTRL, 1 << 31);
- while (REG_RD(TIMROT_BASE + ROTCTRL) & (1 << 31))
+ REG_CLR(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL, 1 << 31);
+ while (REG_RD(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL) & (1 << 31))
;
/* Clear CLKGATE */
- REG_CLR(TIMROT_BASE + ROTCTRL, 1 << 30);
+ REG_CLR(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL, 1 << 30);
/* Set SFTRST and wait until CLKGATE is set */
- REG_SET(TIMROT_BASE + ROTCTRL, 1 << 31);
- while (!(REG_RD(TIMROT_BASE + ROTCTRL) & (1 << 30)))
+ REG_SET(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL, 1 << 31);
+ while (!(REG_RD(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL) & (1 << 30)))
;
/* Clear SFTRST and CLKGATE */
- REG_CLR(TIMROT_BASE + ROTCTRL, 1 << 31);
- REG_CLR(TIMROT_BASE + ROTCTRL, 1 << 30);
+ REG_CLR(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL, 1 << 31);
+ REG_CLR(REGS_TIMROT_BASE, HW_TIMROT_ROTCTRL, 1 << 30);
/*
* Now initialize timer
*/
/* Set fixed_count to 0 */
- REG_WR(TIMROT_BASE + TIMCOUNT, 0);
+ REG_WR(REGS_TIMROT_BASE, HW_TIMROT_FIXED_COUNTn(N), 0);
/* set UPDATE bit and 1Khz frequency */
- REG_WR(TIMROT_BASE + TIMCTRL,
- TIMCTRL_RELOAD | TIMCTRL_UPDATE | TIMCTRL_SELECT_1KHZ);
+ REG_WR(REGS_TIMROT_BASE, HW_TIMROT_TIMCTRLn(N),
+ BM_TIMROT_TIMCTRLn_RELOAD | BM_TIMROT_TIMCTRLn_UPDATE |
+ BV_TIMROT_TIMCTRLn_SELECT__1KHZ_XTAL);
/* Set fixed_count to maximal value */
- REG_WR(TIMROT_BASE + TIMCOUNT, TIMER_LOAD_VAL);
+ REG_WR(REGS_TIMROT_BASE, HW_TIMROT_FIXED_COUNTn(N), TIMER_LOAD_VAL);
/* init the timestamp and lastdec value */
reset_timer_masked();
@@ -166,14 +154,16 @@ void udelay(unsigned long usec)
void reset_timer_masked(void)
{
- /* reset time */
- lastdec = READ_TIMER; /* capure current decrementer value time */
- timestamp = 0; /* start "advancing" time stamp from 0 */
+ /* capure current decrementer value time */
+ lastdec = REG_RD(REGS_TIMROT_BASE, HW_TIMROT_RUNNING_COUNTn(N));
+ /* start "advancing" time stamp from 0 */
+ timestamp = 0;
}
ulong get_timer_masked(void)
{
- ulong now = READ_TIMER; /* current tick value */
+ /* current tick value */
+ ulong now = REG_RD(REGS_TIMROT_BASE, HW_TIMROT_RUNNING_COUNTn(N));
if (lastdec >= now) { /* normal mode (non roll) */
/* normal mode */