summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2012-07-02 11:08:03 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2012-07-02 11:08:03 +0200
commitd04ce5dfe7d2a7502c9a77952164dd6a1e1666a4 (patch)
treee3cc1f97ef08950b42223b7334dffe8a721c9a6e /arch
parent1510c57e93fdfdc8f5f6f6b2bbce44ac9a313172 (diff)
parent0256d59a41d2ee933fa6e5cc6ee3e933749171e9 (diff)
downloadbarebox-d04ce5dfe7d2a7502c9a77952164dd6a1e1666a4.tar.gz
barebox-d04ce5dfe7d2a7502c9a77952164dd6a1e1666a4.tar.xz
Merge branch 'for-next/mxs-nand'
Conflicts: arch/arm/mach-mxs/Kconfig arch/arm/mach-mxs/Makefile drivers/Makefile
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-mxs/Kconfig8
-rw-r--r--arch/arm/mach-mxs/Makefile3
-rw-r--r--arch/arm/mach-mxs/bcb.c399
-rw-r--r--arch/arm/mach-mxs/common.c33
-rw-r--r--arch/arm/mach-mxs/include/mach/clock-imx23.h1
-rw-r--r--arch/arm/mach-mxs/include/mach/clock-imx28.h1
-rw-r--r--arch/arm/mach-mxs/include/mach/dma.h145
-rw-r--r--arch/arm/mach-mxs/include/mach/imx23-regs.h3
-rw-r--r--arch/arm/mach-mxs/include/mach/imx28-regs.h4
-rw-r--r--arch/arm/mach-mxs/include/mach/mxs.h6
-rw-r--r--arch/arm/mach-mxs/speed-imx23.c19
-rw-r--r--arch/arm/mach-mxs/speed-imx28.c19
12 files changed, 639 insertions, 2 deletions
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index 9ce7c7a1bd..746c986083 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -92,6 +92,14 @@ config MXS_OCOTP_WRITABLE
enable writing:
ocotp0.permanent_write_enable=1
+config MXS_CMD_BCB
+ depends on NAND_MXS
+ tristate "Nand bcb command"
+ help
+ To be able to boot from NAND the i.MX23/28 need a Boot Control Block
+ in flash. This option enabled the 'bcb' command which can be used to
+ generate this block during runtime.
+
endmenu
menu "Board specific settings "
diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
index 9a42a5f176..fe93096dad 100644
--- a/arch/arm/mach-mxs/Makefile
+++ b/arch/arm/mach-mxs/Makefile
@@ -1,5 +1,6 @@
-obj-y += imx.o iomux-imx.o power.o
+obj-y += imx.o iomux-imx.o power.o common.o
obj-$(CONFIG_DRIVER_VIDEO_STM) += imx_lcd_clk.o
obj-$(CONFIG_ARCH_IMX23) += speed-imx23.o clocksource-imx23.o usb-imx23.o soc-imx23.o
obj-$(CONFIG_ARCH_IMX28) += speed-imx28.o clocksource-imx28.o usb-imx28.o soc-imx28.o
obj-$(CONFIG_MXS_OCOTP) += ocotp.o
+obj-$(CONFIG_MXS_CMD_BCB) += bcb.o
diff --git a/arch/arm/mach-mxs/bcb.c b/arch/arm/mach-mxs/bcb.c
new file mode 100644
index 0000000000..d0a3ddc8d3
--- /dev/null
+++ b/arch/arm/mach-mxs/bcb.c
@@ -0,0 +1,399 @@
+/*
+ * (C) Copyright 2011 Wolfram Sang, Pengutronix e.K.
+ *
+ * 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.
+ *
+ * Based on a similar function in Karo Electronics TX28-U-Boot (flash.c).
+ * Probably written by Lothar Waßmann (like tx28.c).
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <malloc.h>
+#include <nand.h>
+#include <sizes.h>
+#include <errno.h>
+#include <io.h>
+
+#include <mach/imx-regs.h>
+
+#include <linux/err.h>
+#include <linux/mtd/nand.h>
+
+#define FCB_START_BLOCK 0
+#define NUM_FCB_BLOCKS 1
+#define MAX_FCB_BLOCKS 32768
+
+#define GPMI_TIMING0 0x00000070
+#define GPMI_TIMING0_ADDRESS_SETUP_MASK (0xff << 16)
+#define GPMI_TIMING0_ADDRESS_SETUP_OFFSET 16
+#define GPMI_TIMING0_DATA_HOLD_MASK (0xff << 8)
+#define GPMI_TIMING0_DATA_HOLD_OFFSET 8
+#define GPMI_TIMING0_DATA_SETUP_MASK 0xff
+#define GPMI_TIMING0_DATA_SETUP_OFFSET 0
+
+#define GPMI_TIMING1 0x00000080
+
+#define BCH_MODE 0x00000020
+
+#define BCH_FLASH0LAYOUT0 0x00000080
+#define BCH_FLASHLAYOUT0_NBLOCKS_MASK (0xff << 24)
+#define BCH_FLASHLAYOUT0_NBLOCKS_OFFSET 24
+#define BCH_FLASHLAYOUT0_META_SIZE_MASK (0xff << 16)
+#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
+#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
+#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
+#define BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0xfff
+#define BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET 0
+
+#define BCH_FLASH0LAYOUT1 0x00000090
+#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
+#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
+#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
+#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
+#define BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0xfff
+#define BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET 0
+
+struct mx28_nand_timing {
+ u8 data_setup;
+ u8 data_hold;
+ u8 address_setup;
+ u8 dsample_time;
+ u8 nand_timing_state;
+ u8 tREA;
+ u8 tRLOH;
+ u8 tRHOH;
+};
+
+struct mx28_fcb {
+ u32 checksum;
+ u32 fingerprint;
+ u32 version;
+ struct mx28_nand_timing timing;
+ u32 page_data_size;
+ u32 total_page_size;
+ u32 sectors_per_block;
+ u32 number_of_nands; /* not used by ROM code */
+ u32 total_internal_die; /* not used by ROM code */
+ u32 cell_type; /* not used by ROM code */
+ u32 ecc_blockn_type;
+ u32 ecc_block0_size;
+ u32 ecc_blockn_size;
+ u32 ecc_block0_type;
+ u32 metadata_size;
+ u32 ecc_blocks_per_page;
+ u32 rsrvd[6]; /* not used by ROM code */
+ u32 bch_mode;
+ u32 boot_patch;
+ u32 patch_sectors;
+ u32 fw1_start_page;
+ u32 fw2_start_page;
+ u32 fw1_sectors;
+ u32 fw2_sectors;
+ u32 dbbt_search_area;
+ u32 bb_mark_byte;
+ u32 bb_mark_startbit;
+ u32 bb_mark_phys_offset;
+};
+
+struct mx28_dbbt_header {
+ u32 checksum;
+ u32 fingerprint;
+ u32 version;
+ u32 number_bb;
+ u32 number_pages;
+ u8 spare[492];
+};
+
+struct mx28_dbbt {
+ u32 nand_number;
+ u32 number_bb;
+ u32 bb_num[2040 / 4];
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+static u8 calculate_parity_13_8(u8 d)
+{
+ u8 p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ u8 *src = _src;
+ u8 *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static u32 calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+/*
+ Physical organisation of data in NAND flash:
+ metadata
+ payload chunk 0 (may be empty)
+ ecc for metadata + payload chunk 0
+ payload chunk 1
+ ecc for payload chunk 1
+...
+ payload chunk n
+ ecc for payload chunk n
+ */
+
+static int calc_bb_offset(struct mtd_info *mtd, struct mx28_fcb *fcb)
+{
+ int bb_mark_offset;
+ int chunk_data_size = fcb->ecc_blockn_size * 8;
+ int chunk_ecc_size = (fcb->ecc_blockn_type << 1) * 13;
+ int chunk_total_size = chunk_data_size + chunk_ecc_size;
+ int bb_mark_chunk, bb_mark_chunk_offs;
+
+ bb_mark_offset = (mtd->writesize - fcb->metadata_size) * 8;
+ if (fcb->ecc_block0_size == 0)
+ bb_mark_offset -= (fcb->ecc_block0_type << 1) * 13;
+
+ bb_mark_chunk = bb_mark_offset / chunk_total_size;
+ bb_mark_chunk_offs = bb_mark_offset - (bb_mark_chunk * chunk_total_size);
+ if (bb_mark_chunk_offs > chunk_data_size) {
+ printf("Unsupported ECC layout; BB mark resides in ECC data: %u\n",
+ bb_mark_chunk_offs);
+ return -EINVAL;
+ }
+ bb_mark_offset -= bb_mark_chunk * chunk_ecc_size;
+ return bb_mark_offset;
+}
+
+static struct mx28_fcb *create_fcb(struct mtd_info *mtd, void *buf, unsigned fw1_start_block,
+ size_t fw_size, unsigned fw2_start_block)
+{
+ u32 fl0, fl1, t0;
+ int metadata_size;
+ int bb_mark_bit_offs;
+ struct mx28_fcb *fcb;
+ int fcb_offs;
+ void __iomem *bch_regs = (void *)MXS_BCH_BASE;
+ void __iomem *gpmi_regs = (void *)MXS_GPMI_BASE;
+
+ fl0 = readl(bch_regs + BCH_FLASH0LAYOUT0);
+ fl1 = readl(bch_regs + BCH_FLASH0LAYOUT1);
+ t0 = readl(gpmi_regs + GPMI_TIMING0);
+ metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
+
+ fcb = buf + ALIGN(metadata_size, 4);
+ fcb_offs = (void *)fcb - buf;
+
+ memset(buf, 0x00, fcb_offs);
+ memset(fcb, 0x00, sizeof(*fcb));
+ memset(fcb + 1, 0xff, mtd->erasesize - fcb_offs - sizeof(*fcb));
+
+ strncpy((char *)&fcb->fingerprint, "FCB ", 4);
+ fcb->version = cpu_to_be32(1);
+
+ fcb->timing.data_setup = BF_VAL(t0, GPMI_TIMING0_DATA_SETUP);
+ fcb->timing.data_hold = BF_VAL(t0, GPMI_TIMING0_DATA_HOLD);
+ fcb->timing.address_setup = BF_VAL(t0, GPMI_TIMING0_ADDRESS_SETUP);
+
+ fcb->page_data_size = mtd->writesize;
+ fcb->total_page_size = mtd->writesize + mtd->oobsize;
+ fcb->sectors_per_block = mtd->erasesize / mtd->writesize;
+
+ fcb->ecc_block0_type = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0);
+ fcb->ecc_block0_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_DATA0_SIZE);
+ fcb->ecc_blockn_type = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN);
+ fcb->ecc_blockn_size = BF_VAL(fl1, BCH_FLASHLAYOUT1_DATAN_SIZE);
+
+ fcb->metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
+ fcb->ecc_blocks_per_page = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS);
+ fcb->bch_mode = readl(bch_regs + BCH_MODE);
+/*
+ fcb->boot_patch = 0;
+ fcb->patch_sectors = 0;
+*/
+ fcb->fw1_start_page = fw1_start_block / mtd->writesize;
+ fcb->fw1_sectors = DIV_ROUND_UP(fw_size, mtd->writesize);
+
+ if (fw2_start_block != 0 && fw2_start_block < mtd->size / mtd->erasesize) {
+ fcb->fw2_start_page = fw2_start_block / mtd->writesize;
+ fcb->fw2_sectors = fcb->fw1_sectors;
+ }
+
+ fcb->dbbt_search_area = 1;
+
+ bb_mark_bit_offs = calc_bb_offset(mtd, fcb);
+ if (bb_mark_bit_offs < 0)
+ return ERR_PTR(bb_mark_bit_offs);
+ fcb->bb_mark_byte = bb_mark_bit_offs / 8;
+ fcb->bb_mark_startbit = bb_mark_bit_offs % 8;
+ fcb->bb_mark_phys_offset = mtd->writesize;
+
+ fcb->checksum = calc_chksum(&fcb->fingerprint, 512 - 4);
+ return fcb;
+}
+
+static int find_fcb(struct mtd_info *mtd, void *ref, int page)
+{
+ int ret = 0;
+ struct nand_chip *chip = mtd->priv;
+ void *buf = malloc(mtd->erasesize);
+
+ if (buf == NULL)
+ return -ENOMEM;
+
+ chip->select_chip(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ ret = chip->ecc.read_page_raw(mtd, chip, buf);
+ if (ret) {
+ printf("Failed to read FCB from page %u: %d\n", page, ret);
+ return ret;
+ }
+ chip->select_chip(mtd, -1);
+ if (memcmp(buf, ref, mtd->writesize) == 0) {
+ printf("%s: Found FCB in page %u (%08x)\n", __func__,
+ page, page * mtd->writesize);
+ ret = 1;
+ }
+ free(buf);
+ return ret;
+}
+
+static int write_fcb(struct mtd_info *mtd, void *buf, int block)
+{
+ int ret;
+ struct nand_chip *chip = mtd->priv;
+ int page = block / mtd->writesize;
+ struct erase_info erase_opts = {
+ .mtd = mtd,
+ .addr = block,
+ .len = mtd->erasesize,
+ .callback = NULL,
+ };
+
+ ret = find_fcb(mtd, buf, page);
+ if (ret > 0) {
+ printf("FCB at block %08x is up to date\n", block);
+ return 0;
+ }
+
+ ret = mtd->erase(mtd, &erase_opts);
+ if (ret) {
+ printf("Failed to erase FCB block %08x\n", block);
+ return ret;
+ }
+
+ printf("Writing FCB to block %08x\n", block);
+ chip->select_chip(mtd, 0);
+ ret = chip->write_page(mtd, chip, buf, page, 0, 1);
+ if (ret) {
+ printf("Failed to write FCB to block %08x: %d\n", block, ret);
+ }
+ chip->select_chip(mtd, -1);
+ return ret;
+}
+
+int update_bcb(int argc, char *argv[])
+{
+ int ret;
+ int block;
+ void *buf;
+ struct mx28_fcb *fcb;
+ struct cdev *tmp_cdev, *bcb_cdev, *firmware_cdev;
+ unsigned long fw2_offset = 0;
+ struct mtd_info *mtd;
+ unsigned fcb_written = 0;
+
+ if (argc == 1)
+ return COMMAND_ERROR_USAGE;
+
+ tmp_cdev = cdev_by_name("nand0");
+ if (!tmp_cdev || !tmp_cdev->mtd) {
+ pr_err("%s: No NAND device!\n", __func__);
+ return -ENODEV;
+ }
+
+ mtd = tmp_cdev->mtd;
+
+ bcb_cdev = cdev_by_name("nand0.bcb");
+ if (!bcb_cdev) {
+ pr_err("%s: No FCB device!\n", __func__);
+ return -ENODEV;
+ }
+
+ firmware_cdev = cdev_by_name(argv[1]);
+ if (!firmware_cdev) {
+ pr_err("%s: Bootstream-Image not found!\n", __func__);
+ return -ENODEV;
+ }
+
+ if (argc > 2) {
+ tmp_cdev = cdev_by_name(argv[2]);
+ if (!tmp_cdev) {
+ pr_err("%s: Redundant Bootstream-Image not found!\n", __func__);
+ return -ENODEV;
+ }
+ fw2_offset = tmp_cdev->offset;
+ }
+
+ buf = malloc(mtd->erasesize);
+ if (!buf)
+ return -ENOMEM;
+
+ fcb = create_fcb(mtd, buf, firmware_cdev->offset, firmware_cdev->size, fw2_offset);
+ if (IS_ERR(fcb)) {
+ printf("Failed to initialize FCB: %ld\n", PTR_ERR(fcb));
+ return PTR_ERR(fcb);
+ }
+ encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
+
+ for (block = bcb_cdev->offset; block < bcb_cdev->offset + bcb_cdev->size / 2;
+ block += mtd->erasesize) {
+
+ if (nand_isbad_bbt(mtd, block, false))
+ continue;
+
+ ret = write_fcb(mtd, buf, block);
+ if (ret) {
+ printf("Failed to write FCB to block %u\n", block);
+ return ret;
+ }
+
+ fcb_written++;
+ }
+
+ return fcb_written ? 0 : -ENOSPC;
+}
+
+BAREBOX_CMD_HELP_START(bcb)
+BAREBOX_CMD_HELP_USAGE("bcb <first_bootstream> [second_bootstream]\n")
+BAREBOX_CMD_HELP_SHORT("Write a BCB to NAND flash which an MX23/28 needs to boot.\n")
+BAREBOX_CMD_HELP_TEXT ("Example: bcb nand0.bootstream\n")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(bcb)
+ .cmd = update_bcb,
+ .usage = "Writes a MX23/28 BCB data structure to flash",
+ BAREBOX_CMD_HELP(cmd_bcb_help)
+BAREBOX_CMD_END
diff --git a/arch/arm/mach-mxs/common.c b/arch/arm/mach-mxs/common.c
new file mode 100644
index 0000000000..3730633c71
--- /dev/null
+++ b/arch/arm/mach-mxs/common.c
@@ -0,0 +1,33 @@
+#include <common.h>
+#include <io.h>
+#include <mach/mxs.h>
+#include <mach/imx-regs.h>
+
+#define MXS_BLOCK_SFTRST (1 << 31)
+#define MXS_BLOCK_CLKGATE (1 << 30)
+
+int mxs_reset_block(void __iomem *reg, int just_enable)
+{
+ /* Clear SFTRST */
+ writel(MXS_BLOCK_SFTRST, reg + BIT_CLR);
+ mdelay(1);
+
+ /* Clear CLKGATE */
+ writel(MXS_BLOCK_CLKGATE, reg + BIT_CLR);
+
+ if (!just_enable) {
+ /* Set SFTRST */
+ writel(MXS_BLOCK_SFTRST, reg + BIT_SET);
+ mdelay(1);
+ }
+
+ /* Clear SFTRST */
+ writel(MXS_BLOCK_SFTRST, reg + BIT_CLR);
+ mdelay(1);
+
+ /* Clear CLKGATE */
+ writel(MXS_BLOCK_CLKGATE, reg + BIT_CLR);
+ mdelay(1);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mxs/include/mach/clock-imx23.h b/arch/arm/mach-mxs/include/mach/clock-imx23.h
index 4f20e09d3d..650779278d 100644
--- a/arch/arm/mach-mxs/include/mach/clock-imx23.h
+++ b/arch/arm/mach-mxs/include/mach/clock-imx23.h
@@ -25,5 +25,6 @@ unsigned imx_set_sspclk(unsigned, unsigned, int);
unsigned imx_set_ioclk(unsigned);
unsigned imx_set_lcdifclk(unsigned);
unsigned imx_get_lcdifclk(void);
+void imx_enable_nandclk(void);
#endif /* MACH_CLOCK_IMX23_H */
diff --git a/arch/arm/mach-mxs/include/mach/clock-imx28.h b/arch/arm/mach-mxs/include/mach/clock-imx28.h
index 613c97b916..0604f0a557 100644
--- a/arch/arm/mach-mxs/include/mach/clock-imx28.h
+++ b/arch/arm/mach-mxs/include/mach/clock-imx28.h
@@ -27,6 +27,7 @@ unsigned imx_set_lcdifclk(unsigned);
unsigned imx_get_lcdifclk(void);
unsigned imx_get_fecclk(void);
void imx_enable_enetclk(void);
+void imx_enable_nandclk(void);
#endif /* MACH_CLOCK_IMX28_H */
diff --git a/arch/arm/mach-mxs/include/mach/dma.h b/arch/arm/mach-mxs/include/mach/dma.h
new file mode 100644
index 0000000000..52747e2fbf
--- /dev/null
+++ b/arch/arm/mach-mxs/include/mach/dma.h
@@ -0,0 +1,145 @@
+/*
+ * Freescale i.MX28 APBH DMA
+ *
+ * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
+ * on behalf of DENX Software Engineering GmbH
+ *
+ * Based on code from LTIB:
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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
+ *
+ */
+
+#ifndef __DMA_H__
+#define __DMA_H__
+
+#include <linux/list.h>
+
+#ifndef CONFIG_ARCH_DMA_PIO_WORDS
+#define DMA_PIO_WORDS 15
+#else
+#define DMA_PIO_WORDS CONFIG_ARCH_DMA_PIO_WORDS
+#endif
+
+#define MXS_DMA_ALIGNMENT 32
+
+/*
+ * MXS DMA channels
+ */
+enum {
+ MXS_DMA_CHANNEL_AHB_APBH_SSP0 = 0,
+ MXS_DMA_CHANNEL_AHB_APBH_SSP1,
+ MXS_DMA_CHANNEL_AHB_APBH_SSP2,
+ MXS_DMA_CHANNEL_AHB_APBH_SSP3,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI0,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI1,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI2,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI3,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI4,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI5,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI6,
+ MXS_DMA_CHANNEL_AHB_APBH_GPMI7,
+ MXS_DMA_CHANNEL_AHB_APBH_SSP,
+ MXS_MAX_DMA_CHANNELS,
+};
+
+/*
+ * MXS DMA hardware command.
+ *
+ * This structure describes the in-memory layout of an entire DMA command,
+ * including space for the maximum number of PIO accesses. See the appropriate
+ * reference manual for a detailed description of what these fields mean to the
+ * DMA hardware.
+ */
+#define MXS_DMA_DESC_COMMAND_MASK 0x3
+#define MXS_DMA_DESC_COMMAND_OFFSET 0
+#define MXS_DMA_DESC_COMMAND_NO_DMAXFER 0x0
+#define MXS_DMA_DESC_COMMAND_DMA_WRITE 0x1
+#define MXS_DMA_DESC_COMMAND_DMA_READ 0x2
+#define MXS_DMA_DESC_COMMAND_DMA_SENSE 0x3
+#define MXS_DMA_DESC_CHAIN (1 << 2)
+#define MXS_DMA_DESC_IRQ (1 << 3)
+#define MXS_DMA_DESC_NAND_LOCK (1 << 4)
+#define MXS_DMA_DESC_NAND_WAIT_4_READY (1 << 5)
+#define MXS_DMA_DESC_DEC_SEM (1 << 6)
+#define MXS_DMA_DESC_WAIT4END (1 << 7)
+#define MXS_DMA_DESC_HALT_ON_TERMINATE (1 << 8)
+#define MXS_DMA_DESC_TERMINATE_FLUSH (1 << 9)
+#define MXS_DMA_DESC_PIO_WORDS_MASK (0xf << 12)
+#define MXS_DMA_DESC_PIO_WORDS_OFFSET 12
+#define MXS_DMA_DESC_BYTES_MASK (0xffff << 16)
+#define MXS_DMA_DESC_BYTES_OFFSET 16
+
+struct mxs_dma_cmd {
+ unsigned long next;
+ unsigned long data;
+ union {
+ dma_addr_t address;
+ unsigned long alternate;
+ };
+ unsigned long pio_words[DMA_PIO_WORDS];
+};
+
+/*
+ * MXS DMA command descriptor.
+ *
+ * This structure incorporates an MXS DMA hardware command structure, along
+ * with metadata.
+ */
+#define MXS_DMA_DESC_FIRST (1 << 0)
+#define MXS_DMA_DESC_LAST (1 << 1)
+#define MXS_DMA_DESC_READY (1 << 31)
+
+struct mxs_dma_desc {
+ struct mxs_dma_cmd cmd;
+ unsigned int flags;
+ dma_addr_t address;
+ void *buffer;
+ struct list_head node;
+};
+
+/**
+ * MXS DMA channel
+ *
+ * This structure represents a single DMA channel. The MXS platform code
+ * maintains an array of these structures to represent every DMA channel in the
+ * system (see mxs_dma_channels).
+ */
+#define MXS_DMA_FLAGS_IDLE 0
+#define MXS_DMA_FLAGS_BUSY (1 << 0)
+#define MXS_DMA_FLAGS_FREE 0
+#define MXS_DMA_FLAGS_ALLOCATED (1 << 16)
+#define MXS_DMA_FLAGS_VALID (1 << 31)
+
+struct mxs_dma_chan {
+ const char *name;
+ unsigned long dev;
+ struct mxs_dma_device *dma;
+ unsigned int flags;
+ unsigned int active_num;
+ unsigned int pending_num;
+ struct list_head active;
+ struct list_head done;
+};
+
+struct mxs_dma_desc *mxs_dma_desc_alloc(void);
+void mxs_dma_desc_free(struct mxs_dma_desc *);
+int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc);
+
+int mxs_dma_go(int chan);
+int mxs_dma_init(void);
+
+#endif /* __DMA_H__ */
diff --git a/arch/arm/mach-mxs/include/mach/imx23-regs.h b/arch/arm/mach-mxs/include/mach/imx23-regs.h
index 60f5bf9d6f..7ea3057513 100644
--- a/arch/arm/mach-mxs/include/mach/imx23-regs.h
+++ b/arch/arm/mach-mxs/include/mach/imx23-regs.h
@@ -27,6 +27,9 @@
#endif
#define IMX_MEMORY_BASE 0x40000000
+#define MXS_APBH_BASE 0x80004000
+#define MXS_BCH_BASE 0x8000a000
+#define MXS_GPMI_BASE 0x8000c000
#define IMX_UART1_BASE 0x8006c000
#define IMX_UART2_BASE 0x8006e000
#define IMX_DBGUART_BASE 0x80070000
diff --git a/arch/arm/mach-mxs/include/mach/imx28-regs.h b/arch/arm/mach-mxs/include/mach/imx28-regs.h
index 900990a381..16bf5f7a60 100644
--- a/arch/arm/mach-mxs/include/mach/imx28-regs.h
+++ b/arch/arm/mach-mxs/include/mach/imx28-regs.h
@@ -23,7 +23,9 @@
#define IMX_SRAM_BASE 0x00000000
#define IMX_MEMORY_BASE 0x40000000
-#define IMX_NFC_BASE 0x8000C000
+#define MXS_APBH_BASE 0x80004000
+#define MXS_BCH_BASE 0x8000a000
+#define MXS_GPMI_BASE 0x8000c000
#define IMX_SSP0_BASE 0x80010000
#define IMX_SSP1_BASE 0x80012000
#define IMX_SSP2_BASE 0x80014000
diff --git a/arch/arm/mach-mxs/include/mach/mxs.h b/arch/arm/mach-mxs/include/mach/mxs.h
new file mode 100644
index 0000000000..182ed8a998
--- /dev/null
+++ b/arch/arm/mach-mxs/include/mach/mxs.h
@@ -0,0 +1,6 @@
+#ifndef __MACH_MXS_H
+#define __MACH_MXS_H
+
+int mxs_reset_block(void __iomem *reg, int just_enable);
+
+#endif /* __MACH_MXS_H */
diff --git a/arch/arm/mach-mxs/speed-imx23.c b/arch/arm/mach-mxs/speed-imx23.c
index f41b9bc75a..5d0c90dc8c 100644
--- a/arch/arm/mach-mxs/speed-imx23.c
+++ b/arch/arm/mach-mxs/speed-imx23.c
@@ -47,6 +47,8 @@
# define GET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK)
# define SET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK)
#define HW_CLKCTRL_GPMI 0x080
+# define CLKCTRL_GPMI_CLKGATE (1 << 31)
+# define CLKCTRL_GPMI_DIV_MASK 0x3ff
/* note: no set/clear register! */
#define HW_CLKCTRL_SPDIF 0x090
/* note: no set/clear register! */
@@ -288,6 +290,23 @@ unsigned imx_set_sspclk(unsigned index, unsigned nc, int high)
return imx_get_sspclk(index);
}
+void imx_enable_nandclk(void)
+{
+ uint32_t reg;
+
+ /* Clear bypass bit; refman says clear, but fsl-code does set. Hooray! */
+ writel(CLKCTRL_CLKSEQ_BYPASS_GPMI,
+ IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_SET);
+
+ reg = readl(IMX_CCM_BASE + HW_CLKCTRL_GPMI) & ~CLKCTRL_GPMI_CLKGATE;
+ writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI);
+ udelay(1000);
+ /* Initialize DIV to 1 */
+ reg &= ~CLKCTRL_GPMI_DIV_MASK;
+ reg |= 1;
+ writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI);
+}
+
void imx_dump_clocks(void)
{
printf("mpll: %10u kHz\n", imx_get_mpllclk() / 1000);
diff --git a/arch/arm/mach-mxs/speed-imx28.c b/arch/arm/mach-mxs/speed-imx28.c
index 2641fb6fb2..df55f64c24 100644
--- a/arch/arm/mach-mxs/speed-imx28.c
+++ b/arch/arm/mach-mxs/speed-imx28.c
@@ -48,6 +48,8 @@
# define GET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK)
# define SET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK)
#define HW_CLKCTRL_GPMI 0x0d0
+# define CLKCTRL_GPMI_CLKGATE (1 << 31)
+# define CLKCTRL_GPMI_DIV_MASK 0x3ff
/* note: no set/clear register! */
#define HW_CLKCTRL_SPDIF 0x0e0
/* note: no set/clear register! */
@@ -398,6 +400,23 @@ void imx_enable_enetclk(void)
IMX_CCM_BASE + HW_CLKCTRL_ENET);
}
+void imx_enable_nandclk(void)
+{
+ uint32_t reg;
+
+ /* Clear bypass bit; refman says clear, but fsl-code does set. Hooray! */
+ writel(CLKCTRL_CLKSEQ_BYPASS_GPMI,
+ IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_SET);
+
+ reg = readl(IMX_CCM_BASE + HW_CLKCTRL_GPMI) & ~CLKCTRL_GPMI_CLKGATE;
+ writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI);
+ udelay(1000);
+ /* Initialize DIV to 1 */
+ reg &= ~CLKCTRL_GPMI_DIV_MASK;
+ reg |= 1;
+ writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI);
+}
+
void imx_dump_clocks(void)
{
printf("mpll: %10u kHz\n", imx_get_mpllclk() / 1000);