summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/systemace.c38
-rw-r--r--drivers/bootcount/Makefile45
-rw-r--r--drivers/bootcount/bootcount.c100
-rw-r--r--drivers/bootcount/bootcount_at91.c43
-rw-r--r--drivers/bootcount/bootcount_blackfin.c34
-rw-r--r--drivers/bootcount/bootcount_davinci.c49
-rw-r--r--drivers/bootcount/bootcount_ram.c72
-rw-r--r--drivers/dfu/Makefile44
-rw-r--r--drivers/dfu/dfu.c238
-rw-r--r--drivers/dfu/dfu_mmc.c182
-rw-r--r--drivers/dma/apbh_dma.c38
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/bcm2835_gpio.c89
-rw-r--r--drivers/gpio/db8500_gpio.c221
-rw-r--r--drivers/gpio/mxc_gpio.c10
-rw-r--r--drivers/gpio/mxs_gpio.c16
-rw-r--r--drivers/gpio/omap_gpio.c243
-rw-r--r--drivers/gpio/tegra_gpio.c12
-rw-r--r--drivers/i2c/mxs_i2c.c14
-rw-r--r--drivers/i2c/omap24xx_i2c.c66
-rw-r--r--drivers/i2c/tegra_i2c.c12
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/key_matrix.c1
-rw-r--r--drivers/mmc/Makefile4
-rw-r--r--drivers/mmc/arm_pl180_mmci.c133
-rw-r--r--drivers/mmc/arm_pl180_mmci.h27
-rw-r--r--drivers/mmc/fsl_esdhc.c5
-rw-r--r--drivers/mmc/mmc.c48
-rw-r--r--drivers/mmc/mxsmmc.c208
-rw-r--r--drivers/mmc/pxa_mmc_gen.c8
-rw-r--r--drivers/mmc/s5p_sdhci.c18
-rw-r--r--drivers/mmc/sdhci.c30
-rw-r--r--drivers/mmc/sh_mmcif.c2
-rw-r--r--drivers/mmc/spl_mmc_load.c62
-rw-r--r--drivers/mmc/tegra_mmc.c34
-rw-r--r--drivers/mmc/tegra_mmc.h131
-rw-r--r--drivers/mtd/cfi_flash.c37
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/atmel_nand.c827
-rw-r--r--drivers/mtd/nand/atmel_nand_ecc.h113
-rw-r--r--drivers/mtd/nand/mxc_nand.c337
-rw-r--r--drivers/mtd/nand/mxs_nand.c20
-rw-r--r--drivers/mtd/nand/nand_base.c13
-rw-r--r--drivers/mtd/nand/nand_util.c26
-rw-r--r--drivers/mtd/nand/omap_gpmc.c1
-rw-r--r--drivers/mtd/nand/tegra_nand.c1026
-rw-r--r--drivers/mtd/nand/tegra_nand.h257
-rw-r--r--drivers/mtd/spi/spansion.c7
-rw-r--r--drivers/mtd/spi/stmicro.c39
-rw-r--r--drivers/mtd/spi/winbond.c7
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/cpsw.c991
-rw-r--r--drivers/net/greth.c10
-rw-r--r--drivers/net/macb.c6
-rw-r--r--drivers/pci/pci.c122
-rw-r--r--drivers/pci/pci_auto.c104
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/imxdi.c244
-rw-r--r--drivers/rtc/mx27rtc.c83
-rw-r--r--drivers/rtc/mxsrtc.c10
-rw-r--r--drivers/rtc/pcf8563.c4
-rw-r--r--drivers/serial/atmel_usart.c11
-rw-r--r--drivers/serial/ns16550.c2
-rw-r--r--drivers/serial/serial.c3
-rw-r--r--drivers/serial/serial_pl01x.c2
-rw-r--r--drivers/serial/serial_xuartlite.c34
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/atmel_spi.c3
-rw-r--r--drivers/spi/atmel_spi.h1
-rw-r--r--drivers/spi/cf_qspi.c373
-rw-r--r--drivers/spi/kirkwood_spi.c5
-rw-r--r--drivers/spi/mpc8xxx_spi.c4
-rw-r--r--drivers/spi/mxc_spi.c6
-rw-r--r--drivers/spi/mxs_spi.c252
-rw-r--r--drivers/spi/omap3_spi.c16
-rw-r--r--drivers/spi/omap3_spi.h5
-rw-r--r--drivers/spi/tegra_spi.c10
-rw-r--r--drivers/spi/xilinx_spi.c1
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/f_dfu.c749
-rw-r--r--drivers/usb/gadget/f_dfu.h100
-rw-r--r--drivers/usb/gadget/g_dnl.c202
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-hcd.c312
-rw-r--r--drivers/usb/host/ehci-mxs.c26
-rw-r--r--drivers/usb/host/ehci.h60
-rw-r--r--drivers/usb/host/ohci-da8xx.c48
-rw-r--r--drivers/usb/host/ohci-hcd.c70
-rw-r--r--drivers/usb/musb/da8xx.c3
-rw-r--r--drivers/usb/musb/da8xx.h102
-rw-r--r--drivers/usb/musb/musb_hcd.c2
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/exynos_dp.c925
-rw-r--r--drivers/video/exynos_dp_lowlevel.c1291
-rw-r--r--drivers/video/exynos_dp_lowlevel.h80
-rw-r--r--drivers/video/exynos_fb.c5
-rw-r--r--drivers/video/exynos_fimd.c91
-rw-r--r--drivers/video/exynos_pwm_bl.c57
-rw-r--r--drivers/video/omap3_dss.c3
99 files changed, 10136 insertions, 1294 deletions
diff --git a/drivers/block/systemace.c b/drivers/block/systemace.c
index 58402b9289..247cf060e4 100644
--- a/drivers/block/systemace.c
+++ b/drivers/block/systemace.c
@@ -51,24 +51,36 @@
* to be the base address for the chip, usually in the local
* peripheral bus.
*/
-#if (CONFIG_SYS_SYSTEMACE_WIDTH == 8)
+
+static u32 base = CONFIG_SYS_SYSTEMACE_BASE;
+static u32 width = CONFIG_SYS_SYSTEMACE_WIDTH;
+
+static void ace_writew(u16 val, unsigned off)
+{
+ if (width == 8) {
#if !defined(__BIG_ENDIAN)
-#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)<<8) | \
- (readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)))
-#define ace_writew(val, off) {writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off); \
- writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off+1);}
+ writeb(val >> 8, base + off);
+ writeb(val, base + off + 1);
#else
-#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)) | \
- (readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)<<8))
-#define ace_writew(val, off) {writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off); \
- writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off+1);}
+ writeb(val, base + off);
+ writeb(val >> 8, base + off + 1);
#endif
+ }
+ out16(base + off, val);
+}
+
+static u16 ace_readw(unsigned off)
+{
+ if (width == 8) {
+#if !defined(__BIG_ENDIAN)
+ return (readb(base + off) << 8) | readb(base + off + 1);
#else
-#define ace_readw(off) (in16(CONFIG_SYS_SYSTEMACE_BASE+off))
-#define ace_writew(val, off) (out16(CONFIG_SYS_SYSTEMACE_BASE+off,val))
+ return readb(base + off) | (readb(base + off + 1) << 8);
#endif
+ }
-/* */
+ return in16(base + off);
+}
static unsigned long systemace_read(int dev, unsigned long start,
unsigned long blkcnt, void *buffer);
@@ -121,7 +133,7 @@ block_dev_desc_t *systemace_get_dev(int dev)
/*
* Ensure the correct bus mode (8/16 bits) gets enabled
*/
- ace_writew(CONFIG_SYS_SYSTEMACE_WIDTH == 8 ? 0 : 0x0001, 0);
+ ace_writew(width == 8 ? 0 : 0x0001, 0);
init_part(&systemace_dev);
diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile
new file mode 100644
index 0000000000..a8d0ac7a3e
--- /dev/null
+++ b/drivers/bootcount/Makefile
@@ -0,0 +1,45 @@
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB := $(obj)libbootcount.o
+
+COBJS-y += bootcount.o
+COBJS-$(CONFIG_AT91SAM9XE) += bootcount_at91.o
+COBJS-$(CONFIG_BLACKFIN) += bootcount_blackfin.o
+COBJS-$(CONFIG_SOC_DA8XX) += bootcount_davinci.o
+COBJS-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+########################################################################
diff --git a/drivers/bootcount/bootcount.c b/drivers/bootcount/bootcount.c
new file mode 100644
index 0000000000..80eb71763f
--- /dev/null
+++ b/drivers/bootcount/bootcount.c
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 2010-2012
+ * Stefan Roese, DENX Software Engineering, sr@denx.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
+ */
+
+#include <bootcount.h>
+#include <linux/compiler.h>
+
+/*
+ * Only override CONFIG_SYS_BOOTCOUNT_ADDR if not already defined. This
+ * way, some boards can define it directly in their config header.
+ */
+#if !defined(CONFIG_SYS_BOOTCOUNT_ADDR)
+
+#if defined(CONFIG_MPC5xxx)
+#define CONFIG_SYS_BOOTCOUNT_ADDR (MPC5XXX_CDM_BRDCRMB)
+#define CONFIG_SYS_BOOTCOUNT_SINGLEWORD
+#endif /* defined(CONFIG_MPC5xxx) */
+
+#if defined(CONFIG_MPC512X)
+#define CONFIG_SYS_BOOTCOUNT_ADDR (&((immap_t *)CONFIG_SYS_IMMR)->clk.bcr)
+#define CONFIG_SYS_BOOTCOUNT_SINGLEWORD
+#endif /* defined(CONFIG_MPC512X) */
+
+#if defined(CONFIG_8xx)
+#define CONFIG_SYS_BOOTCOUNT_ADDR (((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + \
+ CPM_BOOTCOUNT_ADDR)
+#endif /* defined(CONFIG_8xx) */
+
+#if defined(CONFIG_MPC8260)
+#include <asm/cpm_8260.h>
+
+#define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR)
+#endif /* defined(CONFIG_MPC8260) */
+
+#if defined(CONFIG_QE)
+#include <asm/immap_qe.h>
+
+#define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_IMMR + 0x110000 + \
+ QE_MURAM_SIZE - 2 * sizeof(u32))
+#endif /* defined(CONFIG_MPC8360) */
+
+#if defined(CONFIG_4xx)
+#define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_OCM_DATA_ADDR + \
+ CONFIG_SYS_BOOTCOUNT_ADDR)
+#endif /* defined(CONFIG_4xx) */
+
+#endif /* !defined(CONFIG_SYS_BOOTCOUNT_ADDR) */
+
+/* Now implement the generic default functions */
+#if defined(CONFIG_SYS_BOOTCOUNT_ADDR)
+__weak void bootcount_store(ulong a)
+{
+ void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR;
+
+#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD)
+ raw_bootcount_store(reg, (BOOTCOUNT_MAGIC & 0xffff0000) | a);
+#else
+ raw_bootcount_store(reg, a);
+ raw_bootcount_store(reg + 4, BOOTCOUNT_MAGIC);
+#endif
+}
+
+__weak ulong bootcount_load(void)
+{
+ void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR;
+
+#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD)
+ u32 tmp = raw_bootcount_load(reg);
+
+ if ((tmp & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000))
+ return 0;
+ else
+ return (tmp & 0x0000ffff);
+#else
+ if (raw_bootcount_load(reg + 4) != BOOTCOUNT_MAGIC)
+ return 0;
+ else
+ return raw_bootcount_load(reg);
+#endif
+}
+#endif
diff --git a/drivers/bootcount/bootcount_at91.c b/drivers/bootcount/bootcount_at91.c
new file mode 100644
index 0000000000..7cfe14d370
--- /dev/null
+++ b/drivers/bootcount/bootcount_at91.c
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/at91_gpbr.h>
+
+/*
+ * We combine the BOOTCOUNT_MAGIC and bootcount in one 32-bit register.
+ * This is done so we need to use only one of the four GPBR registers.
+ */
+void bootcount_store(ulong a)
+{
+ at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
+
+ writel((BOOTCOUNT_MAGIC & 0xffff0000) | (a & 0x0000ffff),
+ &gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]);
+}
+
+ulong bootcount_load(void)
+{
+ at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
+
+ ulong val = readl(&gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]);
+ if ((val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000))
+ return 0;
+ else
+ return val & 0x0000ffff;
+}
diff --git a/drivers/bootcount/bootcount_blackfin.c b/drivers/bootcount/bootcount_blackfin.c
new file mode 100644
index 0000000000..6cf6dd58b6
--- /dev/null
+++ b/drivers/bootcount/bootcount_blackfin.c
@@ -0,0 +1,34 @@
+/*
+ * functions for handling bootcount support
+ *
+ * Copyright (c) 2010 Analog Devices Inc.
+ *
+ * Licensed under the 2-clause BSD.
+ */
+
+/* This version uses one 32bit storage and combines the magic/count */
+
+#include <common.h>
+
+/* We abuse the EVT0 MMR for bootcount storage by default */
+#ifndef CONFIG_SYS_BOOTCOUNT_ADDR
+# define CONFIG_SYS_BOOTCOUNT_ADDR EVT0
+#endif
+
+#define MAGIC_MASK 0xffff0000
+#define COUNT_MASK 0x0000ffff
+
+void bootcount_store(ulong cnt)
+{
+ ulong magic = (BOOTCOUNT_MAGIC & MAGIC_MASK) | (cnt & COUNT_MASK);
+ bfin_write32(CONFIG_SYS_BOOTCOUNT_ADDR, magic);
+}
+
+ulong bootcount_load(void)
+{
+ ulong magic = bfin_read32(CONFIG_SYS_BOOTCOUNT_ADDR);
+ if ((magic & MAGIC_MASK) == (BOOTCOUNT_MAGIC & MAGIC_MASK))
+ return magic & COUNT_MASK;
+ else
+ return 0;
+}
diff --git a/drivers/bootcount/bootcount_davinci.c b/drivers/bootcount/bootcount_davinci.c
new file mode 100644
index 0000000000..1cd943630a
--- /dev/null
+++ b/drivers/bootcount/bootcount_davinci.c
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 2011
+ * Heiko Schocher, DENX Software Engineering, hs@denx.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.
+ *
+ */
+
+#include <bootcount.h>
+#include <asm/arch/da850_lowlevel.h>
+#include <asm/arch/davinci_misc.h>
+
+void bootcount_store(ulong a)
+{
+ struct davinci_rtc *reg =
+ (struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
+
+ /*
+ * write RTC kick register to enable write
+ * for RTC Scratch registers. Scratch0 and 1 are
+ * used for bootcount values.
+ */
+ writel(RTC_KICK0R_WE, &reg->kick0r);
+ writel(RTC_KICK1R_WE, &reg->kick1r);
+ raw_bootcount_store(&reg->scratch0, a);
+ raw_bootcount_store(&reg->scratch1, BOOTCOUNT_MAGIC);
+}
+
+ulong bootcount_load(void)
+{
+ struct davinci_rtc *reg =
+ (struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
+
+ if (raw_bootcount_load(&reg->scratch1) != BOOTCOUNT_MAGIC)
+ return 0;
+ else
+ return raw_bootcount_load(&reg->scratch0);
+}
diff --git a/drivers/bootcount/bootcount_ram.c b/drivers/bootcount/bootcount_ram.c
new file mode 100644
index 0000000000..8655af7eab
--- /dev/null
+++ b/drivers/bootcount/bootcount_ram.c
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 2010
+ * Heiko Schocher, DENX Software Engineering, hs@denx.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.
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+const ulong patterns[] = { 0x00000000,
+ 0xFFFFFFFF,
+ 0xFF00FF00,
+ 0x0F0F0F0F,
+ 0xF0F0F0F0};
+const ulong NBR_OF_PATTERNS = sizeof(patterns) / sizeof(*patterns);
+const ulong OFFS_PATTERN = 3;
+const ulong REPEAT_PATTERN = 1000;
+
+void bootcount_store(ulong a)
+{
+ ulong *save_addr;
+ ulong size = 0;
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
+ size += gd->bd->bi_dram[i].size;
+ save_addr = (ulong *)(size - BOOTCOUNT_ADDR);
+ writel(a, save_addr);
+ writel(BOOTCOUNT_MAGIC, &save_addr[1]);
+
+ for (i = 0; i < REPEAT_PATTERN; i++)
+ writel(patterns[i % NBR_OF_PATTERNS],
+ &save_addr[i + OFFS_PATTERN]);
+
+}
+
+ulong bootcount_load(void)
+{
+ ulong *save_addr;
+ ulong size = 0;
+ ulong counter = 0;
+ int i, tmp;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
+ size += gd->bd->bi_dram[i].size;
+ save_addr = (ulong *)(size - BOOTCOUNT_ADDR);
+
+ counter = readl(&save_addr[0]);
+
+ /* Is the counter reliable, check in the big pattern for bit errors */
+ for (i = 0; (i < REPEAT_PATTERN) && (counter != 0); i++) {
+ tmp = readl(&save_addr[i + OFFS_PATTERN]);
+ if (tmp != patterns[i % NBR_OF_PATTERNS])
+ counter = 0;
+ }
+ return counter;
+}
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile
new file mode 100644
index 0000000000..7b717bce28
--- /dev/null
+++ b/drivers/dfu/Makefile
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2012 Samsung Electronics
+# Lukasz Majewski <l.majewski@samsung.com>
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB = $(obj)libdfu.o
+
+COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o
+COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
+
+SRCS := $(COBJS-y:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS-y))
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c
new file mode 100644
index 0000000000..e8477fb7c7
--- /dev/null
+++ b/drivers/dfu/dfu.c
@@ -0,0 +1,238 @@
+/*
+ * dfu.c -- DFU back-end routines
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <fat.h>
+#include <dfu.h>
+#include <linux/list.h>
+#include <linux/compiler.h>
+
+static LIST_HEAD(dfu_list);
+static int dfu_alt_num;
+
+static int dfu_find_alt_num(const char *s)
+{
+ int i = 0;
+
+ for (; *s; s++)
+ if (*s == ';')
+ i++;
+
+ return ++i;
+}
+
+static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
+ dfu_buf[DFU_DATA_BUF_SIZE];
+
+int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
+{
+ static unsigned char *i_buf;
+ static int i_blk_seq_num;
+ long w_size = 0;
+ int ret = 0;
+
+ debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
+ __func__, dfu->name, buf, size, blk_seq_num, i_buf);
+
+ if (blk_seq_num == 0) {
+ i_buf = dfu_buf;
+ i_blk_seq_num = 0;
+ }
+
+ if (i_blk_seq_num++ != blk_seq_num) {
+ printf("%s: Wrong sequence number! [%d] [%d]\n",
+ __func__, i_blk_seq_num, blk_seq_num);
+ return -1;
+ }
+
+ memcpy(i_buf, buf, size);
+ i_buf += size;
+
+ if (size == 0) {
+ /* Integrity check (if needed) */
+ debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name,
+ i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf));
+
+ w_size = i_buf - dfu_buf;
+ ret = dfu->write_medium(dfu, dfu_buf, &w_size);
+ if (ret)
+ debug("%s: Write error!\n", __func__);
+
+ i_blk_seq_num = 0;
+ i_buf = NULL;
+ return ret;
+ }
+
+ return ret;
+}
+
+int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
+{
+ static unsigned char *i_buf;
+ static int i_blk_seq_num;
+ static long r_size;
+ static u32 crc;
+ int ret = 0;
+
+ debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
+ __func__, dfu->name, buf, size, blk_seq_num, i_buf);
+
+ if (blk_seq_num == 0) {
+ i_buf = dfu_buf;
+ ret = dfu->read_medium(dfu, i_buf, &r_size);
+ debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size);
+ i_blk_seq_num = 0;
+ /* Integrity check (if needed) */
+ crc = crc32(0, dfu_buf, r_size);
+ }
+
+ if (i_blk_seq_num++ != blk_seq_num) {
+ printf("%s: Wrong sequence number! [%d] [%d]\n",
+ __func__, i_blk_seq_num, blk_seq_num);
+ return -1;
+ }
+
+ if (r_size >= size) {
+ memcpy(buf, i_buf, size);
+ i_buf += size;
+ r_size -= size;
+ return size;
+ } else {
+ memcpy(buf, i_buf, r_size);
+ i_buf += r_size;
+ debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc);
+ puts("UPLOAD ... done\nCtrl+C to exit ...\n");
+
+ i_buf = NULL;
+ i_blk_seq_num = 0;
+ crc = 0;
+ return r_size;
+ }
+ return ret;
+}
+
+static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
+ char *interface, int num)
+{
+ char *st;
+
+ debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
+ st = strsep(&s, " ");
+ strcpy(dfu->name, st);
+
+ dfu->dev_num = num;
+ dfu->alt = alt;
+
+ /* Specific for mmc device */
+ if (strcmp(interface, "mmc") == 0) {
+ if (dfu_fill_entity_mmc(dfu, s))
+ return -1;
+ } else {
+ printf("%s: Device %s not (yet) supported!\n",
+ __func__, interface);
+ return -1;
+ }
+
+ return 0;
+}
+
+void dfu_free_entities(void)
+{
+ struct dfu_entity *dfu, *p, *t = NULL;
+
+ list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
+ list_del(&dfu->list);
+ t = dfu;
+ }
+ if (t)
+ free(t);
+ INIT_LIST_HEAD(&dfu_list);
+}
+
+int dfu_config_entities(char *env, char *interface, int num)
+{
+ struct dfu_entity *dfu;
+ int i, ret;
+ char *s;
+
+ dfu_alt_num = dfu_find_alt_num(env);
+ debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
+
+ dfu = calloc(sizeof(*dfu), dfu_alt_num);
+ if (!dfu)
+ return -1;
+ for (i = 0; i < dfu_alt_num; i++) {
+
+ s = strsep(&env, ";");
+ ret = dfu_fill_entity(&dfu[i], s, i, interface, num);
+ if (ret)
+ return -1;
+
+ list_add_tail(&dfu[i].list, &dfu_list);
+ }
+
+ return 0;
+}
+
+const char *dfu_get_dev_type(enum dfu_device_type t)
+{
+ const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" };
+ return dev_t[t];
+}
+
+const char *dfu_get_layout(enum dfu_layout l)
+{
+ const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
+ "EXT3", "EXT4" };
+ return dfu_layout[l];
+}
+
+void dfu_show_entities(void)
+{
+ struct dfu_entity *dfu;
+
+ puts("DFU alt settings list:\n");
+
+ list_for_each_entry(dfu, &dfu_list, list) {
+ printf("dev: %s alt: %d name: %s layout: %s\n",
+ dfu_get_dev_type(dfu->dev_type), dfu->alt,
+ dfu->name, dfu_get_layout(dfu->layout));
+ }
+}
+
+int dfu_get_alt_number(void)
+{
+ return dfu_alt_num;
+}
+
+struct dfu_entity *dfu_get_entity(int alt)
+{
+ struct dfu_entity *dfu;
+
+ list_for_each_entry(dfu, &dfu_list, list) {
+ if (dfu->alt == alt)
+ return dfu;
+ }
+
+ return NULL;
+}
diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c
new file mode 100644
index 0000000000..5d504dffd1
--- /dev/null
+++ b/drivers/dfu/dfu_mmc.c
@@ -0,0 +1,182 @@
+/*
+ * dfu.c -- DFU back-end routines
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * 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 <malloc.h>
+#include <dfu.h>
+
+enum dfu_mmc_op {
+ DFU_OP_READ = 1,
+ DFU_OP_WRITE,
+};
+
+static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
+ void *buf, long *len)
+{
+ char cmd_buf[DFU_CMD_BUF_SIZE];
+
+ sprintf(cmd_buf, "mmc %s 0x%x %x %x",
+ op == DFU_OP_READ ? "read" : "write",
+ (unsigned int) buf,
+ dfu->data.mmc.lba_start,
+ dfu->data.mmc.lba_size);
+
+ if (op == DFU_OP_READ)
+ *len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size;
+
+ debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
+ return run_command(cmd_buf, 0);
+}
+
+static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len)
+{
+ return mmc_block_op(DFU_OP_WRITE, dfu, buf, len);
+}
+
+static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len)
+{
+ return mmc_block_op(DFU_OP_READ, dfu, buf, len);
+}
+
+static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
+ void *buf, long *len)
+{
+ char cmd_buf[DFU_CMD_BUF_SIZE];
+ char *str_env;
+ int ret;
+
+ switch (dfu->layout) {
+ case DFU_FS_FAT:
+ sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx",
+ op == DFU_OP_READ ? "load" : "write",
+ dfu->data.mmc.dev, dfu->data.mmc.part,
+ (unsigned int) buf, dfu->name, *len);
+ break;
+ case DFU_FS_EXT4:
+ sprintf(cmd_buf, "ext4%s mmc %d:%d /%s 0x%x %ld",
+ op == DFU_OP_READ ? "load" : "write",
+ dfu->data.mmc.dev, dfu->data.mmc.part,
+ dfu->name, (unsigned int) buf, *len);
+ break;
+ default:
+ printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+ dfu_get_layout(dfu->layout));
+ }
+
+ debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
+
+ ret = run_command(cmd_buf, 0);
+ if (ret) {
+ puts("dfu: Read error!\n");
+ return ret;
+ }
+
+ if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) {
+ str_env = getenv("filesize");
+ if (str_env == NULL) {
+ puts("dfu: Wrong file size!\n");
+ return -1;
+ }
+ *len = simple_strtoul(str_env, NULL, 16);
+ }
+
+ return ret;
+}
+
+static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len)
+{
+ return mmc_file_op(DFU_OP_WRITE, dfu, buf, len);
+}
+
+static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len)
+{
+ return mmc_file_op(DFU_OP_READ, dfu, buf, len);
+}
+
+int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
+{
+ int ret = -1;
+
+ switch (dfu->layout) {
+ case DFU_RAW_ADDR:
+ ret = mmc_block_write(dfu, buf, len);
+ break;
+ case DFU_FS_FAT:
+ case DFU_FS_EXT4:
+ ret = mmc_file_write(dfu, buf, len);
+ break;
+ default:
+ printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+ dfu_get_layout(dfu->layout));
+ }
+
+ return ret;
+}
+
+int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
+{
+ int ret = -1;
+
+ switch (dfu->layout) {
+ case DFU_RAW_ADDR:
+ ret = mmc_block_read(dfu, buf, len);
+ break;
+ case DFU_FS_FAT:
+ case DFU_FS_EXT4:
+ ret = mmc_file_read(dfu, buf, len);
+ break;
+ default:
+ printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+ dfu_get_layout(dfu->layout));
+ }
+
+ return ret;
+}
+
+int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
+{
+ char *st;
+
+ dfu->dev_type = DFU_DEV_MMC;
+ st = strsep(&s, " ");
+ if (!strcmp(st, "mmc")) {
+ dfu->layout = DFU_RAW_ADDR;
+ dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
+ dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
+ dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
+ } else if (!strcmp(st, "fat")) {
+ dfu->layout = DFU_FS_FAT;
+ } else if (!strcmp(st, "ext4")) {
+ dfu->layout = DFU_FS_EXT4;
+ } else {
+ printf("%s: Memory layout (%s) not supported!\n", __func__, st);
+ }
+
+ if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) {
+ dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
+ dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
+ }
+
+ dfu->read_medium = dfu_read_medium_mmc;
+ dfu->write_medium = dfu_write_medium_mmc;
+
+ return 0;
+}
diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c
index cb2193ec55..37a941cc5b 100644
--- a/drivers/dma/apbh_dma.c
+++ b/drivers/dma/apbh_dma.c
@@ -76,8 +76,8 @@ static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)
*/
static int mxs_dma_read_semaphore(int channel)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
uint32_t tmp;
int ret;
@@ -119,8 +119,8 @@ inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {}
*/
static int mxs_dma_enable(int channel)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
unsigned int sem;
struct mxs_dma_chan *pchan;
struct mxs_dma_desc *pdesc;
@@ -191,8 +191,8 @@ static int mxs_dma_enable(int channel)
static int mxs_dma_disable(int channel)
{
struct mxs_dma_chan *pchan;
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
@@ -220,8 +220,8 @@ static int mxs_dma_disable(int channel)
*/
static int mxs_dma_reset(int channel)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
@@ -241,8 +241,8 @@ static int mxs_dma_reset(int channel)
*/
static int mxs_dma_enable_irq(int channel, int enable)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
@@ -267,8 +267,8 @@ static int mxs_dma_enable_irq(int channel, int enable)
*/
static int mxs_dma_ack_irq(int channel)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
@@ -504,15 +504,15 @@ static int mxs_dma_finish(int channel, struct list_head *head)
*/
static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(chan);
if (ret)
return ret;
- if (mx28_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg,
+ if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg,
1 << chan, timeout)) {
ret = -ETIMEDOUT;
mxs_dma_reset(chan);
@@ -526,7 +526,7 @@ static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
*/
int mxs_dma_go(int chan)
{
- uint32_t timeout = 10000;
+ uint32_t timeout = 10000000;
int ret;
LIST_HEAD(tmp_desc_list);
@@ -554,10 +554,10 @@ int mxs_dma_go(int chan)
*/
void mxs_dma_init(void)
{
- struct mx28_apbh_regs *apbh_regs =
- (struct mx28_apbh_regs *)MXS_APBH_BASE;
+ struct mxs_apbh_regs *apbh_regs =
+ (struct mxs_apbh_regs *)MXS_APBH_BASE;
- mx28_reset_block(&apbh_regs->hw_apbh_ctrl0_reg);
+ mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg);
#ifdef CONFIG_APBH_DMA_BURST8
writel(APBH_CTRL0_AHB_BURST8_EN,
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 106549dc2c..17f4b739a9 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -41,6 +41,9 @@ COBJS-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o
COBJS-$(CONFIG_ALTERA_PIO) += altera_pio.o
COBJS-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o
COBJS-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o
+COBJS-$(CONFIG_OMAP_GPIO) += omap_gpio.o
+COBJS-$(CONFIG_DB8500_GPIO) += db8500_gpio.o
+COBJS-$(CONFIG_BCM2835_GPIO) += bcm2835_gpio.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/gpio/bcm2835_gpio.c b/drivers/gpio/bcm2835_gpio.c
new file mode 100644
index 0000000000..1d50dbd5e0
--- /dev/null
+++ b/drivers/gpio/bcm2835_gpio.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 Vikram Narayananan
+ * <vikram186@gmail.com>
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+inline int gpio_is_valid(unsigned gpio)
+{
+ return (gpio < BCM2835_GPIO_COUNT);
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ return !gpio_is_valid(gpio);
+}
+
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ struct bcm2835_gpio_regs *reg =
+ (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE;
+ unsigned val;
+
+ val = readl(&reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+ val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ val |= (BCM2835_GPIO_INPUT << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ writel(val, &reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+
+ return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ struct bcm2835_gpio_regs *reg =
+ (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE;
+ unsigned val;
+
+ gpio_set_value(gpio, value);
+
+ val = readl(&reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+ val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ val |= (BCM2835_GPIO_OUTPUT << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ writel(val, &reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+
+ return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+ struct bcm2835_gpio_regs *reg =
+ (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE;
+ unsigned val;
+
+ val = readl(&reg->gplev[BCM2835_GPIO_COMMON_BANK(gpio)]);
+
+ return (val >> BCM2835_GPIO_COMMON_SHIFT(gpio)) & 0x1;
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+ struct bcm2835_gpio_regs *reg =
+ (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE;
+ u32 *output_reg = value ? reg->gpset : reg->gpclr;
+
+ writel(1 << BCM2835_GPIO_COMMON_SHIFT(gpio),
+ &output_reg[BCM2835_GPIO_COMMON_BANK(gpio)]);
+
+ return 0;
+}
diff --git a/drivers/gpio/db8500_gpio.c b/drivers/gpio/db8500_gpio.c
new file mode 100644
index 0000000000..d5cb383e85
--- /dev/null
+++ b/drivers/gpio/db8500_gpio.c
@@ -0,0 +1,221 @@
+/*
+ * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code.
+ * The purpose is that GPIO config found in kernel should work by simply
+ * copy-paste it to U-boot.
+ *
+ * Original Linux authors:
+ * Copyright (C) 2008,2009 STMicroelectronics
+ * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
+ * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com>
+ *
+ * Ported to U-boot by:
+ * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+
+#include <asm/arch/db8500_gpio.h>
+#include <asm/arch/db8500_pincfg.h>
+#include <linux/compiler.h>
+
+#define IO_ADDR(x) (void *) (x)
+
+/*
+ * The GPIO module in the db8500 Systems-on-Chip is an
+ * AMBA device, managing 32 pins and alternate functions. The logic block
+ * is currently only used in the db8500.
+ */
+
+#define GPIO_TOTAL_PINS 268
+#define GPIO_PINS_PER_BLOCK 32
+#define GPIO_BLOCKS_COUNT (GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1)
+#define GPIO_BLOCK(pin) (((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1)
+#define GPIO_PIN_WITHIN_BLOCK(pin) ((pin)%(GPIO_PINS_PER_BLOCK))
+
+/* Register in the logic block */
+#define DB8500_GPIO_DAT 0x00
+#define DB8500_GPIO_DATS 0x04
+#define DB8500_GPIO_DATC 0x08
+#define DB8500_GPIO_PDIS 0x0c
+#define DB8500_GPIO_DIR 0x10
+#define DB8500_GPIO_DIRS 0x14
+#define DB8500_GPIO_DIRC 0x18
+#define DB8500_GPIO_SLPC 0x1c
+#define DB8500_GPIO_AFSLA 0x20
+#define DB8500_GPIO_AFSLB 0x24
+
+#define DB8500_GPIO_RIMSC 0x40
+#define DB8500_GPIO_FIMSC 0x44
+#define DB8500_GPIO_IS 0x48
+#define DB8500_GPIO_IC 0x4c
+#define DB8500_GPIO_RWIMSC 0x50
+#define DB8500_GPIO_FWIMSC 0x54
+#define DB8500_GPIO_WKS 0x58
+
+static void __iomem *get_gpio_addr(unsigned gpio)
+{
+ /* Our list of GPIO chips */
+ static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = {
+ IO_ADDR(CFG_GPIO_0_BASE),
+ IO_ADDR(CFG_GPIO_1_BASE),
+ IO_ADDR(CFG_GPIO_2_BASE),
+ IO_ADDR(CFG_GPIO_3_BASE),
+ IO_ADDR(CFG_GPIO_4_BASE),
+ IO_ADDR(CFG_GPIO_5_BASE),
+ IO_ADDR(CFG_GPIO_6_BASE),
+ IO_ADDR(CFG_GPIO_7_BASE),
+ IO_ADDR(CFG_GPIO_8_BASE)
+ };
+
+ return gpio_addrs[GPIO_BLOCK(gpio)];
+}
+
+static unsigned get_gpio_offset(unsigned gpio)
+{
+ return GPIO_PIN_WITHIN_BLOCK(gpio);
+}
+
+/* Can only be called from config_pin. Don't configure alt-mode directly */
+static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode)
+{
+ void __iomem *addr = get_gpio_addr(gpio);
+ unsigned offset = get_gpio_offset(gpio);
+ u32 bit = 1 << offset;
+ u32 afunc, bfunc;
+
+ afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit;
+ bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit;
+ if (mode & DB8500_GPIO_ALT_A)
+ afunc |= bit;
+ if (mode & DB8500_GPIO_ALT_B)
+ bfunc |= bit;
+ writel(afunc, addr + DB8500_GPIO_AFSLA);
+ writel(bfunc, addr + DB8500_GPIO_AFSLB);
+}
+
+/**
+ * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio
+ * @gpio: pin number
+ * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP,
+ * and DB8500_GPIO_PULL_NONE
+ *
+ * Enables/disables pull up/down on a specified pin. This only takes effect if
+ * the pin is configured as an input (either explicitly or by the alternate
+ * function).
+ *
+ * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is
+ * configured as an input. Otherwise, due to the way the controller registers
+ * work, this function will change the value output on the pin.
+ */
+void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull)
+{
+ void __iomem *addr = get_gpio_addr(gpio);
+ unsigned offset = get_gpio_offset(gpio);
+ u32 bit = 1 << offset;
+ u32 pdis;
+
+ pdis = readl(addr + DB8500_GPIO_PDIS);
+ if (pull == DB8500_GPIO_PULL_NONE)
+ pdis |= bit;
+ else
+ pdis &= ~bit;
+ writel(pdis, addr + DB8500_GPIO_PDIS);
+
+ if (pull == DB8500_GPIO_PULL_UP)
+ writel(bit, addr + DB8500_GPIO_DATS);
+ else if (pull == DB8500_GPIO_PULL_DOWN)
+ writel(bit, addr + DB8500_GPIO_DATC);
+}
+
+void db8500_gpio_make_input(unsigned gpio)
+{
+ void __iomem *addr = get_gpio_addr(gpio);
+ unsigned offset = get_gpio_offset(gpio);
+
+ writel(1 << offset, addr + DB8500_GPIO_DIRC);
+}
+
+int db8500_gpio_get_input(unsigned gpio)
+{
+ void __iomem *addr = get_gpio_addr(gpio);
+ unsigned offset = get_gpio_offset(gpio);
+ u32 bit = 1 << offset;
+
+ printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n",
+ gpio, addr, offset, bit);
+
+ return (readl(addr + DB8500_GPIO_DAT) & bit) != 0;
+}
+
+void db8500_gpio_make_output(unsigned gpio, int val)
+{
+ void __iomem *addr = get_gpio_addr(gpio);
+ unsigned offset = get_gpio_offset(gpio);
+
+ writel(1 << offset, addr + DB8500_GPIO_DIRS);
+ db8500_gpio_set_output(gpio, val);
+}
+
+void db8500_gpio_set_output(unsigned gpio, int val)
+{
+ void __iomem *addr = get_gpio_addr(gpio);
+ unsigned offset = get_gpio_offset(gpio);
+
+ if (val)
+ writel(1 << offset, addr + DB8500_GPIO_DATS);
+ else
+ writel(1 << offset, addr + DB8500_GPIO_DATC);
+}
+
+/**
+ * config_pin - configure a pin's mux attributes
+ * @cfg: pin confguration
+ *
+ * Configures a pin's mode (alternate function or GPIO), its pull up status,
+ * and its sleep mode based on the specified configuration. The @cfg is
+ * usually one of the SoC specific macros defined in mach/<soc>-pins.h. These
+ * are constructed using, and can be further enhanced with, the macros in
+ * plat/pincfg.h.
+ *
+ * If a pin's mode is set to GPIO, it is configured as an input to avoid
+ * side-effects. The gpio can be manipulated later using standard GPIO API
+ * calls.
+ */
+static void config_pin(unsigned long cfg)
+{
+ int pin = PIN_NUM(cfg);
+ int pull = PIN_PULL(cfg);
+ int af = PIN_ALT(cfg);
+ int output = PIN_DIR(cfg);
+ int val = PIN_VAL(cfg);
+
+ if (output)
+ db8500_gpio_make_output(pin, val);
+ else {
+ db8500_gpio_make_input(pin);
+ db8500_gpio_set_pull(pin, pull);
+ }
+
+ gpio_set_mode(pin, af);
+}
+
+/**
+ * db8500_config_pins - configure several pins at once
+ * @cfgs: array of pin configurations
+ * @num: number of elments in the array
+ *
+ * Configures several pins using config_pin(). Refer to that function for
+ * further information.
+ */
+void db8500_gpio_config_pins(unsigned long *cfgs, size_t num)
+{
+ size_t i;
+
+ for (i = 0; i < num; i++)
+ config_pin(cfgs[i]);
+}
diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c
index 661553552e..2c79bff62b 100644
--- a/drivers/gpio/mxc_gpio.c
+++ b/drivers/gpio/mxc_gpio.c
@@ -41,13 +41,15 @@ static unsigned long gpio_ports[] = {
[0] = GPIO1_BASE_ADDR,
[1] = GPIO2_BASE_ADDR,
[2] = GPIO3_BASE_ADDR,
-#if defined(CONFIG_MX25) || defined(CONFIG_MX51) || defined(CONFIG_MX53) || \
- defined(CONFIG_MX6Q)
+#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
+ defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
[3] = GPIO4_BASE_ADDR,
#endif
-#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
+#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
[4] = GPIO5_BASE_ADDR,
[5] = GPIO6_BASE_ADDR,
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
[6] = GPIO7_BASE_ADDR,
#endif
};
@@ -116,7 +118,7 @@ int gpio_get_value(unsigned gpio)
regs = (struct gpio_regs *)gpio_ports[port];
- val = (readl(&regs->gpio_dr) >> gpio) & 0x01;
+ val = (readl(&regs->gpio_psr) >> gpio) & 0x01;
return val;
}
diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c
index 38dbc81b45..29f19badda 100644
--- a/drivers/gpio/mxs_gpio.c
+++ b/drivers/gpio/mxs_gpio.c
@@ -73,8 +73,8 @@ int gpio_get_value(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DIN(bank);
- struct mx28_register_32 *reg =
- (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
return (readl(&reg->reg) >> PAD_PIN(gpio)) & 1;
}
@@ -83,8 +83,8 @@ void gpio_set_value(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOUT(bank);
- struct mx28_register_32 *reg =
- (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
if (value)
writel(1 << PAD_PIN(gpio), &reg->reg_set);
@@ -96,8 +96,8 @@ int gpio_direction_input(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
- struct mx28_register_32 *reg =
- (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_clr);
@@ -108,8 +108,8 @@ int gpio_direction_output(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
- struct mx28_register_32 *reg =
- (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_set);
diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c
new file mode 100644
index 0000000000..fc89f2a42b
--- /dev/null
+++ b/drivers/gpio/omap_gpio.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2009 Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * 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 work is derived from the linux 2.6.27 kernel source
+ * To fetch, use the kernel repository
+ * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+ * Use the v2.6.27 tag.
+ *
+ * Below is the original's header including its copyright
+ *
+ * linux/arch/arm/plat-omap/gpio.c
+ *
+ * Support functions for OMAP GPIO
+ *
+ * Copyright (C) 2003-2005 Nokia Corporation
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <common.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+
+#define OMAP_GPIO_DIR_OUT 0
+#define OMAP_GPIO_DIR_IN 1
+
+static inline const struct gpio_bank *get_gpio_bank(int gpio)
+{
+ return &omap_gpio_bank[gpio >> 5];
+}
+
+static inline int get_gpio_index(int gpio)
+{
+ return gpio & 0x1f;
+}
+
+static inline int gpio_valid(int gpio)
+{
+ if (gpio < 0)
+ return -1;
+ if (gpio < 192)
+ return 0;
+ return -1;
+}
+
+static int check_gpio(int gpio)
+{
+ if (gpio_valid(gpio) < 0) {
+ printf("ERROR : check_gpio: invalid GPIO %d\n", gpio);
+ return -1;
+ }
+ return 0;
+}
+
+static void _set_gpio_direction(const struct gpio_bank *bank, int gpio,
+ int is_input)
+{
+ void *reg = bank->base;
+ u32 l;
+
+ switch (bank->method) {
+ case METHOD_GPIO_24XX:
+ reg += OMAP_GPIO_OE;
+ break;
+ default:
+ return;
+ }
+ l = __raw_readl(reg);
+ if (is_input)
+ l |= 1 << gpio;
+ else
+ l &= ~(1 << gpio);
+ __raw_writel(l, reg);
+}
+
+/**
+ * Get the direction of the GPIO by reading the GPIO_OE register
+ * corresponding to the specified bank.
+ */
+static int _get_gpio_direction(const struct gpio_bank *bank, int gpio)
+{
+ void *reg = bank->base;
+ u32 v;
+
+ switch (bank->method) {
+ case METHOD_GPIO_24XX:
+ reg += OMAP_GPIO_OE;
+ break;
+ default:
+ return -1;
+ }
+
+ v = __raw_readl(reg);
+
+ if (v & (1 << gpio))
+ return OMAP_GPIO_DIR_IN;
+ else
+ return OMAP_GPIO_DIR_OUT;
+}
+
+static void _set_gpio_dataout(const struct gpio_bank *bank, int gpio,
+ int enable)
+{
+ void *reg = bank->base;
+ u32 l = 0;
+
+ switch (bank->method) {
+ case METHOD_GPIO_24XX:
+ if (enable)
+ reg += OMAP_GPIO_SETDATAOUT;
+ else
+ reg += OMAP_GPIO_CLEARDATAOUT;
+ l = 1 << gpio;
+ break;
+ default:
+ printf("omap3-gpio unknown bank method %s %d\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ __raw_writel(l, reg);
+}
+
+/**
+ * Set value of the specified gpio
+ */
+int gpio_set_value(unsigned gpio, int value)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+ bank = get_gpio_bank(gpio);
+ _set_gpio_dataout(bank, get_gpio_index(gpio), value);
+
+ return 0;
+}
+
+/**
+ * Get value of the specified gpio
+ */
+int gpio_get_value(unsigned gpio)
+{
+ const struct gpio_bank *bank;
+ void *reg;
+ int input;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+ bank = get_gpio_bank(gpio);
+ reg = bank->base;
+ switch (bank->method) {
+ case METHOD_GPIO_24XX:
+ input = _get_gpio_direction(bank, get_gpio_index(gpio));
+ switch (input) {
+ case OMAP_GPIO_DIR_IN:
+ reg += OMAP_GPIO_DATAIN;
+ break;
+ case OMAP_GPIO_DIR_OUT:
+ reg += OMAP_GPIO_DATAOUT;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+ return (__raw_readl(reg)
+ & (1 << get_gpio_index(gpio))) != 0;
+}
+
+/**
+ * Set gpio direction as input
+ */
+int gpio_direction_input(unsigned gpio)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+
+ bank = get_gpio_bank(gpio);
+ _set_gpio_direction(bank, get_gpio_index(gpio), 1);
+
+ return 0;
+}
+
+/**
+ * Set gpio direction as output
+ */
+int gpio_direction_output(unsigned gpio, int value)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+
+ bank = get_gpio_bank(gpio);
+ _set_gpio_dataout(bank, get_gpio_index(gpio), value);
+ _set_gpio_direction(bank, get_gpio_index(gpio), 0);
+
+ return 0;
+}
+
+/**
+ * Request a gpio before using it.
+ *
+ * NOTE: Argument 'label' is unused.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+ if (check_gpio(gpio) < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Reset and free the gpio after using it.
+ */
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c
index 60ec6e3d78..747f4cf921 100644
--- a/drivers/gpio/tegra_gpio.c
+++ b/drivers/gpio/tegra_gpio.c
@@ -1,5 +1,5 @@
/*
- * NVIDIA Tegra2 GPIO handling.
+ * NVIDIA Tegra20 GPIO handling.
* (C) Copyright 2010-2012
* NVIDIA Corporation <www.nvidia.com>
*
@@ -30,14 +30,14 @@
#include <common.h>
#include <asm/io.h>
#include <asm/bitops.h>
-#include <asm/arch/tegra2.h>
+#include <asm/arch/tegra20.h>
#include <asm/gpio.h>
enum {
- TEGRA2_CMD_INFO,
- TEGRA2_CMD_PORT,
- TEGRA2_CMD_OUTPUT,
- TEGRA2_CMD_INPUT,
+ TEGRA_CMD_INFO,
+ TEGRA_CMD_PORT,
+ TEGRA_CMD_OUTPUT,
+ TEGRA_CMD_INPUT,
};
static struct gpio_names {
diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c
index 48aaaa6268..2a193c220d 100644
--- a/drivers/i2c/mxs_i2c.c
+++ b/drivers/i2c/mxs_i2c.c
@@ -38,10 +38,10 @@
void mxs_i2c_reset(void)
{
- struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
+ struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;
int ret;
- ret = mx28_reset_block(&i2c_regs->hw_i2c_ctrl0_reg);
+ ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg);
if (ret) {
debug("MXS I2C: Block reset timeout\n");
return;
@@ -57,7 +57,7 @@ void mxs_i2c_reset(void)
void mxs_i2c_setup_read(uint8_t chip, int len)
{
- struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
+ struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;
writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START |
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
@@ -76,7 +76,7 @@ void mxs_i2c_setup_read(uint8_t chip, int len)
void mxs_i2c_write(uchar chip, uint addr, int alen,
uchar *buf, int blen, int stop)
{
- struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
+ struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;
uint32_t data;
int i, remain, off;
@@ -119,7 +119,7 @@ void mxs_i2c_write(uchar chip, uint addr, int alen,
int mxs_i2c_wait_for_ack(void)
{
- struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
+ struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;
uint32_t tmp;
int timeout = MXS_I2C_MAX_TIMEOUT;
@@ -157,7 +157,7 @@ err:
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
- struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
+ struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;
uint32_t tmp = 0;
int ret;
int i;
@@ -212,7 +212,7 @@ int i2c_probe(uchar chip)
void i2c_init(int speed, int slaveadd)
{
- struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
+ struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;
mxs_i2c_reset();
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index 81193b0e6e..094305fdf9 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -150,16 +150,19 @@ void i2c_init(int speed, int slaveadd)
bus_initialized[current_bus] = 1;
}
-static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value)
+static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value)
{
int i2c_error = 0;
u16 status;
+ int i = 2 - alen;
+ u8 tmpbuf[2] = {(regoffset) >> 8, regoffset & 0xff};
+ u16 w;
/* wait until bus not busy */
wait_for_bb();
/* one byte only */
- writew(1, &i2c_base->cnt);
+ writew(alen, &i2c_base->cnt);
/* set slave address */
writew(devaddr, &i2c_base->sa);
/* no stop bit needed here */
@@ -174,8 +177,12 @@ static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value)
goto read_exit;
}
if (status & I2C_STAT_XRDY) {
- /* Important: have to use byte access */
- writeb(regoffset, &i2c_base->data);
+ w = tmpbuf[i++];
+#if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
+ defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX))
+ w |= tmpbuf[i++] << 8;
+#endif
+ writew(w, &i2c_base->data);
writew(I2C_STAT_XRDY, &i2c_base->stat);
}
if (status & I2C_STAT_ARDY) {
@@ -303,18 +310,18 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
int i;
- if (alen > 1) {
+ if (alen > 2) {
printf("I2C read: addr len %d not supported\n", alen);
return 1;
}
- if (addr + len > 256) {
+ if (addr + len > (1 << 16)) {
puts("I2C read: address out of range\n");
return 1;
}
for (i = 0; i < len; i++) {
- if (i2c_read_byte(chip, addr + i, &buffer[i])) {
+ if (i2c_read_byte(chip, addr + i, alen, &buffer[i])) {
puts("I2C read: I/O error\n");
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
return 1;
@@ -329,13 +336,15 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
int i;
u16 status;
int i2c_error = 0;
+ u16 w;
+ u8 tmpbuf[2] = {addr >> 8, addr & 0xff};
- if (alen > 1) {
+ if (alen > 2) {
printf("I2C write: addr len %d not supported\n", alen);
return 1;
}
- if (addr + len > 256) {
+ if (addr + len > (1 << 16)) {
printf("I2C write: address 0x%x + 0x%x out of range\n",
addr, len);
return 1;
@@ -353,28 +362,8 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
I2C_CON_STP, &i2c_base->con);
- /* Send address byte */
- status = wait_for_pin();
-
- if (status == 0 || status & I2C_STAT_NACK) {
- i2c_error = 1;
- printf("error waiting for i2c address ACK (status=0x%x)\n",
- status);
- goto write_exit;
- }
-
- if (status & I2C_STAT_XRDY) {
- writeb(addr & 0xFF, &i2c_base->data);
- writew(I2C_STAT_XRDY, &i2c_base->stat);
- } else {
- i2c_error = 1;
- printf("i2c bus not ready for transmit (status=0x%x)\n",
- status);
- goto write_exit;
- }
-
- /* address phase is over, now write data */
- for (i = 0; i < len; i++) {
+ /* Send address and data */
+ for (i = -alen; i < len; i++) {
status = wait_for_pin();
if (status == 0 || status & I2C_STAT_NACK) {
@@ -385,7 +374,12 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
}
if (status & I2C_STAT_XRDY) {
- writeb(buffer[i], &i2c_base->data);
+ w = (i < 0) ? tmpbuf[2+i] : buffer[i];
+#if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
+ defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX))
+ w |= ((++i < 0) ? tmpbuf[2+i] : buffer[i]) << 8;
+#endif
+ writew(w, &i2c_base->data);
writew(I2C_STAT_XRDY, &i2c_base->stat);
} else {
i2c_error = 1;
@@ -448,6 +442,14 @@ int i2c_set_bus_num(unsigned int bus)
return -1;
}
+#if I2C_BUS_MAX == 4
+ if (bus == 3)
+ i2c_base = (struct i2c *)I2C_BASE4;
+ else
+ if (bus == 2)
+ i2c_base = (struct i2c *)I2C_BASE3;
+ else
+#endif
#if I2C_BUS_MAX == 3
if (bus == 2)
i2c_base = (struct i2c *)I2C_BASE3;
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c
index 5b6ea0e759..e3be14e3cf 100644
--- a/drivers/i2c/tegra_i2c.c
+++ b/drivers/i2c/tegra_i2c.c
@@ -262,7 +262,7 @@ exit:
return error;
}
-static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len)
+static int tegra_i2c_write_data(u32 addr, u8 *data, u32 len)
{
int error;
struct i2c_trans_info trans_info;
@@ -275,12 +275,12 @@ static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len)
error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);
if (error)
- debug("tegra2_i2c_write_data: Error (%d) !!!\n", error);
+ debug("tegra_i2c_write_data: Error (%d) !!!\n", error);
return error;
}
-static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len)
+static int tegra_i2c_read_data(u32 addr, u8 *data, u32 len)
{
int error;
struct i2c_trans_info trans_info;
@@ -293,7 +293,7 @@ static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len)
error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);
if (error)
- debug("tegra2_i2c_read_data: Error (%d) !!!\n", error);
+ debug("tegra_i2c_read_data: Error (%d) !!!\n", error);
return error;
}
@@ -438,7 +438,7 @@ int i2c_write_data(uchar chip, uchar *buffer, int len)
debug("\n");
/* Shift 7-bit address over for lower-level i2c functions */
- rc = tegra2_i2c_write_data(chip << 1, buffer, len);
+ rc = tegra_i2c_write_data(chip << 1, buffer, len);
if (rc)
debug("i2c_write_data(): rc=%d\n", rc);
@@ -452,7 +452,7 @@ int i2c_read_data(uchar chip, uchar *buffer, int len)
debug("inside i2c_read_data():\n");
/* Shift 7-bit address over for lower-level i2c functions */
- rc = tegra2_i2c_read_data(chip << 1, buffer, len);
+ rc = tegra_i2c_read_data(chip << 1, buffer, len);
if (rc) {
debug("i2c_read_data(): rc=%d\n", rc);
return rc;
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5c831b2611..0805e86678 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libinput.o
COBJS-$(CONFIG_I8042_KBD) += i8042.o
-COBJS-$(CONFIG_TEGRA2_KEYBOARD) += tegra-kbc.o
+COBJS-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o
ifdef CONFIG_PS2KBD
COBJS-y += keyboard.o pc_keyb.o
COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o
diff --git a/drivers/input/key_matrix.c b/drivers/input/key_matrix.c
index 84b898ff38..715e57a2af 100644
--- a/drivers/input/key_matrix.c
+++ b/drivers/input/key_matrix.c
@@ -23,6 +23,7 @@
* MA 02111-1307 USA
*/
+#include <common.h>
#include <fdtdec.h>
#include <key_matrix.h>
#include <malloc.h>
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index c567737015..2b96cdcd40 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -25,6 +25,10 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmmc.o
+ifdef CONFIG_SPL_MMC_LOAD
+COBJS-$(CONFIG_SPL_MMC_LOAD) += spl_mmc_load.o
+endif
+
COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c
index 09d443ee39..af1380a455 100644
--- a/drivers/mmc/arm_pl180_mmci.c
+++ b/drivers/mmc/arm_pl180_mmci.c
@@ -32,14 +32,10 @@
#include "arm_pl180_mmci.h"
#include <malloc.h>
-struct mmc_host {
- struct sdi_registers *base;
-};
-
static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
{
u32 hoststatus, statusmask;
- struct mmc_host *host = dev->priv;
+ struct pl180_mmc_host *host = dev->priv;
statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL;
if ((cmd->resp_type & MMC_RSP_PRESENT))
@@ -53,10 +49,10 @@ static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
writel(statusmask, &host->base->status_clear);
if (hoststatus & SDI_STA_CTIMEOUT) {
- printf("CMD%d time out\n", cmd->cmdidx);
- return -ETIMEDOUT;
+ debug("CMD%d time out\n", cmd->cmdidx);
+ return TIMEOUT;
} else if ((hoststatus & SDI_STA_CCRCFAIL) &&
- (cmd->flags & MMC_RSP_CRC)) {
+ (cmd->resp_type & MMC_RSP_CRC)) {
printf("CMD%d CRC error\n", cmd->cmdidx);
return -EILSEQ;
}
@@ -80,7 +76,7 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
{
int result;
u32 sdi_cmd = 0;
- struct mmc_host *host = dev->priv;
+ struct pl180_mmc_host *host = dev->priv;
sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN);
@@ -112,7 +108,7 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)
{
u32 *tempbuff = dest;
u64 xfercount = blkcount * blksize;
- struct mmc_host *host = dev->priv;
+ struct pl180_mmc_host *host = dev->priv;
u32 status, status_err;
debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
@@ -168,7 +164,7 @@ static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize)
u32 *tempbuff = src;
int i;
u64 xfercount = blkcount * blksize;
- struct mmc_host *host = dev->priv;
+ struct pl180_mmc_host *host = dev->priv;
u32 status, status_err;
debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
@@ -227,14 +223,19 @@ static int do_data_transfer(struct mmc *dev,
struct mmc_data *data)
{
int error = -ETIMEDOUT;
- struct mmc_host *host = dev->priv;
+ struct pl180_mmc_host *host = dev->priv;
u32 blksz = 0;
u32 data_ctrl = 0;
u32 data_len = (u32) (data->blocks * data->blocksize);
- blksz = (ffs(data->blocksize) - 1);
- data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK);
- data_ctrl |= SDI_DCTRL_DTEN;
+ if (!host->version2) {
+ blksz = (ffs(data->blocksize) - 1);
+ data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK);
+ } else {
+ blksz = data->blocksize;
+ data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT);
+ }
+ data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE;
writel(SDI_DTIMER_DEFAULT, &host->base->datatimer);
writel(data_len, &host->base->datalength);
@@ -257,7 +258,7 @@ static int do_data_transfer(struct mmc *dev,
writel(data_ctrl, &host->base->datactrl);
error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks,
- (u32)data->blocksize);
+ (u32)data->blocksize);
}
return error;
@@ -280,17 +281,16 @@ static int host_request(struct mmc *dev,
/* MMC uses open drain drivers in the enumeration phase */
static int mmc_host_reset(struct mmc *dev)
{
- struct mmc_host *host = dev->priv;
- u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON;
+ struct pl180_mmc_host *host = dev->priv;
- writel(sdi_u32, &host->base->power);
+ writel(host->pwr_init, &host->base->power);
return 0;
}
static void host_set_ios(struct mmc *dev)
{
- struct mmc_host *host = dev->priv;
+ struct pl180_mmc_host *host = dev->priv;
u32 sdi_clkcr;
sdi_clkcr = readl(&host->base->clock);
@@ -298,15 +298,26 @@ static void host_set_ios(struct mmc *dev)
/* Ramp up the clock rate */
if (dev->clock) {
u32 clkdiv = 0;
+ u32 tmp_clock;
- if (dev->clock >= dev->f_max)
+ if (dev->clock >= dev->f_max) {
+ clkdiv = 0;
dev->clock = dev->f_max;
+ } else {
+ clkdiv = (host->clock_in / dev->clock) - 2;
+ }
- clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1;
+ tmp_clock = host->clock_in / (clkdiv + 2);
+ while (tmp_clock > dev->clock) {
+ clkdiv++;
+ tmp_clock = host->clock_in / (clkdiv + 2);
+ }
if (clkdiv > SDI_CLKCR_CLKDIV_MASK)
clkdiv = SDI_CLKCR_CLKDIV_MASK;
+ tmp_clock = host->clock_in / (clkdiv + 2);
+ dev->clock = tmp_clock;
sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK);
sdi_clkcr |= clkdiv;
}
@@ -322,8 +333,11 @@ static void host_set_ios(struct mmc *dev)
case 4:
buswidth |= SDI_CLKCR_WIDBUS_4;
break;
+ case 8:
+ buswidth |= SDI_CLKCR_WIDBUS_8;
+ break;
default:
- printf("Invalid bus width\n");
+ printf("Invalid bus width: %d\n", dev->bus_width);
break;
}
sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK);
@@ -334,83 +348,40 @@ static void host_set_ios(struct mmc *dev)
udelay(CLK_CHANGE_DELAY);
}
-struct mmc *alloc_mmc_struct(void)
-{
- struct mmc_host *host = NULL;
- struct mmc *mmc_device = NULL;
-
- host = malloc(sizeof(struct mmc_host));
- if (!host)
- return NULL;
-
- mmc_device = malloc(sizeof(struct mmc));
- if (!mmc_device)
- goto err;
-
- mmc_device->priv = host;
- return mmc_device;
-
-err:
- free(host);
- return NULL;
-}
-
/*
* mmc_host_init - initialize the mmc controller.
* Set initial clock and power for mmc slot.
* Initialize mmc struct and register with mmc framework.
*/
-static int arm_pl180_mmci_host_init(struct mmc *dev)
+int arm_pl180_mmci_init(struct pl180_mmc_host *host)
{
- struct mmc_host *host = dev->priv;
+ struct mmc *dev;
u32 sdi_u32;
- host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE;
+ dev = malloc(sizeof(struct mmc));
+ if (!dev)
+ return -ENOMEM;
- /* Initially set power-on, full voltage & MMCI read */
- sdi_u32 = INIT_PWR;
- writel(sdi_u32, &host->base->power);
+ memset(dev, 0, sizeof(struct mmc));
+ dev->priv = host;
- /* setting clk freq 505KHz */
- sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN;
- writel(sdi_u32, &host->base->clock);
+ writel(host->pwr_init, &host->base->power);
+ writel(host->clkdiv_init, &host->base->clock);
udelay(CLK_CHANGE_DELAY);
/* Disable mmc interrupts */
sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;
writel(sdi_u32, &host->base->mask0);
-
- sprintf(dev->name, "MMC");
- dev->clock = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1));
+ strncpy(dev->name, host->name, sizeof(dev->name));
dev->send_cmd = host_request;
dev->set_ios = host_set_ios;
dev->init = mmc_host_reset;
dev->getcd = NULL;
- dev->host_caps = 0;
- dev->voltages = VOLTAGE_WINDOW_MMC;
- dev->f_min = dev->clock;
- dev->f_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ;
-
- return 0;
-}
-
-int arm_pl180_mmci_init(void)
-{
- int error;
- struct mmc *dev;
-
- dev = alloc_mmc_struct();
- if (!dev)
- return -1;
-
- error = arm_pl180_mmci_host_init(dev);
- if (error) {
- printf("mmci_host_init error - %d\n", error);
- return -1;
- }
-
- dev->b_max = 0;
-
+ dev->host_caps = host->caps;
+ dev->voltages = host->voltages;
+ dev->f_min = host->clock_min;
+ dev->f_max = host->clock_max;
+ dev->b_max = host->b_max;
mmc_register(dev);
debug("registered mmc interface number is:%d\n", dev->block_dev.dev);
diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h
index 42fbe3e386..06709ed7fb 100644
--- a/drivers/mmc/arm_pl180_mmci.h
+++ b/drivers/mmc/arm_pl180_mmci.h
@@ -26,8 +26,6 @@
#ifndef __ARM_PL180_MMCI_H__
#define __ARM_PL180_MMCI_H__
-int arm_pl180_mmci_init(void);
-
#define COMMAND_REG_DELAY 300
#define DATA_REG_DELAY 1000
#define CLK_CHANGE_DELAY 2000
@@ -59,8 +57,13 @@ int arm_pl180_mmci_init(void);
#define SDI_CLKCR_WIDBUS_MASK 0x00001800
#define SDI_CLKCR_WIDBUS_1 0x00000000
#define SDI_CLKCR_WIDBUS_4 0x00000800
+/* V2 only */
+#define SDI_CLKCR_WIDBUS_8 0x00001000
+#define SDI_CLKCR_NEDGE 0x00002000
+#define SDI_CLKCR_HWFC_EN 0x00004000
-#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */
+#define SDI_CLKCR_CLKDIV_INIT_V1 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */
+#define SDI_CLKCR_CLKDIV_INIT_V2 0x000000FD
/* SDI command register bits */
#define SDI_CMD_CMDINDEX_MASK 0x000000FF
@@ -144,6 +147,8 @@ int arm_pl180_mmci_init(void);
#define SDI_DCTRL_DBOOTMODEEN 0x00002000
#define SDI_DCTRL_BUSYMODE 0x00004000
#define SDI_DCTRL_DDR_MODE 0x00008000
+#define SDI_DCTRL_DBLOCKSIZE_V2_MASK 0x7fff0000
+#define SDI_DCTRL_DBLOCKSIZE_V2_SHIFT 16
#define SDI_FIFO_BURST_SIZE 8
@@ -180,4 +185,20 @@ struct sdi_registers {
u32 pcell_id3; /* 0xFFC*/
};
+struct pl180_mmc_host {
+ struct sdi_registers *base;
+ char name[32];
+ unsigned int b_max;
+ unsigned int voltages;
+ unsigned int caps;
+ unsigned int clock_in;
+ unsigned int clock_min;
+ unsigned int clock_max;
+ unsigned int clkdiv_init;
+ unsigned int pwr_init;
+ int version2;
+};
+
+int arm_pl180_mmci_init(struct pl180_mmc_host *);
+
#endif
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index b6c969d2c8..3f8d30db4c 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -479,9 +479,10 @@ static int esdhc_init(struct mmc *mmc)
while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
udelay(1000);
+#ifndef ARCH_MXC
/* Enable cache snooping */
- if (cfg && !cfg->no_snoop)
- esdhc_write32(&regs->scr, 0x00000040);
+ esdhc_write32(&regs->scr, 0x00000040);
+#endif
esdhc_write32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index c1c2862986..a60cfe1cb0 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -151,7 +151,6 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
printf("CMD_SEND:%d\n", cmd->cmdidx);
printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
- printf("\t\tFLAG\t\t\t %d\n", cmd->flags);
ret = mmc->send_cmd(mmc, cmd, data);
switch (cmd->resp_type) {
case MMC_RSP_NONE:
@@ -213,7 +212,6 @@ int mmc_send_status(struct mmc *mmc, int timeout)
cmd.resp_type = MMC_RSP_R1;
if (!mmc_host_is_spi(mmc))
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
do {
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -238,7 +236,7 @@ int mmc_send_status(struct mmc *mmc, int timeout)
status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
printf("CURR STATE:%d\n", status);
#endif
- if (!timeout) {
+ if (timeout <= 0) {
printf("Timeout waiting card ready\n");
return TIMEOUT;
}
@@ -253,7 +251,6 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = len;
- cmd.flags = 0;
return mmc_send_cmd(mmc, &cmd, NULL);
}
@@ -299,7 +296,6 @@ static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
cmd.cmdidx = start_cmd;
cmd.cmdarg = start;
cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -386,7 +382,6 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
cmd.cmdarg = start * mmc->write_bl_len;
cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
data.src = src;
data.blocks = blkcnt;
@@ -405,7 +400,6 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
printf("mmc fail to send stop cmd\n");
return 0;
@@ -459,7 +453,6 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
cmd.cmdarg = start * mmc->read_bl_len;
cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
data.dest = dst;
data.blocks = blkcnt;
@@ -473,7 +466,6 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
printf("mmc fail to send stop cmd\n");
return 0;
@@ -525,7 +517,6 @@ int mmc_go_idle(struct mmc* mmc)
cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_NONE;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -548,7 +539,6 @@ sd_send_op_cond(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -589,7 +579,6 @@ sd_send_op_cond(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -618,7 +607,6 @@ int mmc_send_op_cond(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SEND_OP_COND;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -638,8 +626,6 @@ int mmc_send_op_cond(struct mmc *mmc)
if (mmc->host_caps & MMC_MODE_HC)
cmd.cmdarg |= OCR_HCS;
- cmd.flags = 0;
-
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -655,7 +641,6 @@ int mmc_send_op_cond(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -673,7 +658,7 @@ int mmc_send_op_cond(struct mmc *mmc)
}
-int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
+int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
{
struct mmc_cmd cmd;
struct mmc_data data;
@@ -683,9 +668,8 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
- cmd.flags = 0;
- data.dest = ext_csd;
+ data.dest = (char *)ext_csd;
data.blocks = 1;
data.blocksize = 512;
data.flags = MMC_DATA_READ;
@@ -707,7 +691,6 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) |
(value << 8);
- cmd.flags = 0;
ret = mmc_send_cmd(mmc, &cmd, NULL);
@@ -721,7 +704,7 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
int mmc_change_freq(struct mmc *mmc)
{
- ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, 512);
char cardtype;
int err;
@@ -800,7 +783,6 @@ int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
cmd.cmdarg = (mode << 31) | 0xffffff;
cmd.cmdarg &= ~(0xf << (group * 4));
cmd.cmdarg |= value << (group * 4);
- cmd.flags = 0;
data.dest = (char *)resp;
data.blocksize = 64;
@@ -829,7 +811,6 @@ int sd_change_freq(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -839,7 +820,6 @@ int sd_change_freq(struct mmc *mmc)
cmd.cmdidx = SD_CMD_APP_SEND_SCR;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
- cmd.flags = 0;
timeout = 3;
@@ -983,8 +963,8 @@ int mmc_startup(struct mmc *mmc)
uint mult, freq;
u64 cmult, csize, capacity;
struct mmc_cmd cmd;
- ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512);
- ALLOC_CACHE_ALIGN_BUFFER(char, test_csd, 512);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, 512);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, 512);
int timeout = 1000;
#ifdef CONFIG_MMC_SPI_CRC_ON
@@ -992,7 +972,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 1;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -1005,7 +984,6 @@ int mmc_startup(struct mmc *mmc)
MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1023,7 +1001,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
cmd.cmdarg = mmc->rca << 16;
cmd.resp_type = MMC_RSP_R6;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1038,7 +1015,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SEND_CSD;
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1115,7 +1091,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SELECT_CARD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -1162,7 +1137,8 @@ int mmc_startup(struct mmc *mmc)
}
/* store the partition info of emmc */
- if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT)
+ if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
+ ext_csd[EXT_CSD_BOOT_MULT])
mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
}
@@ -1182,7 +1158,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -1191,7 +1166,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 2;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
@@ -1259,7 +1233,9 @@ int mmc_startup(struct mmc *mmc)
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28,
(mmc->cid[2] >> 24) & 0xf);
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
init_part(&mmc->block_dev);
+#endif
return 0;
}
@@ -1273,7 +1249,6 @@ int mmc_send_if_cond(struct mmc *mmc)
/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;
cmd.resp_type = MMC_RSP_R7;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1311,10 +1286,9 @@ int mmc_register(struct mmc *mmc)
block_dev_desc_t *mmc_get_dev(int dev)
{
struct mmc *mmc = find_mmc_device(dev);
- if (!mmc)
+ if (!mmc || mmc_init(mmc))
return NULL;
- mmc_init(mmc);
return &mmc->block_dev;
}
#endif
diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c
index 4187a94120..c80b41b192 100644
--- a/drivers/mmc/mxsmmc.c
+++ b/drivers/mmc/mxsmmc.c
@@ -43,16 +43,9 @@
#include <asm/arch/sys_proto.h>
#include <asm/arch/dma.h>
-/*
- * CONFIG_MXS_MMC_DMA: This feature is highly experimental and has no
- * performance benefit unless you operate the platform with
- * data cache enabled. This is disabled by default, enable
- * only if you know what you're doing.
- */
-
struct mxsmmc_priv {
int id;
- struct mx28_ssp_regs *regs;
+ struct mxs_ssp_regs *regs;
uint32_t clkseq_bypass;
uint32_t *clkctrl_ssp;
uint32_t buswidth;
@@ -61,6 +54,91 @@ struct mxsmmc_priv {
};
#define MXSMMC_MAX_TIMEOUT 10000
+#define MXSMMC_SMALL_TRANSFER 512
+
+static int mxsmmc_send_cmd_pio(struct mxsmmc_priv *priv, struct mmc_data *data)
+{
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
+ uint32_t *data_ptr;
+ int timeout = MXSMMC_MAX_TIMEOUT;
+ uint32_t reg;
+ uint32_t data_count = data->blocksize * data->blocks;
+
+ if (data->flags & MMC_DATA_READ) {
+ data_ptr = (uint32_t *)data->dest;
+ while (data_count && --timeout) {
+ reg = readl(&ssp_regs->hw_ssp_status);
+ if (!(reg & SSP_STATUS_FIFO_EMPTY)) {
+ *data_ptr++ = readl(&ssp_regs->hw_ssp_data);
+ data_count -= 4;
+ timeout = MXSMMC_MAX_TIMEOUT;
+ } else
+ udelay(1000);
+ }
+ } else {
+ data_ptr = (uint32_t *)data->src;
+ timeout *= 100;
+ while (data_count && --timeout) {
+ reg = readl(&ssp_regs->hw_ssp_status);
+ if (!(reg & SSP_STATUS_FIFO_FULL)) {
+ writel(*data_ptr++, &ssp_regs->hw_ssp_data);
+ data_count -= 4;
+ timeout = MXSMMC_MAX_TIMEOUT;
+ } else
+ udelay(1000);
+ }
+ }
+
+ return timeout ? 0 : COMM_ERR;
+}
+
+static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data)
+{
+ uint32_t data_count = data->blocksize * data->blocks;
+ uint32_t cache_data_count;
+ int dmach;
+ struct mxs_dma_desc *desc = priv->desc;
+
+ memset(desc, 0, sizeof(struct mxs_dma_desc));
+ desc->address = (dma_addr_t)desc;
+
+ if (data_count % ARCH_DMA_MINALIGN)
+ cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN);
+ else
+ cache_data_count = data_count;
+
+ if (data->flags & MMC_DATA_READ) {
+ priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
+ priv->desc->cmd.address = (dma_addr_t)data->dest;
+ } else {
+ priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
+ priv->desc->cmd.address = (dma_addr_t)data->src;
+
+ /* Flush data to DRAM so DMA can pick them up */
+ flush_dcache_range((uint32_t)priv->desc->cmd.address,
+ (uint32_t)(priv->desc->cmd.address + cache_data_count));
+ }
+
+ /* Invalidate the area, so no writeback into the RAM races with DMA */
+ invalidate_dcache_range((uint32_t)priv->desc->cmd.address,
+ (uint32_t)(priv->desc->cmd.address + cache_data_count));
+
+ priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |
+ (data_count << MXS_DMA_DESC_BYTES_OFFSET);
+
+ dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id;
+ mxs_dma_desc_append(dmach, priv->desc);
+ if (mxs_dma_go(dmach))
+ return COMM_ERR;
+
+ /* The data arrived into DRAM, invalidate cache over them */
+ if (data->flags & MMC_DATA_READ) {
+ invalidate_dcache_range((uint32_t)priv->desc->cmd.address,
+ (uint32_t)(priv->desc->cmd.address + cache_data_count));
+ }
+
+ return 0;
+}
/*
* Sends a command out on the bus. Takes the mmc pointer,
@@ -70,16 +148,11 @@ static int
mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv;
- struct mx28_ssp_regs *ssp_regs = priv->regs;
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
uint32_t reg;
int timeout;
- uint32_t data_count;
uint32_t ctrl0;
-#ifndef CONFIG_MXS_MMC_DMA
- uint32_t *data_ptr;
-#else
- uint32_t cache_data_count;
-#endif
+ int ret;
debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx);
@@ -117,6 +190,11 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */
ctrl0 |= SSP_CTRL0_LONG_RESP;
+ if (data && (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER))
+ writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
+ else
+ writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
+
/* Command index */
reg = readl(&ssp_regs->hw_ssp_cmd0);
reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC);
@@ -197,75 +275,23 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
if (!data)
return 0;
- data_count = data->blocksize * data->blocks;
- timeout = MXSMMC_MAX_TIMEOUT;
-
-#ifdef CONFIG_MXS_MMC_DMA
- if (data_count % ARCH_DMA_MINALIGN)
- cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN);
- else
- cache_data_count = data_count;
-
- if (data->flags & MMC_DATA_READ) {
- priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
- priv->desc->cmd.address = (dma_addr_t)data->dest;
- } else {
- priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
- priv->desc->cmd.address = (dma_addr_t)data->src;
-
- /* Flush data to DRAM so DMA can pick them up */
- flush_dcache_range((uint32_t)priv->desc->cmd.address,
- (uint32_t)(priv->desc->cmd.address + cache_data_count));
- }
-
- priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |
- (data_count << MXS_DMA_DESC_BYTES_OFFSET);
-
-
- mxs_dma_desc_append(MXS_DMA_CHANNEL_AHB_APBH_SSP0, priv->desc);
- if (mxs_dma_go(MXS_DMA_CHANNEL_AHB_APBH_SSP0)) {
- printf("MMC%d: DMA transfer failed\n", mmc->block_dev.dev);
- return COMM_ERR;
- }
-
- /* The data arrived into DRAM, invalidate cache over them */
- if (data->flags & MMC_DATA_READ) {
- invalidate_dcache_range((uint32_t)priv->desc->cmd.address,
- (uint32_t)(priv->desc->cmd.address + cache_data_count));
- }
-#else
- if (data->flags & MMC_DATA_READ) {
- data_ptr = (uint32_t *)data->dest;
- while (data_count && --timeout) {
- reg = readl(&ssp_regs->hw_ssp_status);
- if (!(reg & SSP_STATUS_FIFO_EMPTY)) {
- *data_ptr++ = readl(&ssp_regs->hw_ssp_data);
- data_count -= 4;
- timeout = MXSMMC_MAX_TIMEOUT;
- } else
- udelay(1000);
+ if (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER) {
+ ret = mxsmmc_send_cmd_pio(priv, data);
+ if (ret) {
+ printf("MMC%d: Data timeout with command %d "
+ "(status 0x%08x)!\n",
+ mmc->block_dev.dev, cmd->cmdidx, reg);
+ return ret;
}
} else {
- data_ptr = (uint32_t *)data->src;
- timeout *= 100;
- while (data_count && --timeout) {
- reg = readl(&ssp_regs->hw_ssp_status);
- if (!(reg & SSP_STATUS_FIFO_FULL)) {
- writel(*data_ptr++, &ssp_regs->hw_ssp_data);
- data_count -= 4;
- timeout = MXSMMC_MAX_TIMEOUT;
- } else
- udelay(1000);
+ ret = mxsmmc_send_cmd_dma(priv, data);
+ if (ret) {
+ printf("MMC%d: DMA transfer failed\n",
+ mmc->block_dev.dev);
+ return ret;
}
}
- if (!timeout) {
- printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n",
- mmc->block_dev.dev, cmd->cmdidx, reg);
- return COMM_ERR;
- }
-#endif
-
/* Check data errors */
reg = readl(&ssp_regs->hw_ssp_status);
if (reg &
@@ -282,7 +308,7 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
static void mxsmmc_set_ios(struct mmc *mmc)
{
struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv;
- struct mx28_ssp_regs *ssp_regs = priv->regs;
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
/* Set the clock speed */
if (mmc->clock)
@@ -311,16 +337,16 @@ static void mxsmmc_set_ios(struct mmc *mmc)
static int mxsmmc_init(struct mmc *mmc)
{
struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv;
- struct mx28_ssp_regs *ssp_regs = priv->regs;
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
/* Reset SSP */
- mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
+ mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
/* 8 bits word length in MMC mode */
clrsetbits_le32(&ssp_regs->hw_ssp_ctrl1,
- SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK,
- SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS |
- SSP_CTRL1_DMA_ENABLE);
+ SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK |
+ SSP_CTRL1_DMA_ENABLE,
+ SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS);
/* Set initial bit clock 400 KHz */
mx28_set_ssp_busclock(priv->id, 400);
@@ -335,8 +361,8 @@ static int mxsmmc_init(struct mmc *mmc)
int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
{
- struct mx28_clkctrl_regs *clkctrl_regs =
- (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
+ struct mxs_clkctrl_regs *clkctrl_regs =
+ (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
struct mmc *mmc = NULL;
struct mxsmmc_priv *priv = NULL;
int ret;
@@ -366,22 +392,22 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
priv->id = id;
switch (id) {
case 0:
- priv->regs = (struct mx28_ssp_regs *)MXS_SSP0_BASE;
+ priv->regs = (struct mxs_ssp_regs *)MXS_SSP0_BASE;
priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP0;
priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp0;
break;
case 1:
- priv->regs = (struct mx28_ssp_regs *)MXS_SSP1_BASE;
+ priv->regs = (struct mxs_ssp_regs *)MXS_SSP1_BASE;
priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP1;
priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp1;
break;
case 2:
- priv->regs = (struct mx28_ssp_regs *)MXS_SSP2_BASE;
+ priv->regs = (struct mxs_ssp_regs *)MXS_SSP2_BASE;
priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP2;
priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp2;
break;
case 3:
- priv->regs = (struct mx28_ssp_regs *)MXS_SSP3_BASE;
+ priv->regs = (struct mxs_ssp_regs *)MXS_SSP3_BASE;
priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP3;
priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp3;
break;
diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c
index 2c5bf17bb3..b3ec441b66 100644
--- a/drivers/mmc/pxa_mmc_gen.c
+++ b/drivers/mmc/pxa_mmc_gen.c
@@ -118,7 +118,7 @@ static int pxa_mmc_start_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
int ret;
/* The card can send a "busy" response */
- if (cmd->flags & MMC_RSP_BUSY)
+ if (cmd->resp_type & MMC_RSP_BUSY)
cmdat |= MMC_CMDAT_BUSY;
/* Inform the controller about response type */
@@ -181,9 +181,11 @@ static int pxa_mmc_cmd_done(struct mmc *mmc, struct mmc_cmd *cmd)
/* The command response didn't arrive */
if (stat & MMC_STAT_TIME_OUT_RESPONSE)
return -ETIMEDOUT;
- else if (stat & MMC_STAT_RES_CRC_ERROR && cmd->flags & MMC_RSP_CRC) {
+ else if (stat & MMC_STAT_RES_CRC_ERROR
+ && cmd->resp_type & MMC_RSP_CRC) {
#ifdef PXAMMC_CRC_SKIP
- if (cmd->flags & MMC_RSP_136 && cmd->response[0] & (1 << 31))
+ if (cmd->resp_type & MMC_RSP_136
+ && cmd->response[0] & (1 << 31))
printf("Ignoring CRC, this may be dangerous!\n");
else
#endif
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
index 1d4481b974..b9782367e2 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -21,6 +21,7 @@
#include <malloc.h>
#include <sdhci.h>
#include <asm/arch/mmc.h>
+#include <asm/arch/clk.h>
static char *S5P_NAME = "SAMSUNG SDHCI";
static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
@@ -54,7 +55,7 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
* 00 = Delay3 (inverter delay)
* 10 = Delay4 (inverter delay + 2ns)
*/
- val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1;
+ val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1;
sdhci_writel(host, val, SDHCI_CONTROL3);
/*
@@ -69,7 +70,7 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
sdhci_writel(host, ctrl, SDHCI_CONTROL2);
}
-int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)
+int s5p_sdhci_init(u32 regbase, int index, int bus_width)
{
struct sdhci_host *host = NULL;
host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
@@ -80,19 +81,18 @@ int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)
host->name = S5P_NAME;
host->ioaddr = (void *)regbase;
- host->quirks = quirks;
- host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE;
+ host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
+ SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR;
host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- if (quirks & SDHCI_QUIRK_REG32_RW)
- host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
- else
- host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+ host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->set_control_reg = &s5p_sdhci_set_control_reg;
+ host->set_clock = set_mmc_clk;
+ host->index = index;
host->host_caps = MMC_MODE_HC;
- add_sdhci(host, max_clk, min_clk);
+ add_sdhci(host, 52000000, 400000);
return 0;
}
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 1709643da8..2e3c408bc5 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -260,7 +260,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (clock == 0)
return 0;
- if (host->version >= SDHCI_SPEC_300) {
+ if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) {
/* Version 3.00 divisors must be a multiple of 2. */
if (mmc->f_max <= clock)
div = 1;
@@ -279,6 +279,9 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
}
div >>= 1;
+ if (host->set_clock)
+ host->set_clock(host->index, div);
+
clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
<< SDHCI_DIVIDER_HI_SHIFT;
@@ -347,10 +350,10 @@ void sdhci_set_ios(struct mmc *mmc)
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (mmc->bus_width == 8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
- if (host->version >= SDHCI_SPEC_300)
+ if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
ctrl |= SDHCI_CTRL_8BITBUS;
} else {
- if (host->version >= SDHCI_SPEC_300)
+ if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
ctrl &= ~SDHCI_CTRL_8BITBUS;
if (mmc->bus_width == 4)
ctrl |= SDHCI_CTRL_4BITBUS;
@@ -381,12 +384,25 @@ int sdhci_init(struct mmc *mmc)
}
}
+ sdhci_set_power(host, fls(mmc->voltages) - 1);
+
+ if (host->quirks & SDHCI_QUIRK_NO_CD) {
+ unsigned int status;
+
+ sdhci_writel(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST,
+ SDHCI_HOST_CONTROL);
+
+ status = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ while ((!(status & SDHCI_CARD_PRESENT)) ||
+ (!(status & SDHCI_CARD_STATE_STABLE)) ||
+ (!(status & SDHCI_CARD_DETECT_PIN_LEVEL)))
+ status = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ }
+
/* Eable all state */
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_ENABLE);
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_SIGNAL_ENABLE);
- sdhci_set_power(host, fls(mmc->voltages) - 1);
-
return 0;
}
@@ -421,7 +437,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
if (max_clk)
mmc->f_max = max_clk;
else {
- if (host->version >= SDHCI_SPEC_300)
+ if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
else
@@ -436,7 +452,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
if (min_clk)
mmc->f_min = min_clk;
else {
- if (host->version >= SDHCI_SPEC_300)
+ if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300;
else
mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200;
diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c
index 2835e242f8..4588568a6d 100644
--- a/drivers/mmc/sh_mmcif.c
+++ b/drivers/mmc/sh_mmcif.c
@@ -593,7 +593,7 @@ int mmcif_mmc_init(void)
mmc->f_max = CLKDEV_EMMC_DATA;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT |
- MMC_MODE_8BIT;
+ MMC_MODE_8BIT | MMC_MODE_HC;
memcpy(mmc->name, DRIVER_NAME, sizeof(DRIVER_NAME));
mmc->send_cmd = sh_mmcif_request;
mmc->set_ios = sh_mmcif_set_ios;
diff --git a/drivers/mmc/spl_mmc_load.c b/drivers/mmc/spl_mmc_load.c
new file mode 100644
index 0000000000..79a68fbcfb
--- /dev/null
+++ b/drivers/mmc/spl_mmc_load.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <mmc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void mmc_load_image(struct mmc *mmc)
+{
+ s32 err;
+ void (*uboot)(void) __noreturn;
+
+ err = mmc->block_dev.block_read(0, CONFIG_SYS_MMC_U_BOOT_OFFS,
+ CONFIG_SYS_MMC_U_BOOT_SIZE/512,
+ (u32 *)CONFIG_SYS_TEXT_BASE);
+
+ if (err <= 0) {
+ printf("spl: error reading image %s, err - %d\n",
+ "u-boot.img", err);
+ hang();
+ }
+ uboot = (void *) CONFIG_SYS_TEXT_BASE;
+ (*uboot)();
+}
+
+void spl_mmc_load(void)
+{
+ struct mmc *mmc;
+ int err;
+ void (mmc_load_image)(struct mmc *mmc) __noreturn;
+
+ mmc_initialize(gd->bd);
+ mmc = find_mmc_device(0);
+ if (!mmc) {
+ puts("spl: mmc device not found!!\n");
+ hang();
+ } else {
+ puts("spl: mmc device found\n");
+ }
+ err = mmc_init(mmc);
+ if (err) {
+ printf("spl: mmc init failed: err - %d\n", err);
+ hang();
+ }
+ mmc_load_image(mmc);
+}
diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c
index 29bf58359d..ca8fad8657 100644
--- a/drivers/mmc/tegra_mmc.c
+++ b/drivers/mmc/tegra_mmc.c
@@ -25,7 +25,7 @@
#include <asm/io.h>
#include <asm/arch/clk_rst.h>
#include <asm/arch/clock.h>
-#include "tegra_mmc.h"
+#include <asm/arch/tegra_mmc.h>
/* support 4 mmc hosts */
struct mmc mmc_dev[4];
@@ -39,31 +39,31 @@ struct mmc_host mmc_host[4];
* @param host Structure to fill in (base, reg, mmc_id)
* @param dev_index Device index (0-3)
*/
-static void tegra2_get_setup(struct mmc_host *host, int dev_index)
+static void tegra_get_setup(struct mmc_host *host, int dev_index)
{
- debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index);
+ debug("tegra_get_setup: dev_index = %d\n", dev_index);
switch (dev_index) {
case 1:
- host->base = TEGRA2_SDMMC3_BASE;
+ host->base = TEGRA_SDMMC3_BASE;
host->mmc_id = PERIPH_ID_SDMMC3;
break;
case 2:
- host->base = TEGRA2_SDMMC2_BASE;
+ host->base = TEGRA_SDMMC2_BASE;
host->mmc_id = PERIPH_ID_SDMMC2;
break;
case 3:
- host->base = TEGRA2_SDMMC1_BASE;
+ host->base = TEGRA_SDMMC1_BASE;
host->mmc_id = PERIPH_ID_SDMMC1;
break;
case 0:
default:
- host->base = TEGRA2_SDMMC4_BASE;
+ host->base = TEGRA_SDMMC4_BASE;
host->mmc_id = PERIPH_ID_SDMMC4;
break;
}
- host->reg = (struct tegra2_mmc *)host->base;
+ host->reg = (struct tegra_mmc *)host->base;
}
static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
@@ -345,7 +345,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
debug(" mmc_change_clock called\n");
/*
- * Change Tegra2 SDMMCx clock divisor here. Source is 216MHz,
+ * Change Tegra SDMMCx clock divisor here. Source is 216MHz,
* PLLP_OUT0
*/
if (clock == 0)
@@ -494,11 +494,11 @@ static int mmc_core_init(struct mmc *mmc)
return 0;
}
-int tegra2_mmc_getcd(struct mmc *mmc)
+int tegra_mmc_getcd(struct mmc *mmc)
{
struct mmc_host *host = (struct mmc_host *)mmc->priv;
- debug("tegra2_mmc_getcd called\n");
+ debug("tegra_mmc_getcd called\n");
if (host->cd_gpio >= 0)
return !gpio_get_value(host->cd_gpio);
@@ -506,13 +506,13 @@ int tegra2_mmc_getcd(struct mmc *mmc)
return 1;
}
-int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
+int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
{
struct mmc_host *host;
char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */
struct mmc *mmc;
- debug(" tegra2_mmc_init: index %d, bus width %d "
+ debug(" tegra_mmc_init: index %d, bus width %d "
"pwr_gpio %d cd_gpio %d\n",
dev_index, bus_width, pwr_gpio, cd_gpio);
@@ -521,7 +521,7 @@ int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
host->clock = 0;
host->pwr_gpio = pwr_gpio;
host->cd_gpio = cd_gpio;
- tegra2_get_setup(host, dev_index);
+ tegra_get_setup(host, dev_index);
clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000);
@@ -539,12 +539,12 @@ int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
mmc = &mmc_dev[dev_index];
- sprintf(mmc->name, "Tegra2 SD/MMC");
+ sprintf(mmc->name, "Tegra SD/MMC");
mmc->priv = host;
mmc->send_cmd = mmc_send_cmd;
mmc->set_ios = mmc_set_ios;
mmc->init = mmc_core_init;
- mmc->getcd = tegra2_mmc_getcd;
+ mmc->getcd = tegra_mmc_getcd;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
if (bus_width == 8)
@@ -559,7 +559,7 @@ int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
* max freq is highest HS eMMC clock as per the SD/MMC spec
* (actually 52MHz)
* Both of these are the closest equivalents w/216MHz source
- * clock and Tegra2 SDMMC divisors.
+ * clock and Tegra SDMMC divisors.
*/
mmc->f_min = 375000;
mmc->f_max = 48000000;
diff --git a/drivers/mmc/tegra_mmc.h b/drivers/mmc/tegra_mmc.h
deleted file mode 100644
index f9cdcaaaa6..0000000000
--- a/drivers/mmc/tegra_mmc.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * (C) Copyright 2009 SAMSUNG Electronics
- * Minkyu Kang <mk7.kang@samsung.com>
- * Portions Copyright (C) 2011-2012 NVIDIA Corporation
- *
- * 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 __TEGRA_MMC_H_
-#define __TEGRA_MMC_H_
-
-#define TEGRA2_SDMMC1_BASE 0xC8000000
-#define TEGRA2_SDMMC2_BASE 0xC8000200
-#define TEGRA2_SDMMC3_BASE 0xC8000400
-#define TEGRA2_SDMMC4_BASE 0xC8000600
-
-#ifndef __ASSEMBLY__
-struct tegra2_mmc {
- unsigned int sysad; /* _SYSTEM_ADDRESS_0 */
- unsigned short blksize; /* _BLOCK_SIZE_BLOCK_COUNT_0 15:00 */
- unsigned short blkcnt; /* _BLOCK_SIZE_BLOCK_COUNT_0 31:16 */
- unsigned int argument; /* _ARGUMENT_0 */
- unsigned short trnmod; /* _CMD_XFER_MODE_0 15:00 xfer mode */
- unsigned short cmdreg; /* _CMD_XFER_MODE_0 31:16 cmd reg */
- unsigned int rspreg0; /* _RESPONSE_R0_R1_0 CMD RESP 31:00 */
- unsigned int rspreg1; /* _RESPONSE_R2_R3_0 CMD RESP 63:32 */
- unsigned int rspreg2; /* _RESPONSE_R4_R5_0 CMD RESP 95:64 */
- unsigned int rspreg3; /* _RESPONSE_R6_R7_0 CMD RESP 127:96 */
- unsigned int bdata; /* _BUFFER_DATA_PORT_0 */
- unsigned int prnsts; /* _PRESENT_STATE_0 */
- unsigned char hostctl; /* _POWER_CONTROL_HOST_0 7:00 */
- unsigned char pwrcon; /* _POWER_CONTROL_HOST_0 15:8 */
- unsigned char blkgap; /* _POWER_CONTROL_HOST_9 23:16 */
- unsigned char wakcon; /* _POWER_CONTROL_HOST_0 31:24 */
- unsigned short clkcon; /* _CLOCK_CONTROL_0 15:00 */
- unsigned char timeoutcon; /* _TIMEOUT_CTRL 23:16 */
- unsigned char swrst; /* _SW_RESET_ 31:24 */
- unsigned int norintsts; /* _INTERRUPT_STATUS_0 */
- unsigned int norintstsen; /* _INTERRUPT_STATUS_ENABLE_0 */
- unsigned int norintsigen; /* _INTERRUPT_SIGNAL_ENABLE_0 */
- unsigned short acmd12errsts; /* _AUTO_CMD12_ERR_STATUS_0 15:00 */
- unsigned char res1[2]; /* _RESERVED 31:16 */
- unsigned int capareg; /* _CAPABILITIES_0 */
- unsigned char res2[4]; /* RESERVED, offset 44h-47h */
- unsigned int maxcurr; /* _MAXIMUM_CURRENT_0 */
- unsigned char res3[4]; /* RESERVED, offset 4Ch-4Fh */
- unsigned short setacmd12err; /* offset 50h */
- unsigned short setinterr; /* offset 52h */
- unsigned char admaerr; /* offset 54h */
- unsigned char res4[3]; /* RESERVED, offset 55h-57h */
- unsigned long admaaddr; /* offset 58h-5Fh */
- unsigned char res5[0x9c]; /* RESERVED, offset 60h-FBh */
- unsigned short slotintstatus; /* offset FCh */
- unsigned short hcver; /* HOST Version */
- unsigned char res6[0x100]; /* RESERVED, offset 100h-1FFh */
-};
-
-#define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3)
-#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3)
-#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3)
-#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT (3 << 3)
-
-#define TEGRA_MMC_TRNMOD_DMA_ENABLE (1 << 0)
-#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE (1 << 1)
-#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE (0 << 4)
-#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ (1 << 4)
-#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT (1 << 5)
-
-#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK (3 << 0)
-#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE (0 << 0)
-#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 (1 << 0)
-#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 (2 << 0)
-#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY (3 << 0)
-
-#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK (1 << 3)
-#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK (1 << 4)
-#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER (1 << 5)
-
-#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD (1 << 0)
-#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT (1 << 1)
-
-#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE (1 << 0)
-#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE (1 << 1)
-#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE (1 << 2)
-
-#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT 8
-#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK (0xff << 8)
-
-#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL (1 << 0)
-#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE (1 << 1)
-#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE (1 << 2)
-
-#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE (1 << 0)
-#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE (1 << 1)
-#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT (1 << 3)
-#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT (1 << 15)
-#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT (1 << 16)
-
-#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE (1 << 0)
-#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE (1 << 1)
-#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT (1 << 3)
-#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY (1 << 4)
-#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY (1 << 5)
-
-#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1)
-
-struct mmc_host {
- struct tegra2_mmc *reg;
- unsigned int version; /* SDHCI spec. version */
- unsigned int clock; /* Current clock (MHz) */
- unsigned int base; /* Base address, SDMMC1/2/3/4 */
- enum periph_id mmc_id; /* Peripheral ID: PERIPH_ID_... */
- int pwr_gpio; /* Power GPIO */
- int cd_gpio; /* Change Detect GPIO */
-};
-
-#endif /* __ASSEMBLY__ */
-#endif /* __TEGRA_MMC_H_ */
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c
index f0f301a460..43140f3647 100644
--- a/drivers/mtd/cfi_flash.c
+++ b/drivers/mtd/cfi_flash.c
@@ -1077,7 +1077,38 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
for (sect = s_first; sect <= s_last; sect++) {
+ if (ctrlc()) {
+ printf("\n");
+ return 1;
+ }
+
if (info->protect[sect] == 0) { /* not protected */
+#ifdef CONFIG_SYS_FLASH_CHECK_BLANK_BEFORE_ERASE
+ int k;
+ int size;
+ int erased;
+ u32 *flash;
+
+ /*
+ * Check if whole sector is erased
+ */
+ size = flash_sector_size(info, sect);
+ erased = 1;
+ flash = (u32 *)info->start[sect];
+ /* divide by 4 for longword access */
+ size = size >> 2;
+ for (k = 0; k < size; k++) {
+ if (flash_read32(flash++) != 0xffffffff) {
+ erased = 0;
+ break;
+ }
+ }
+ if (erased) {
+ if (flash_verbose)
+ putc(',');
+ continue;
+ }
+#endif
switch (info->vendor) {
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_STANDARD:
@@ -1353,6 +1384,9 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
src += i;
cnt -= i;
FLASH_SHOW_PROGRESS(scale, dots, digit, i);
+ /* Only check every once in a while */
+ if ((cnt & 0xFFFF) < buffered_size && ctrlc())
+ return ERR_ABORTED;
}
#else
while (cnt >= info->portwidth) {
@@ -1365,6 +1399,9 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
wp += info->portwidth;
cnt -= info->portwidth;
FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth);
+ /* Only check every once in a while */
+ if ((cnt & 0xFFFF) < info->portwidth && ctrlc())
+ return ERR_ABORTED;
}
#endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 29dc20ef5e..beb99cacb6 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -62,6 +62,7 @@ COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o
+COBJS-$(CONFIG_TEGRA_NAND) += tegra_nand.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
endif
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index de663824fe..c6aa5db33c 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -5,6 +5,9 @@
*
* (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
*
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * (C) Copyright 2012 ATMEL, Hong Xu
+ *
* See file CREDITS for list of people who contributed to this
* project.
*
@@ -30,6 +33,7 @@
#include <asm/arch/at91_pio.h>
#include <nand.h>
+#include <watchdog.h>
#ifdef CONFIG_ATMEL_NAND_HWECC
@@ -41,6 +45,674 @@
#include "atmel_nand_ecc.h" /* Hardware ECC registers */
+#ifdef CONFIG_ATMEL_NAND_HW_PMECC
+
+struct atmel_nand_host {
+ struct pmecc_regs __iomem *pmecc;
+ struct pmecc_errloc_regs __iomem *pmerrloc;
+ void __iomem *pmecc_rom_base;
+
+ u8 pmecc_corr_cap;
+ u16 pmecc_sector_size;
+ u32 pmecc_index_table_offset;
+
+ int pmecc_bytes_per_sector;
+ int pmecc_sector_number;
+ int pmecc_degree; /* Degree of remainders */
+ int pmecc_cw_len; /* Length of codeword */
+
+ /* lookup table for alpha_to and index_of */
+ void __iomem *pmecc_alpha_to;
+ void __iomem *pmecc_index_of;
+
+ /* data for pmecc computation */
+ int16_t pmecc_smu[(CONFIG_PMECC_CAP + 2) * (2 * CONFIG_PMECC_CAP + 1)];
+ int16_t pmecc_partial_syn[2 * CONFIG_PMECC_CAP + 1];
+ int16_t pmecc_si[2 * CONFIG_PMECC_CAP + 1];
+ int16_t pmecc_lmu[CONFIG_PMECC_CAP + 1]; /* polynomal order */
+ int pmecc_mu[CONFIG_PMECC_CAP + 1];
+ int pmecc_dmu[CONFIG_PMECC_CAP + 1];
+ int pmecc_delta[CONFIG_PMECC_CAP + 1];
+};
+
+static struct atmel_nand_host pmecc_host;
+static struct nand_ecclayout atmel_pmecc_oobinfo;
+
+/*
+ * Return number of ecc bytes per sector according to sector size and
+ * correction capability
+ *
+ * Following table shows what at91 PMECC supported:
+ * Correction Capability Sector_512_bytes Sector_1024_bytes
+ * ===================== ================ =================
+ * 2-bits 4-bytes 4-bytes
+ * 4-bits 7-bytes 7-bytes
+ * 8-bits 13-bytes 14-bytes
+ * 12-bits 20-bytes 21-bytes
+ * 24-bits 39-bytes 42-bytes
+ */
+static int pmecc_get_ecc_bytes(int cap, int sector_size)
+{
+ int m = 12 + sector_size / 512;
+ return (m * cap + 7) / 8;
+}
+
+static void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
+ int oobsize, int ecc_len)
+{
+ int i;
+
+ layout->eccbytes = ecc_len;
+
+ /* ECC will occupy the last ecc_len bytes continuously */
+ for (i = 0; i < ecc_len; i++)
+ layout->eccpos[i] = oobsize - ecc_len + i;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length =
+ oobsize - ecc_len - layout->oobfree[0].offset;
+}
+
+static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
+{
+ int table_size;
+
+ table_size = host->pmecc_sector_size == 512 ?
+ PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024;
+
+ /* the ALPHA lookup table is right behind the INDEX lookup table. */
+ return host->pmecc_rom_base + host->pmecc_index_table_offset +
+ table_size * sizeof(int16_t);
+}
+
+static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int i;
+ uint32_t value;
+
+ /* Fill odd syndromes */
+ for (i = 0; i < host->pmecc_corr_cap; i++) {
+ value = readl(&host->pmecc->rem_port[sector].rem[i / 2]);
+ if (i & 1)
+ value >>= 16;
+ value &= 0xffff;
+ host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
+ }
+}
+
+static void pmecc_substitute(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int16_t __iomem *alpha_to = host->pmecc_alpha_to;
+ int16_t __iomem *index_of = host->pmecc_index_of;
+ int16_t *partial_syn = host->pmecc_partial_syn;
+ const int cap = host->pmecc_corr_cap;
+ int16_t *si;
+ int i, j;
+
+ /* si[] is a table that holds the current syndrome value,
+ * an element of that table belongs to the field
+ */
+ si = host->pmecc_si;
+
+ memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
+
+ /* Computation 2t syndromes based on S(x) */
+ /* Odd syndromes */
+ for (i = 1; i < 2 * cap; i += 2) {
+ for (j = 0; j < host->pmecc_degree; j++) {
+ if (partial_syn[i] & (0x1 << j))
+ si[i] = readw(alpha_to + i * j) ^ si[i];
+ }
+ }
+ /* Even syndrome = (Odd syndrome) ** 2 */
+ for (i = 2, j = 1; j <= cap; i = ++j << 1) {
+ if (si[j] == 0) {
+ si[i] = 0;
+ } else {
+ int16_t tmp;
+
+ tmp = readw(index_of + si[j]);
+ tmp = (tmp * 2) % host->pmecc_cw_len;
+ si[i] = readw(alpha_to + tmp);
+ }
+ }
+}
+
+/*
+ * This function defines a Berlekamp iterative procedure for
+ * finding the value of the error location polynomial.
+ * The input is si[], initialize by pmecc_substitute().
+ * The output is smu[][].
+ *
+ * This function is written according to chip datasheet Chapter:
+ * Find the Error Location Polynomial Sigma(x) of Section:
+ * Programmable Multibit ECC Control (PMECC).
+ */
+static void pmecc_get_sigma(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+
+ int16_t *lmu = host->pmecc_lmu;
+ int16_t *si = host->pmecc_si;
+ int *mu = host->pmecc_mu;
+ int *dmu = host->pmecc_dmu; /* Discrepancy */
+ int *delta = host->pmecc_delta; /* Delta order */
+ int cw_len = host->pmecc_cw_len;
+ const int16_t cap = host->pmecc_corr_cap;
+ const int num = 2 * cap + 1;
+ int16_t __iomem *index_of = host->pmecc_index_of;
+ int16_t __iomem *alpha_to = host->pmecc_alpha_to;
+ int i, j, k;
+ uint32_t dmu_0_count, tmp;
+ int16_t *smu = host->pmecc_smu;
+
+ /* index of largest delta */
+ int ro;
+ int largest;
+ int diff;
+
+ /* Init the Sigma(x) */
+ memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu));
+
+ dmu_0_count = 0;
+
+ /* First Row */
+
+ /* Mu */
+ mu[0] = -1;
+
+ smu[0] = 1;
+
+ /* discrepancy set to 1 */
+ dmu[0] = 1;
+ /* polynom order set to 0 */
+ lmu[0] = 0;
+ /* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */
+ delta[0] = -1;
+
+ /* Second Row */
+
+ /* Mu */
+ mu[1] = 0;
+ /* Sigma(x) set to 1 */
+ smu[num] = 1;
+
+ /* discrepancy set to S1 */
+ dmu[1] = si[1];
+
+ /* polynom order set to 0 */
+ lmu[1] = 0;
+
+ /* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */
+ delta[1] = 0;
+
+ for (i = 1; i <= cap; i++) {
+ mu[i + 1] = i << 1;
+ /* Begin Computing Sigma (Mu+1) and L(mu) */
+ /* check if discrepancy is set to 0 */
+ if (dmu[i] == 0) {
+ dmu_0_count++;
+
+ tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
+ if ((cap - (lmu[i] >> 1) - 1) & 0x1)
+ tmp += 2;
+ else
+ tmp += 1;
+
+ if (dmu_0_count == tmp) {
+ for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
+ smu[(cap + 1) * num + j] =
+ smu[i * num + j];
+
+ lmu[cap + 1] = lmu[i];
+ return;
+ }
+
+ /* copy polynom */
+ for (j = 0; j <= lmu[i] >> 1; j++)
+ smu[(i + 1) * num + j] = smu[i * num + j];
+
+ /* copy previous polynom order to the next */
+ lmu[i + 1] = lmu[i];
+ } else {
+ ro = 0;
+ largest = -1;
+ /* find largest delta with dmu != 0 */
+ for (j = 0; j < i; j++) {
+ if ((dmu[j]) && (delta[j] > largest)) {
+ largest = delta[j];
+ ro = j;
+ }
+ }
+
+ /* compute difference */
+ diff = (mu[i] - mu[ro]);
+
+ /* Compute degree of the new smu polynomial */
+ if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
+ lmu[i + 1] = lmu[i];
+ else
+ lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
+
+ /* Init smu[i+1] with 0 */
+ for (k = 0; k < num; k++)
+ smu[(i + 1) * num + k] = 0;
+
+ /* Compute smu[i+1] */
+ for (k = 0; k <= lmu[ro] >> 1; k++) {
+ int16_t a, b, c;
+
+ if (!(smu[ro * num + k] && dmu[i]))
+ continue;
+ a = readw(index_of + dmu[i]);
+ b = readw(index_of + dmu[ro]);
+ c = readw(index_of + smu[ro * num + k]);
+ tmp = a + (cw_len - b) + c;
+ a = readw(alpha_to + tmp % cw_len);
+ smu[(i + 1) * num + (k + diff)] = a;
+ }
+
+ for (k = 0; k <= lmu[i] >> 1; k++)
+ smu[(i + 1) * num + k] ^= smu[i * num + k];
+ }
+
+ /* End Computing Sigma (Mu+1) and L(mu) */
+ /* In either case compute delta */
+ delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
+
+ /* Do not compute discrepancy for the last iteration */
+ if (i >= cap)
+ continue;
+
+ for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
+ tmp = 2 * (i - 1);
+ if (k == 0) {
+ dmu[i + 1] = si[tmp + 3];
+ } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
+ int16_t a, b, c;
+ a = readw(index_of +
+ smu[(i + 1) * num + k]);
+ b = si[2 * (i - 1) + 3 - k];
+ c = readw(index_of + b);
+ tmp = a + c;
+ tmp %= cw_len;
+ dmu[i + 1] = readw(alpha_to + tmp) ^
+ dmu[i + 1];
+ }
+ }
+ }
+}
+
+static int pmecc_err_location(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ const int cap = host->pmecc_corr_cap;
+ const int num = 2 * cap + 1;
+ int sector_size = host->pmecc_sector_size;
+ int err_nbr = 0; /* number of error */
+ int roots_nbr; /* number of roots */
+ int i;
+ uint32_t val;
+ int16_t *smu = host->pmecc_smu;
+ int timeout = PMECC_MAX_TIMEOUT_US;
+
+ writel(PMERRLOC_DISABLE, &host->pmerrloc->eldis);
+
+ for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
+ writel(smu[(cap + 1) * num + i], &host->pmerrloc->sigma[i]);
+ err_nbr++;
+ }
+
+ val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1);
+ if (sector_size == 1024)
+ val |= PMERRLOC_ELCFG_SECTOR_1024;
+
+ writel(val, &host->pmerrloc->elcfg);
+ writel(sector_size * 8 + host->pmecc_degree * cap,
+ &host->pmerrloc->elen);
+
+ while (--timeout) {
+ if (readl(&host->pmerrloc->elisr) & PMERRLOC_CALC_DONE)
+ break;
+ WATCHDOG_RESET();
+ udelay(1);
+ }
+
+ if (!timeout) {
+ printk(KERN_ERR "atmel_nand : Timeout to calculate PMECC error location\n");
+ return -1;
+ }
+
+ roots_nbr = (readl(&host->pmerrloc->elisr) & PMERRLOC_ERR_NUM_MASK)
+ >> 8;
+ /* Number of roots == degree of smu hence <= cap */
+ if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
+ return err_nbr - 1;
+
+ /* Number of roots does not match the degree of smu
+ * unable to correct error */
+ return -1;
+}
+
+static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
+ int sector_num, int extra_bytes, int err_nbr)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int i = 0;
+ int byte_pos, bit_pos, sector_size, pos;
+ uint32_t tmp;
+ uint8_t err_byte;
+
+ sector_size = host->pmecc_sector_size;
+
+ while (err_nbr) {
+ tmp = readl(&host->pmerrloc->el[i]) - 1;
+ byte_pos = tmp / 8;
+ bit_pos = tmp % 8;
+
+ if (byte_pos >= (sector_size + extra_bytes))
+ BUG(); /* should never happen */
+
+ if (byte_pos < sector_size) {
+ err_byte = *(buf + byte_pos);
+ *(buf + byte_pos) ^= (1 << bit_pos);
+
+ pos = sector_num * host->pmecc_sector_size + byte_pos;
+ printk(KERN_INFO "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
+ pos, bit_pos, err_byte, *(buf + byte_pos));
+ } else {
+ /* Bit flip in OOB area */
+ tmp = sector_num * host->pmecc_bytes_per_sector
+ + (byte_pos - sector_size);
+ err_byte = ecc[tmp];
+ ecc[tmp] ^= (1 << bit_pos);
+
+ pos = tmp + nand_chip->ecc.layout->eccpos[0];
+ printk(KERN_INFO "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
+ pos, bit_pos, err_byte, ecc[tmp]);
+ }
+
+ i++;
+ err_nbr--;
+ }
+
+ return;
+}
+
+static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
+ u8 *ecc)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int i, err_nbr, eccbytes;
+ uint8_t *buf_pos;
+
+ eccbytes = nand_chip->ecc.bytes;
+ for (i = 0; i < eccbytes; i++)
+ if (ecc[i] != 0xff)
+ goto normal_check;
+ /* Erased page, return OK */
+ return 0;
+
+normal_check:
+ for (i = 0; i < host->pmecc_sector_number; i++) {
+ err_nbr = 0;
+ if (pmecc_stat & 0x1) {
+ buf_pos = buf + i * host->pmecc_sector_size;
+
+ pmecc_gen_syndrome(mtd, i);
+ pmecc_substitute(mtd);
+ pmecc_get_sigma(mtd);
+
+ err_nbr = pmecc_err_location(mtd);
+ if (err_nbr == -1) {
+ printk(KERN_ERR "PMECC: Too many errors\n");
+ mtd->ecc_stats.failed++;
+ return -EIO;
+ } else {
+ pmecc_correct_data(mtd, buf_pos, ecc, i,
+ host->pmecc_bytes_per_sector, err_nbr);
+ mtd->ecc_stats.corrected += err_nbr;
+ }
+ }
+ pmecc_stat >>= 1;
+ }
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ struct atmel_nand_host *host = chip->priv;
+ int eccsize = chip->ecc.size;
+ uint8_t *oob = chip->oob_poi;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t stat;
+ int timeout = PMECC_MAX_TIMEOUT_US;
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
+ pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg))
+ & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE);
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA);
+
+ chip->read_buf(mtd, buf, eccsize);
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ while (--timeout) {
+ if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY))
+ break;
+ WATCHDOG_RESET();
+ udelay(1);
+ }
+
+ if (!timeout) {
+ printk(KERN_ERR "atmel_nand : Timeout to read PMECC page\n");
+ return -1;
+ }
+
+ stat = pmecc_readl(host->pmecc, isr);
+ if (stat != 0)
+ if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ struct atmel_nand_host *host = chip->priv;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ int i, j;
+ int timeout = PMECC_MAX_TIMEOUT_US;
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
+
+ pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) |
+ PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE);
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA);
+
+ chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
+
+ while (--timeout) {
+ if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY))
+ break;
+ WATCHDOG_RESET();
+ udelay(1);
+ }
+
+ if (!timeout) {
+ printk(KERN_ERR "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n");
+ return;
+ }
+
+ for (i = 0; i < host->pmecc_sector_number; i++) {
+ for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
+ int pos;
+
+ pos = i * host->pmecc_bytes_per_sector + j;
+ chip->oob_poi[eccpos[pos]] =
+ readb(&host->pmecc->ecc_port[i].ecc[j]);
+ }
+ }
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+static void atmel_pmecc_core_init(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ uint32_t val = 0;
+ struct nand_ecclayout *ecc_layout;
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
+
+ switch (host->pmecc_corr_cap) {
+ case 2:
+ val = PMECC_CFG_BCH_ERR2;
+ break;
+ case 4:
+ val = PMECC_CFG_BCH_ERR4;
+ break;
+ case 8:
+ val = PMECC_CFG_BCH_ERR8;
+ break;
+ case 12:
+ val = PMECC_CFG_BCH_ERR12;
+ break;
+ case 24:
+ val = PMECC_CFG_BCH_ERR24;
+ break;
+ }
+
+ if (host->pmecc_sector_size == 512)
+ val |= PMECC_CFG_SECTOR512;
+ else if (host->pmecc_sector_size == 1024)
+ val |= PMECC_CFG_SECTOR1024;
+
+ switch (host->pmecc_sector_number) {
+ case 1:
+ val |= PMECC_CFG_PAGE_1SECTOR;
+ break;
+ case 2:
+ val |= PMECC_CFG_PAGE_2SECTORS;
+ break;
+ case 4:
+ val |= PMECC_CFG_PAGE_4SECTORS;
+ break;
+ case 8:
+ val |= PMECC_CFG_PAGE_8SECTORS;
+ break;
+ }
+
+ val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
+ | PMECC_CFG_AUTO_DISABLE);
+ pmecc_writel(host->pmecc, cfg, val);
+
+ ecc_layout = nand_chip->ecc.layout;
+ pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1);
+ pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]);
+ pmecc_writel(host->pmecc, eaddr,
+ ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
+ /* See datasheet about PMECC Clock Control Register */
+ pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ);
+ pmecc_writel(host->pmecc, idr, 0xff);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
+}
+
+static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
+ struct mtd_info *mtd)
+{
+ struct atmel_nand_host *host;
+ int cap, sector_size;
+
+ host = nand->priv = &pmecc_host;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.calculate = NULL;
+ nand->ecc.correct = NULL;
+ nand->ecc.hwctl = NULL;
+
+ cap = host->pmecc_corr_cap = CONFIG_PMECC_CAP;
+ sector_size = host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
+ host->pmecc_index_table_offset = CONFIG_PMECC_INDEX_TABLE_OFFSET;
+
+ printk(KERN_INFO "Initialize PMECC params, cap: %d, sector: %d\n",
+ cap, sector_size);
+
+ host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC;
+ host->pmerrloc = (struct pmecc_errloc_regs __iomem *)
+ ATMEL_BASE_PMERRLOC;
+ host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM;
+
+ /* ECC is calculated for the whole page (1 step) */
+ nand->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 2048:
+ case 4096:
+ host->pmecc_degree = PMECC_GF_DIMENSION_13;
+ host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
+ host->pmecc_sector_number = mtd->writesize / sector_size;
+ host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
+ cap, sector_size);
+ host->pmecc_alpha_to = pmecc_get_alpha_to(host);
+ host->pmecc_index_of = host->pmecc_rom_base +
+ host->pmecc_index_table_offset;
+
+ nand->ecc.steps = 1;
+ nand->ecc.bytes = host->pmecc_bytes_per_sector *
+ host->pmecc_sector_number;
+ if (nand->ecc.bytes > mtd->oobsize - 2) {
+ printk(KERN_ERR "No room for ECC bytes\n");
+ return -EINVAL;
+ }
+ pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
+ mtd->oobsize,
+ nand->ecc.bytes);
+ nand->ecc.layout = &atmel_pmecc_oobinfo;
+ break;
+ case 512:
+ case 1024:
+ /* TODO */
+ printk(KERN_ERR "Unsupported page size for PMECC, use Software ECC\n");
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.read_page = NULL;
+ nand->ecc.postpad = 0;
+ nand->ecc.prepad = 0;
+ nand->ecc.bytes = 0;
+ return 0;
+ }
+
+ nand->ecc.read_page = atmel_nand_pmecc_read_page;
+ nand->ecc.write_page = atmel_nand_pmecc_write_page;
+
+ atmel_pmecc_core_init(mtd);
+
+ return 0;
+}
+
+#else
+
/* oob layout for large page size
* bad block info is on bytes 0 and 1
* the bytes have to be consecutives to avoid
@@ -79,7 +751,6 @@ static struct nand_ecclayout atmel_oobinfo_small = {
static int atmel_nand_calculate(struct mtd_info *mtd,
const u_char *dat, unsigned char *ecc_code)
{
- struct nand_chip *nand_chip = mtd->priv;
unsigned int ecc_value;
/* get the first 2 ECC bytes */
@@ -167,7 +838,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *isnull)
{
struct nand_chip *nand_chip = mtd->priv;
- unsigned int ecc_status, ecc_parity, ecc_mode;
+ unsigned int ecc_status;
unsigned int ecc_word, ecc_bit;
/* get the status from the Status Register */
@@ -232,7 +903,63 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
{
}
-#endif
+
+int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd)
+{
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.calculate = atmel_nand_calculate;
+ nand->ecc.correct = atmel_nand_correct;
+ nand->ecc.hwctl = atmel_nand_hwctl;
+ nand->ecc.read_page = atmel_nand_read_page;
+ nand->ecc.bytes = 4;
+
+ if (nand->ecc.mode == NAND_ECC_HW) {
+ /* ECC is calculated for the whole page (1 step) */
+ nand->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 512:
+ nand->ecc.layout = &atmel_oobinfo_small;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_528);
+ break;
+ case 1024:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_1056);
+ break;
+ case 2048:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_2112);
+ break;
+ case 4096:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_4224);
+ break;
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.calculate = NULL;
+ nand->ecc.correct = NULL;
+ nand->ecc.hwctl = NULL;
+ nand->ecc.read_page = NULL;
+ nand->ecc.postpad = 0;
+ nand->ecc.prepad = 0;
+ nand->ecc.bytes = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_ATMEL_NAND_HW_PMECC */
+
+#endif /* CONFIG_ATMEL_NAND_HWECC */
static void at91_nand_hwcontrol(struct mtd_info *mtd,
int cmd, unsigned int ctrl)
@@ -267,12 +994,20 @@ static int at91_nand_ready(struct mtd_info *mtd)
}
#endif
-int board_nand_init(struct nand_chip *nand)
-{
-#ifdef CONFIG_ATMEL_NAND_HWECC
- static int chip_nr = 0;
- struct mtd_info *mtd;
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
#endif
+static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
+static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
+
+int atmel_nand_chip_init(int devnum, ulong base_addr)
+{
+ int ret;
+ struct mtd_info *mtd = &nand_info[devnum];
+ struct nand_chip *nand = &nand_chip[devnum];
+
+ mtd->priv = nand;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
nand->ecc.mode = NAND_ECC_SOFT;
#ifdef CONFIG_SYS_NAND_DBW_16
@@ -284,62 +1019,32 @@ int board_nand_init(struct nand_chip *nand)
#endif
nand->chip_delay = 20;
-#ifdef CONFIG_ATMEL_NAND_HWECC
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.calculate = atmel_nand_calculate;
- nand->ecc.correct = atmel_nand_correct;
- nand->ecc.hwctl = atmel_nand_hwctl;
- nand->ecc.read_page = atmel_nand_read_page;
- nand->ecc.bytes = 4;
-#endif
+ ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+ if (ret)
+ return ret;
#ifdef CONFIG_ATMEL_NAND_HWECC
- mtd = &nand_info[chip_nr++];
- mtd->priv = nand;
-
- /* Detect NAND chips */
- if (nand_scan_ident(mtd, 1, NULL)) {
- printk(KERN_WARNING "NAND Flash not found !\n");
- return -ENXIO;
- }
+#ifdef CONFIG_ATMEL_NAND_HW_PMECC
+ ret = atmel_pmecc_nand_init_params(nand, mtd);
+#else
+ ret = atmel_hwecc_nand_init_param(nand, mtd);
+#endif
+ if (ret)
+ return ret;
+#endif
- if (nand->ecc.mode == NAND_ECC_HW) {
- /* ECC is calculated for the whole page (1 step) */
- nand->ecc.size = mtd->writesize;
+ ret = nand_scan_tail(mtd);
+ if (!ret)
+ nand_register(devnum);
- /* set ECC page size and oob layout */
- switch (mtd->writesize) {
- case 512:
- nand->ecc.layout = &atmel_oobinfo_small;
- ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_528);
- break;
- case 1024:
- nand->ecc.layout = &atmel_oobinfo_large;
- ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_1056);
- break;
- case 2048:
- nand->ecc.layout = &atmel_oobinfo_large;
- ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_2112);
- break;
- case 4096:
- nand->ecc.layout = &atmel_oobinfo_large;
- ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_4224);
- break;
- default:
- /* page size not handled by HW ECC */
- /* switching back to soft ECC */
- nand->ecc.mode = NAND_ECC_SOFT;
- nand->ecc.calculate = NULL;
- nand->ecc.correct = NULL;
- nand->ecc.hwctl = NULL;
- nand->ecc.read_page = NULL;
- nand->ecc.postpad = 0;
- nand->ecc.prepad = 0;
- nand->ecc.bytes = 0;
- break;
- }
- }
-#endif
+ return ret;
+}
- return 0;
+void board_nand_init(void)
+{
+ int i;
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ if (atmel_nand_chip_init(i, base_addr[i]))
+ printk(KERN_ERR "atmel_nand: Fail to initialize #%d chip",
+ i);
}
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
index 1ee7f993db..4732582e7f 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/atmel_nand_ecc.h
@@ -33,4 +33,117 @@
#define ATMEL_ECC_NPR 0x10 /* NParity register */
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
+/* Register access macros for PMECC */
+#define pmecc_readl(addr, reg) \
+ readl(&addr->reg)
+
+#define pmecc_writel(addr, reg, value) \
+ writel((value), &addr->reg)
+
+/* PMECC Register Definitions */
+#define PMECC_MAX_SECTOR_NUM 8
+struct pmecc_regs {
+ u32 cfg; /* 0x00 PMECC Configuration Register */
+ u32 sarea; /* 0x04 PMECC Spare Area Size Register */
+ u32 saddr; /* 0x08 PMECC Start Address Register */
+ u32 eaddr; /* 0x0C PMECC End Address Register */
+ u32 clk; /* 0x10 PMECC Clock Control Register */
+ u32 ctrl; /* 0x14 PMECC Control Register */
+ u32 sr; /* 0x18 PMECC Status Register */
+ u32 ier; /* 0x1C PMECC Interrupt Enable Register */
+ u32 idr; /* 0x20 PMECC Interrupt Disable Register */
+ u32 imr; /* 0x24 PMECC Interrupt Mask Register */
+ u32 isr; /* 0x28 PMECC Interrupt Status Register */
+ u32 reserved0[5]; /* 0x2C-0x3C Reserved */
+
+ /* 0x40 + sector_num * (0x40), Redundancy Registers */
+ struct {
+ u8 ecc[44]; /* PMECC Generated Redundancy Byte Per Sector */
+ u32 reserved1[5];
+ } ecc_port[PMECC_MAX_SECTOR_NUM];
+
+ /* 0x240 + sector_num * (0x40) Remainder Registers */
+ struct {
+ u32 rem[12];
+ u32 reserved2[4];
+ } rem_port[PMECC_MAX_SECTOR_NUM];
+ u32 reserved3[16]; /* 0x440-0x47C Reserved */
+};
+
+/* For PMECC Configuration Register */
+#define PMECC_CFG_BCH_ERR2 (0 << 0)
+#define PMECC_CFG_BCH_ERR4 (1 << 0)
+#define PMECC_CFG_BCH_ERR8 (2 << 0)
+#define PMECC_CFG_BCH_ERR12 (3 << 0)
+#define PMECC_CFG_BCH_ERR24 (4 << 0)
+
+#define PMECC_CFG_SECTOR512 (0 << 4)
+#define PMECC_CFG_SECTOR1024 (1 << 4)
+
+#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
+#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
+#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
+#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
+
+#define PMECC_CFG_READ_OP (0 << 12)
+#define PMECC_CFG_WRITE_OP (1 << 12)
+
+#define PMECC_CFG_SPARE_ENABLE (1 << 16)
+#define PMECC_CFG_SPARE_DISABLE (0 << 16)
+
+#define PMECC_CFG_AUTO_ENABLE (1 << 20)
+#define PMECC_CFG_AUTO_DISABLE (0 << 20)
+
+/* For PMECC Clock Control Register */
+#define PMECC_CLK_133MHZ (2 << 0)
+
+/* For PMECC Control Register */
+#define PMECC_CTRL_RST (1 << 0)
+#define PMECC_CTRL_DATA (1 << 1)
+#define PMECC_CTRL_USER (1 << 2)
+#define PMECC_CTRL_ENABLE (1 << 4)
+#define PMECC_CTRL_DISABLE (1 << 5)
+
+/* For PMECC Status Register */
+#define PMECC_SR_BUSY (1 << 0)
+#define PMECC_SR_ENABLE (1 << 4)
+
+/* PMERRLOC Register Definitions */
+struct pmecc_errloc_regs {
+ u32 elcfg; /* 0x00 Error Location Configuration Register */
+ u32 elprim; /* 0x04 Error Location Primitive Register */
+ u32 elen; /* 0x08 Error Location Enable Register */
+ u32 eldis; /* 0x0C Error Location Disable Register */
+ u32 elsr; /* 0x10 Error Location Status Register */
+ u32 elier; /* 0x14 Error Location Interrupt Enable Register */
+ u32 elidr; /* 0x08 Error Location Interrupt Disable Register */
+ u32 elimr; /* 0x0C Error Location Interrupt Mask Register */
+ u32 elisr; /* 0x20 Error Location Interrupt Status Register */
+ u32 reserved0; /* 0x24 Reserved */
+ u32 sigma[25]; /* 0x28-0x88 Error Location Sigma Registers */
+ u32 el[24]; /* 0x8C-0xE8 Error Location Registers */
+ u32 reserved1[5]; /* 0xEC-0xFC Reserved */
+};
+
+/* For Error Location Configuration Register */
+#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
+#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
+#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
+
+/* For Error Location Disable Register */
+#define PMERRLOC_DISABLE (1 << 0)
+
+/* For Error Location Interrupt Status Register */
+#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
+#define PMERRLOC_CALC_DONE (1 << 0)
+
+/* Galois field dimension */
+#define PMECC_GF_DIMENSION_13 13
+#define PMECC_GF_DIMENSION_14 14
+
+#define PMECC_INDEX_TABLE_SIZE_512 0x2000
+#define PMECC_INDEX_TABLE_SIZE_1024 0x4000
+
+#define PMECC_MAX_TIMEOUT_US (100 * 1000)
+
#endif
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 936186f75e..d0ded483e2 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -25,168 +25,23 @@
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35)
#include <asm/arch/imx-regs.h>
#endif
+#include <fsl_nfc.h>
#define DRIVER_NAME "mxc_nand"
-/*
- * TODO: Use same register defs here as nand_spl mxc nand driver.
- */
-/*
- * Register map and bit definitions for the Freescale NAND Flash Controller
- * present in various i.MX devices.
- *
- * MX31 and MX27 have version 1 which has
- * 4 512 byte main buffers and
- * 4 16 byte spare buffers
- * to support up to 2K byte pagesize nand.
- * Reading or writing a 2K page requires 4 FDI/FDO cycles.
- *
- * MX25 has version 1.1 which has
- * 8 512 byte main buffers and
- * 8 64 byte spare buffers
- * to support up to 4K byte pagesize nand.
- * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle.
- * Also some of registers are moved and/or changed meaning as seen below.
- */
-#if defined(CONFIG_MX31) || defined(CONFIG_MX27)
-#define MXC_NFC_V1
-#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
-#define MXC_NFC_V1_1
-#else
-#warning "MXC NFC version not defined"
-#endif
-
-#if defined(MXC_NFC_V1)
-#define NAND_MXC_NR_BUFS 4
-#define NAND_MXC_SPARE_BUF_SIZE 16
-#define NAND_MXC_REG_OFFSET 0xe00
-#define is_mxc_nfc_11() 0
-#elif defined(MXC_NFC_V1_1)
-#define NAND_MXC_NR_BUFS 8
-#define NAND_MXC_SPARE_BUF_SIZE 64
-#define NAND_MXC_REG_OFFSET 0x1e00
-#define is_mxc_nfc_11() 1
-#else
-#error "define CONFIG_NAND_MXC_VXXX to use mtd mxc nand driver"
-#endif
-struct nfc_regs {
- uint8_t main_area[NAND_MXC_NR_BUFS][0x200];
- uint8_t spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE];
- /*
- * reserved size is offset of nfc registers
- * minus total main and spare sizes
- */
- uint8_t reserved1[NAND_MXC_REG_OFFSET
- - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)];
-#if defined(MXC_NFC_V1)
- uint16_t nfc_buf_size;
- uint16_t reserved2;
- uint16_t nfc_buf_addr;
- uint16_t nfc_flash_addr;
- uint16_t nfc_flash_cmd;
- uint16_t nfc_config;
- uint16_t nfc_ecc_status_result;
- uint16_t nfc_rsltmain_area;
- uint16_t nfc_rsltspare_area;
- uint16_t nfc_wrprot;
- uint16_t nfc_unlockstart_blkaddr;
- uint16_t nfc_unlockend_blkaddr;
- uint16_t nfc_nf_wrprst;
- uint16_t nfc_config1;
- uint16_t nfc_config2;
-#elif defined(MXC_NFC_V1_1)
- uint16_t reserved2[2];
- uint16_t nfc_buf_addr;
- uint16_t nfc_flash_addr;
- uint16_t nfc_flash_cmd;
- uint16_t nfc_config;
- uint16_t nfc_ecc_status_result;
- uint16_t nfc_ecc_status_result2;
- uint16_t nfc_spare_area_size;
- uint16_t nfc_wrprot;
- uint16_t reserved3[2];
- uint16_t nfc_nf_wrprst;
- uint16_t nfc_config1;
- uint16_t nfc_config2;
- uint16_t reserved4;
- uint16_t nfc_unlockstart_blkaddr;
- uint16_t nfc_unlockend_blkaddr;
- uint16_t nfc_unlockstart_blkaddr1;
- uint16_t nfc_unlockend_blkaddr1;
- uint16_t nfc_unlockstart_blkaddr2;
- uint16_t nfc_unlockend_blkaddr2;
- uint16_t nfc_unlockstart_blkaddr3;
- uint16_t nfc_unlockend_blkaddr3;
-#endif
-};
-
-/*
- * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register
- * for Command operation
- */
-#define NFC_CMD 0x1
-
-/*
- * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register
- * for Address operation
- */
-#define NFC_ADDR 0x2
-
-/*
- * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register
- * for Input operation
- */
-#define NFC_INPUT 0x4
-
-/*
- * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register
- * for Data Output operation
- */
-#define NFC_OUTPUT 0x8
-
-/*
- * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register
- * for Read ID operation
- */
-#define NFC_ID 0x10
-
-/*
- * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register
- * for Read Status operation
- */
-#define NFC_STATUS 0x20
-
-/*
- * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read
- * Status operation
- */
-#define NFC_INT 0x8000
-
-#ifdef MXC_NFC_V1_1
-#define NFC_4_8N_ECC (1 << 0)
-#else
-#define NFC_4_8N_ECC 0
-#endif
-#define NFC_SP_EN (1 << 2)
-#define NFC_ECC_EN (1 << 3)
-#define NFC_BIG (1 << 5)
-#define NFC_RST (1 << 6)
-#define NFC_CE (1 << 7)
-#define NFC_ONE_CYCLE (1 << 8)
-
typedef enum {false, true} bool;
struct mxc_nand_host {
- struct mtd_info mtd;
- struct nand_chip *nand;
-
- struct nfc_regs __iomem *regs;
- int spare_only;
- int status_request;
- int pagesize_2k;
- int clk_act;
- uint16_t col_addr;
- unsigned int page_addr;
+ struct mtd_info mtd;
+ struct nand_chip *nand;
+
+ struct fsl_nfc_regs __iomem *regs;
+ int spare_only;
+ int status_request;
+ int pagesize_2k;
+ int clk_act;
+ uint16_t col_addr;
+ unsigned int page_addr;
};
static struct mxc_nand_host mxc_host;
@@ -222,7 +77,7 @@ static struct nand_ecclayout nand_hw_eccoob2k = {
.oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} },
};
#endif
-#elif defined(MXC_NFC_V1_1)
+#elif defined(MXC_NFC_V2_1)
#ifndef CONFIG_SYS_NAND_LARGEPAGE
static struct nand_ecclayout nand_hw_eccoob = {
.eccbytes = 9,
@@ -268,8 +123,7 @@ static int is_16bit_nand(void)
#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
static int is_16bit_nand(void)
{
- struct ccm_regs *ccm =
- (struct ccm_regs *)IMX_CCM_BASE;
+ struct ccm_regs *ccm = (struct ccm_regs *)IMX_CCM_BASE;
if (readl(&ccm->rcsr) & CCM_RCSR_NF_16BIT_SEL)
return 1;
@@ -304,10 +158,10 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,
uint32_t tmp;
while (max_retries-- > 0) {
- if (readw(&host->regs->nfc_config2) & NFC_INT) {
- tmp = readw(&host->regs->nfc_config2);
+ if (readw(&host->regs->config2) & NFC_INT) {
+ tmp = readw(&host->regs->config2);
tmp &= ~NFC_INT;
- writew(tmp, &host->regs->nfc_config2);
+ writew(tmp, &host->regs->config2);
break;
}
udelay(1);
@@ -326,8 +180,8 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd)
{
MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd);
- writew(cmd, &host->regs->nfc_flash_cmd);
- writew(NFC_CMD, &host->regs->nfc_config2);
+ writew(cmd, &host->regs->flash_cmd);
+ writew(NFC_CMD, &host->regs->config2);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, cmd);
@@ -342,8 +196,8 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr)
{
MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr);
- writew(addr, &host->regs->nfc_flash_addr);
- writew(NFC_ADDR, &host->regs->nfc_config2);
+ writew(addr, &host->regs->flash_addr);
+ writew(NFC_ADDR, &host->regs->config2);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, addr);
@@ -359,7 +213,7 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,
if (spare_only)
MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only);
- if (is_mxc_nfc_11()) {
+ if (is_mxc_nfc_21()) {
int i;
/*
* The controller copies the 64 bytes of spare data from
@@ -375,19 +229,19 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,
}
}
- writew(buf_id, &host->regs->nfc_buf_addr);
+ writew(buf_id, &host->regs->buf_addr);
/* Configure spare or page+spare access */
if (!host->pagesize_2k) {
- uint16_t config1 = readw(&host->regs->nfc_config1);
+ uint16_t config1 = readw(&host->regs->config1);
if (spare_only)
config1 |= NFC_SP_EN;
else
- config1 &= ~(NFC_SP_EN);
- writew(config1, &host->regs->nfc_config1);
+ config1 &= ~NFC_SP_EN;
+ writew(config1, &host->regs->config1);
}
- writew(NFC_INPUT, &host->regs->nfc_config2);
+ writew(NFC_INPUT, &host->regs->config2);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, spare_only);
@@ -402,24 +256,24 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id,
{
MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only);
- writew(buf_id, &host->regs->nfc_buf_addr);
+ writew(buf_id, &host->regs->buf_addr);
/* Configure spare or page+spare access */
if (!host->pagesize_2k) {
- uint32_t config1 = readw(&host->regs->nfc_config1);
+ uint32_t config1 = readw(&host->regs->config1);
if (spare_only)
config1 |= NFC_SP_EN;
else
config1 &= ~NFC_SP_EN;
- writew(config1, &host->regs->nfc_config1);
+ writew(config1, &host->regs->config1);
}
- writew(NFC_OUTPUT, &host->regs->nfc_config2);
+ writew(NFC_OUTPUT, &host->regs->config2);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, spare_only);
- if (is_mxc_nfc_11()) {
+ if (is_mxc_nfc_21()) {
int i;
/*
@@ -442,14 +296,14 @@ static void send_read_id(struct mxc_nand_host *host)
uint16_t tmp;
/* NANDFC buffer 0 is used for device ID output */
- writew(0x0, &host->regs->nfc_buf_addr);
+ writew(0x0, &host->regs->buf_addr);
/* Read ID into main buffer */
- tmp = readw(&host->regs->nfc_config1);
+ tmp = readw(&host->regs->config1);
tmp &= ~NFC_SP_EN;
- writew(tmp, &host->regs->nfc_config1);
+ writew(tmp, &host->regs->config1);
- writew(NFC_ID, &host->regs->nfc_config2);
+ writew(NFC_ID, &host->regs->config2);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, 0);
@@ -469,14 +323,14 @@ static uint16_t get_dev_status(struct mxc_nand_host *host)
/* store the main area1 first word, later do recovery */
store = readl(main_buf);
/* NANDFC buffer 1 is used for device status */
- writew(1, &host->regs->nfc_buf_addr);
+ writew(1, &host->regs->buf_addr);
/* Read status into main buffer */
- tmp = readw(&host->regs->nfc_config1);
+ tmp = readw(&host->regs->config1);
tmp &= ~NFC_SP_EN;
- writew(tmp, &host->regs->nfc_config1);
+ writew(tmp, &host->regs->config1);
- writew(NFC_STATUS, &host->regs->nfc_config2);
+ writew(NFC_STATUS, &host->regs->config2);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, 0);
@@ -501,29 +355,29 @@ static int mxc_nand_dev_ready(struct mtd_info *mtd)
return 1;
}
-#ifdef CONFIG_MXC_NAND_HWECC
-static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- /*
- * If HW ECC is enabled, we turn it on during init. There is
- * no need to enable again here.
- */
-}
-
-#ifdef MXC_NFC_V1_1
static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on)
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- uint16_t tmp = readw(&host->regs->nfc_config1);
+ uint16_t tmp = readw(&host->regs->config1);
if (on)
tmp |= NFC_ECC_EN;
else
tmp &= ~NFC_ECC_EN;
- writew(tmp, &host->regs->nfc_config1);
+ writew(tmp, &host->regs->config1);
+}
+
+#ifdef CONFIG_MXC_NAND_HWECC
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ /*
+ * If HW ECC is enabled, we turn it on during init. There is
+ * no need to enable again here.
+ */
}
+#ifdef MXC_NFC_V2_1
static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
int page, int sndcmd)
@@ -616,7 +470,7 @@ static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,
size = mtd->oobsize - (oob - chip->oob_poi);
if (size)
chip->read_buf(mtd, oob, size);
- _mxc_nand_enable_hwecc(mtd, 0);
+ _mxc_nand_enable_hwecc(mtd, 1);
return 0;
}
@@ -799,7 +653,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- uint16_t ecc_status = readw(&host->regs->nfc_ecc_status_result);
+ uint32_t ecc_status = readl(&host->regs->ecc_status_result);
int subpages = mtd->writesize / nand_chip->subpagesize;
int pg2blk_shift = nand_chip->phys_erase_shift -
nand_chip->page_shift;
@@ -832,7 +686,6 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
#define mxc_nand_write_page_syndrome NULL
#define mxc_nand_write_page_raw_syndrome NULL
#define mxc_nand_write_oob_syndrome NULL
-#define mxc_nfc_11_nand_correct_data NULL
static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
@@ -845,7 +698,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
* additional correction. 2-Bit errors cannot be corrected by
* HW ECC, so we need to return failure
*/
- uint16_t ecc_status = readw(&host->regs->nfc_ecc_status_result);
+ uint16_t ecc_status = readw(&host->regs->ecc_status_result);
if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
MTDDEBUG(MTD_DEBUG_LEVEL0,
@@ -1208,7 +1061,7 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command,
case NAND_CMD_PAGEPROG:
send_prog_page(host, 0, host->spare_only);
- if (host->pagesize_2k && !is_mxc_nfc_11()) {
+ if (host->pagesize_2k && is_mxc_nfc_1()) {
/* data in 4 areas */
send_prog_page(host, 1, host->spare_only);
send_prog_page(host, 2, host->spare_only);
@@ -1258,7 +1111,7 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command,
send_cmd(host, NAND_CMD_READSTART);
/* read for each AREA */
send_read_page(host, 0, host->spare_only);
- if (!is_mxc_nfc_11()) {
+ if (is_mxc_nfc_1()) {
send_read_page(host, 1, host->spare_only);
send_read_page(host, 2, host->spare_only);
send_read_page(host, 3, host->spare_only);
@@ -1284,24 +1137,6 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command,
}
}
-#ifdef MXC_NFC_V1_1
-static void mxc_setup_config1(void)
-{
- uint16_t tmp;
-
- tmp = readw(&host->regs->nfc_config1);
- tmp |= NFC_ONE_CYCLE;
- tmp |= NFC_4_8N_ECC;
- writew(tmp, &host->regs->nfc_config1);
- if (host->pagesize_2k)
- writew(64/2, &host->regs->nfc_spare_area_size);
- else
- writew(16/2, &host->regs->nfc_spare_area_size);
-}
-#else
-#define mxc_setup_config1()
-#endif
-
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
@@ -1332,8 +1167,9 @@ static struct nand_bbt_descr bbt_mirror_descr = {
int board_nand_init(struct nand_chip *this)
{
struct mtd_info *mtd;
+#ifdef MXC_NFC_V2_1
uint16_t tmp;
- int err = 0;
+#endif
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
this->options |= NAND_USE_FLASH_BBT;
@@ -1359,14 +1195,14 @@ int board_nand_init(struct nand_chip *this)
this->read_buf = mxc_nand_read_buf;
this->verify_buf = mxc_nand_verify_buf;
- host->regs = (struct nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
+ host->regs = (struct fsl_nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
host->clk_act = 1;
#ifdef CONFIG_MXC_NAND_HWECC
this->ecc.calculate = mxc_nand_calculate_ecc;
this->ecc.hwctl = mxc_nand_enable_hwecc;
this->ecc.correct = mxc_nand_correct_data;
- if (is_mxc_nfc_11()) {
+ if (is_mxc_nfc_21()) {
this->ecc.mode = NAND_ECC_HW_SYNDROME;
this->ecc.read_page = mxc_nand_read_page_syndrome;
this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome;
@@ -1383,27 +1219,46 @@ int board_nand_init(struct nand_chip *this)
host->pagesize_2k = 0;
this->ecc.size = 512;
- tmp = readw(&host->regs->nfc_config1);
- tmp |= NFC_ECC_EN;
- writew(tmp, &host->regs->nfc_config1);
+ _mxc_nand_enable_hwecc(mtd, 1);
#else
this->ecc.layout = &nand_soft_eccoob;
this->ecc.mode = NAND_ECC_SOFT;
- tmp = readw(&host->regs->nfc_config1);
- tmp &= ~NFC_ECC_EN;
- writew(tmp, &host->regs->nfc_config1);
+ _mxc_nand_enable_hwecc(mtd, 0);
#endif
/* Reset NAND */
this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ /* NAND bus width determines access functions used by upper layer */
+ if (is_16bit_nand())
+ this->options |= NAND_BUSWIDTH_16;
+
+#ifdef CONFIG_SYS_NAND_LARGEPAGE
+ host->pagesize_2k = 1;
+ this->ecc.layout = &nand_hw_eccoob2k;
+#else
+ host->pagesize_2k = 0;
+ this->ecc.layout = &nand_hw_eccoob;
+#endif
+
+#ifdef MXC_NFC_V2_1
+ tmp = readw(&host->regs->config1);
+ tmp |= NFC_ONE_CYCLE;
+ tmp |= NFC_4_8N_ECC;
+ writew(tmp, &host->regs->config1);
+ if (host->pagesize_2k)
+ writew(64/2, &host->regs->spare_area_size);
+ else
+ writew(16/2, &host->regs->spare_area_size);
+#endif
+
/*
* preset operation
* Unlock the internal RAM Buffer
*/
- writew(0x2, &host->regs->nfc_config);
+ writew(0x2, &host->regs->config);
/* Blocks to be unlocked */
- writew(0x0, &host->regs->nfc_unlockstart_blkaddr);
+ writew(0x0, &host->regs->unlockstart_blkaddr);
/* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the
* unlockend_blkaddr, but the magic 0x4000 does not always work
* when writing more than some 32 megabytes (on 2k page nands)
@@ -1415,22 +1270,10 @@ int board_nand_init(struct nand_chip *this)
* This might be NAND chip specific and the i.MX31 datasheet is
* extremely vague about the semantics of this register.
*/
- writew(0xFFFF, &host->regs->nfc_unlockend_blkaddr);
+ writew(0xFFFF, &host->regs->unlockend_blkaddr);
/* Unlock Block Command for given address range */
- writew(0x4, &host->regs->nfc_wrprot);
+ writew(0x4, &host->regs->wrprot);
- /* NAND bus width determines access functions used by upper layer */
- if (is_16bit_nand())
- this->options |= NAND_BUSWIDTH_16;
-
-#ifdef CONFIG_SYS_NAND_LARGEPAGE
- host->pagesize_2k = 1;
- this->ecc.layout = &nand_hw_eccoob2k;
-#else
- host->pagesize_2k = 0;
- this->ecc.layout = &nand_hw_eccoob;
-#endif
- mxc_setup_config1();
- return err;
+ return 0;
}
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index 9c95811054..4701be846c 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -25,10 +25,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <common.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/types.h>
-#include <common.h>
#include <malloc.h>
#include <asm/errno.h>
#include <asm/io.h>
@@ -233,11 +233,11 @@ static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
*/
static int mxs_nand_wait_for_bch_complete(void)
{
- struct mx28_bch_regs *bch_regs = (struct mx28_bch_regs *)MXS_BCH_BASE;
+ struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
int timeout = MXS_NAND_BCH_TIMEOUT;
int ret;
- ret = mx28_wait_mask_set(&bch_regs->hw_bch_ctrl_reg,
+ ret = mxs_wait_mask_set(&bch_regs->hw_bch_ctrl_reg,
BCH_CTRL_COMPLETE_IRQ, timeout);
writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr);
@@ -338,8 +338,8 @@ static int mxs_nand_device_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
struct mxs_nand_info *nand_info = chip->priv;
- struct mx28_gpmi_regs *gpmi_regs =
- (struct mx28_gpmi_regs *)MXS_GPMI_BASE;
+ struct mxs_gpmi_regs *gpmi_regs =
+ (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
uint32_t tmp;
tmp = readl(&gpmi_regs->hw_gpmi_stat);
@@ -968,11 +968,11 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv;
- struct mx28_bch_regs *bch_regs = (struct mx28_bch_regs *)MXS_BCH_BASE;
+ struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
uint32_t tmp;
/* Configure BCH and set NFC geometry */
- mx28_reset_block(&bch_regs->hw_bch_ctrl_reg);
+ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);
/* Configure layout 0 */
tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
@@ -1056,8 +1056,8 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info)
*/
int mxs_nand_init(struct mxs_nand_info *info)
{
- struct mx28_gpmi_regs *gpmi_regs =
- (struct mx28_gpmi_regs *)MXS_GPMI_BASE;
+ struct mxs_gpmi_regs *gpmi_regs =
+ (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
int i = 0, j;
info->desc = malloc(sizeof(struct mxs_dma_desc *) *
@@ -1080,7 +1080,7 @@ int mxs_nand_init(struct mxs_nand_info *info)
}
/* Reset the GPMI block. */
- mx28_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg);
+ mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg);
/*
* Choose NAND mode, set IRQ polarity, disable write protection and
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index bfd668fa0a..71f5027889 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2573,14 +2573,13 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
mtd->writesize = le32_to_cpu(p->byte_per_page);
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
- chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+ chip->chipsize = le32_to_cpu(p->blocks_per_lun);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
*busw = 0;
if (le16_to_cpu(p->features) & 1)
*busw = NAND_BUSWIDTH_16;
- chip->options &= ~NAND_CHIPOPTIONS_MSK;
- chip->options |= (NAND_NO_READRDY |
- NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+ chip->options |= NAND_NO_READRDY | NAND_NO_AUTOINCR;
return 1;
}
@@ -2752,8 +2751,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
}
}
/* Get chip options, preserve non chip based options */
- chip->options &= ~NAND_CHIPOPTIONS_MSK;
- chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+ chip->options |= type->options;
/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
@@ -2936,7 +2934,8 @@ int nand_scan_tail(struct mtd_info *mtd)
struct nand_chip *chip = mtd->priv;
if (!(chip->options & NAND_OWN_BUFFERS))
- chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
+ chip->buffers = memalign(ARCH_DMA_MINALIGN,
+ sizeof(*chip->buffers));
if (!chip->buffers)
return -ENOMEM;
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 7ed8b1891c..c4752a7cbf 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -207,12 +207,6 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
* Support for locking / unlocking operations of some NAND devices
*****************************************************************************/
-#define NAND_CMD_LOCK 0x2a
-#define NAND_CMD_LOCK_TIGHT 0x2c
-#define NAND_CMD_UNLOCK1 0x23
-#define NAND_CMD_UNLOCK2 0x24
-#define NAND_CMD_LOCK_STATUS 0x7a
-
/**
* nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT
* state
@@ -271,7 +265,6 @@ int nand_lock(struct mtd_info *mtd, int tight)
* >0 lock status:
* bitfield with the following combinations:
* NAND_LOCK_STATUS_TIGHT: page in tight state
- * NAND_LOCK_STATUS_LOCK: page locked
* NAND_LOCK_STATUS_UNLOCK: page unlocked
*
*/
@@ -300,7 +293,6 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT
- | NAND_LOCK_STATUS_LOCK
| NAND_LOCK_STATUS_UNLOCK);
out:
@@ -317,18 +309,21 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
* @param start start byte address
* @param length number of bytes to unlock (must be a multiple of
* page size nand->writesize)
+ * @param allexcept if set, unlock everything not selected
*
* @return 0 on success, -1 in case of error
*/
-int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
+int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
+ int allexcept)
{
int ret = 0;
int chipnr;
int status;
int page;
struct nand_chip *chip = mtd->priv;
- printf ("nand_unlock: start: %08x, length: %d!\n",
- (int)start, (int)length);
+
+ debug("nand_unlock%s: start: %08llx, length: %d!\n",
+ allexcept ? " (allexcept)" : "", start, length);
/* select the NAND device */
chipnr = (int)(start >> chip->chip_shift);
@@ -368,6 +363,15 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
/* submit ADDRESS of LAST page to unlock */
page += (int)(length >> chip->page_shift);
+
+ /*
+ * Page addresses for unlocking are supposed to be block-aligned.
+ * At least some NAND chips use the low bit to indicate that the
+ * page range should be inverted.
+ */
+ if (allexcept)
+ page |= 1;
+
chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask);
/* call wait ready function */
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index ca868efb9f..f1469d1105 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -283,6 +283,7 @@ void omap_nand_switch_ecc(int32_t hardware)
nand->ecc.mode = NAND_ECC_SOFT;
/* Use mtd default settings */
nand->ecc.layout = NULL;
+ nand->ecc.size = 0;
printf("SW ECC selected\n");
}
diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c
new file mode 100644
index 0000000000..8c1de34455
--- /dev/null
+++ b/drivers/mtd/nand/tegra_nand.c
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ * (C) Copyright 2006 Detlev Zundel, dzu@denx.de
+ * (C) Copyright 2006 DENX Software Engineering
+ *
+ * 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/io.h>
+#include <nand.h>
+#include <asm/arch/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/funcmux.h>
+#include <asm/arch/gpio.h>
+#include <asm/errno.h>
+#include <asm-generic/gpio.h>
+#include <fdtdec.h>
+#include "tegra_nand.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NAND_CMD_TIMEOUT_MS 10
+
+#define SKIPPED_SPARE_BYTES 4
+
+/* ECC bytes to be generated for tag data */
+#define TAG_ECC_BYTES 4
+
+/* 64 byte oob block info for large page (== 2KB) device
+ *
+ * OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC:
+ * Skipped bytes(4)
+ * Main area Ecc(36)
+ * Tag data(20)
+ * Tag data Ecc(4)
+ *
+ * Yaffs2 will use 16 tag bytes.
+ */
+static struct nand_ecclayout eccoob = {
+ .eccbytes = 36,
+ .eccpos = {
+ 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ },
+ .oobavail = 20,
+ .oobfree = {
+ {
+ .offset = 40,
+ .length = 20,
+ },
+ }
+};
+
+enum {
+ ECC_OK,
+ ECC_TAG_ERROR = 1 << 0,
+ ECC_DATA_ERROR = 1 << 1
+};
+
+/* Timing parameters */
+enum {
+ FDT_NAND_MAX_TRP_TREA,
+ FDT_NAND_TWB,
+ FDT_NAND_MAX_TCR_TAR_TRR,
+ FDT_NAND_TWHR,
+ FDT_NAND_MAX_TCS_TCH_TALS_TALH,
+ FDT_NAND_TWH,
+ FDT_NAND_TWP,
+ FDT_NAND_TRH,
+ FDT_NAND_TADL,
+
+ FDT_NAND_TIMING_COUNT
+};
+
+/* Information about an attached NAND chip */
+struct fdt_nand {
+ struct nand_ctlr *reg;
+ int enabled; /* 1 to enable, 0 to disable */
+ struct fdt_gpio_state wp_gpio; /* write-protect GPIO */
+ s32 width; /* bit width, normally 8 */
+ u32 timing[FDT_NAND_TIMING_COUNT];
+};
+
+struct nand_drv {
+ struct nand_ctlr *reg;
+
+ /*
+ * When running in PIO mode to get READ ID bytes from register
+ * RESP_0, we need this variable as an index to know which byte in
+ * register RESP_0 should be read.
+ * Because common code in nand_base.c invokes read_byte function two
+ * times for NAND_CMD_READID.
+ * And our controller returns 4 bytes at once in register RESP_0.
+ */
+ int pio_byte_index;
+ struct fdt_nand config;
+};
+
+static struct nand_drv nand_ctrl;
+static struct mtd_info *our_mtd;
+static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
+
+#ifdef CONFIG_SYS_DCACHE_OFF
+static inline void dma_prepare(void *start, unsigned long length,
+ int is_writing)
+{
+}
+#else
+/**
+ * Prepare for a DMA transaction
+ *
+ * For a write we flush out our data. For a read we invalidate, since we
+ * need to do this before we read from the buffer after the DMA has
+ * completed, so may as well do it now.
+ *
+ * @param start Start address for DMA buffer (should be cache-aligned)
+ * @param length Length of DMA buffer in bytes
+ * @param is_writing 0 if reading, non-zero if writing
+ */
+static void dma_prepare(void *start, unsigned long length, int is_writing)
+{
+ unsigned long addr = (unsigned long)start;
+
+ length = ALIGN(length, ARCH_DMA_MINALIGN);
+ if (is_writing)
+ flush_dcache_range(addr, addr + length);
+ else
+ invalidate_dcache_range(addr, addr + length);
+}
+#endif
+
+/**
+ * Wait for command completion
+ *
+ * @param reg nand_ctlr structure
+ * @return
+ * 1 - Command completed
+ * 0 - Timeout
+ */
+static int nand_waitfor_cmd_completion(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+ int running;
+ int i;
+
+ for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) {
+ if ((readl(&reg->command) & CMD_GO) ||
+ !(readl(&reg->status) & STATUS_RBSY0) ||
+ !(readl(&reg->isr) & ISR_IS_CMD_DONE)) {
+ udelay(1);
+ continue;
+ }
+ reg_val = readl(&reg->dma_mst_ctrl);
+ /*
+ * If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE
+ * is set, that means DMA engine is running.
+ *
+ * Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE
+ * is cleared, indicating DMA transfer completion.
+ */
+ running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE |
+ DMA_MST_CTRL_EN_B_ENABLE);
+ if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE))
+ return 1;
+ udelay(1);
+ }
+ return 0;
+}
+
+/**
+ * Read one byte from the chip
+ *
+ * @param mtd MTD device structure
+ * @return data byte
+ *
+ * Read function for 8bit bus-width
+ */
+static uint8_t read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ u32 dword_read;
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)chip->priv;
+
+ /* In PIO mode, only 4 bytes can be transferred with single CMD_GO. */
+ if (info->pio_byte_index > 3) {
+ info->pio_byte_index = 0;
+ writel(CMD_GO | CMD_PIO
+ | CMD_RX | CMD_CE0,
+ &info->reg->command);
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command timeout\n");
+ }
+
+ dword_read = readl(&info->reg->resp);
+ dword_read = dword_read >> (8 * info->pio_byte_index);
+ info->pio_byte_index++;
+ return (uint8_t)dword_read;
+}
+
+/**
+ * Check NAND status to see if it is ready or not
+ *
+ * @param mtd MTD device structure
+ * @return
+ * 1 - ready
+ * 0 - not ready
+ */
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ int reg_val;
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)chip->priv;
+
+ reg_val = readl(&info->reg->status);
+ if (reg_val & STATUS_RBSY0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Dummy implementation: we don't support multiple chips */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ switch (chipnr) {
+ case -1:
+ case 0:
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+/**
+ * Clear all interrupt status bits
+ *
+ * @param reg nand_ctlr structure
+ */
+static void nand_clear_interrupt_status(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+
+ /* Clear interrupt status */
+ reg_val = readl(&reg->isr);
+ writel(reg_val, &reg->isr);
+}
+
+/**
+ * Send command to NAND device
+ *
+ * @param mtd MTD device structure
+ * @param command the command to be sent
+ * @param column the column address for this command, -1 if none
+ * @param page_addr the page address for this command, -1 if none
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)chip->priv;
+
+ /*
+ * Write out the command to the device.
+ *
+ * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+ * here before mtd->writesize is initialized.
+ */
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ assert(mtd->writesize != 0);
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Adjust columns for 16 bit bus-width */
+ if (column != -1 && (chip->options & NAND_BUSWIDTH_16))
+ column >>= 1;
+
+ nand_clear_interrupt_status(info->reg);
+
+ /* Stop DMA engine, clear DMA completion status */
+ writel(DMA_MST_CTRL_EN_A_DISABLE
+ | DMA_MST_CTRL_EN_B_DISABLE
+ | DMA_MST_CTRL_IS_DMA_DONE,
+ &info->reg->dma_mst_ctrl);
+
+ /*
+ * Program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+ case NAND_CMD_READID:
+ writel(NAND_CMD_READID, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_PIO
+ | CMD_RX |
+ ((4 - 1) << CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0,
+ &info->reg->command);
+ info->pio_byte_index = 0;
+ break;
+ case NAND_CMD_READ0:
+ writel(NAND_CMD_READ0, &info->reg->cmd_reg1);
+ writel(NAND_CMD_READSTART, &info->reg->cmd_reg2);
+ writel((page_addr << 16) | (column & 0xFFFF),
+ &info->reg->addr_reg1);
+ writel(page_addr >> 16, &info->reg->addr_reg2);
+ return;
+ case NAND_CMD_SEQIN:
+ writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1);
+ writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2);
+ writel((page_addr << 16) | (column & 0xFFFF),
+ &info->reg->addr_reg1);
+ writel(page_addr >> 16,
+ &info->reg->addr_reg2);
+ return;
+ case NAND_CMD_PAGEPROG:
+ return;
+ case NAND_CMD_ERASE1:
+ writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1);
+ writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2);
+ writel(page_addr, &info->reg->addr_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE |
+ CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3,
+ &info->reg->command);
+ break;
+ case NAND_CMD_ERASE2:
+ return;
+ case NAND_CMD_STATUS:
+ writel(NAND_CMD_STATUS, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX
+ | ((1 - 0) << CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0,
+ &info->reg->command);
+ info->pio_byte_index = 0;
+ break;
+ case NAND_CMD_RESET:
+ writel(NAND_CMD_RESET, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_CE0,
+ &info->reg->command);
+ break;
+ case NAND_CMD_RNDOUT:
+ default:
+ printf("%s: Unsupported command %d\n", __func__, command);
+ return;
+ }
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command 0x%02X timeout\n", command);
+}
+
+/**
+ * Check whether the pointed buffer are all 0xff (blank).
+ *
+ * @param buf data buffer for blank check
+ * @param len length of the buffer in byte
+ * @return
+ * 1 - blank
+ * 0 - non-blank
+ */
+static int blank_check(u8 *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xFF)
+ return 0;
+ return 1;
+}
+
+/**
+ * After a DMA transfer for read, we call this function to see whether there
+ * is any uncorrectable error on the pointed data buffer or oob buffer.
+ *
+ * @param reg nand_ctlr structure
+ * @param databuf data buffer
+ * @param a_len data buffer length
+ * @param oobbuf oob buffer
+ * @param b_len oob buffer length
+ * @return
+ * ECC_OK - no ECC error or correctable ECC error
+ * ECC_TAG_ERROR - uncorrectable tag ECC error
+ * ECC_DATA_ERROR - uncorrectable data ECC error
+ * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error
+ */
+static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf,
+ int a_len, u8 *oobbuf, int b_len)
+{
+ int return_val = ECC_OK;
+ u32 reg_val;
+
+ if (!(readl(&reg->isr) & ISR_IS_ECC_ERR))
+ return ECC_OK;
+
+ /*
+ * Area A is used for the data block (databuf). Area B is used for
+ * the spare block (oobbuf)
+ */
+ reg_val = readl(&reg->dec_status);
+ if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) {
+ reg_val = readl(&reg->bch_dec_status_buf);
+ /*
+ * If uncorrectable error occurs on data area, then see whether
+ * they are all FF. If all are FF, it's a blank page.
+ * Not error.
+ */
+ if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) &&
+ !blank_check(databuf, a_len))
+ return_val |= ECC_DATA_ERROR;
+ }
+
+ if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) {
+ reg_val = readl(&reg->bch_dec_status_buf);
+ /*
+ * If uncorrectable error occurs on tag area, then see whether
+ * they are all FF. If all are FF, it's a blank page.
+ * Not error.
+ */
+ if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) &&
+ !blank_check(oobbuf, b_len))
+ return_val |= ECC_TAG_ERROR;
+ }
+
+ return return_val;
+}
+
+/**
+ * Set GO bit to send command to device
+ *
+ * @param reg nand_ctlr structure
+ */
+static void start_command(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+
+ reg_val = readl(&reg->command);
+ reg_val |= CMD_GO;
+ writel(reg_val, &reg->command);
+}
+
+/**
+ * Clear command GO bit, DMA GO bit, and DMA completion status
+ *
+ * @param reg nand_ctlr structure
+ */
+static void stop_command(struct nand_ctlr *reg)
+{
+ /* Stop command */
+ writel(0, &reg->command);
+
+ /* Stop DMA engine and clear DMA completion status */
+ writel(DMA_MST_CTRL_GO_DISABLE
+ | DMA_MST_CTRL_IS_DMA_DONE,
+ &reg->dma_mst_ctrl);
+}
+
+/**
+ * Set up NAND bus width and page size
+ *
+ * @param info nand_info structure
+ * @param *reg_val address of reg_val
+ * @return 0 if ok, -1 on error
+ */
+static int set_bus_width_page_size(struct fdt_nand *config,
+ u32 *reg_val)
+{
+ if (config->width == 8)
+ *reg_val = CFG_BUS_WIDTH_8BIT;
+ else if (config->width == 16)
+ *reg_val = CFG_BUS_WIDTH_16BIT;
+ else {
+ debug("%s: Unsupported bus width %d\n", __func__,
+ config->width);
+ return -1;
+ }
+
+ if (our_mtd->writesize == 512)
+ *reg_val |= CFG_PAGE_SIZE_512;
+ else if (our_mtd->writesize == 2048)
+ *reg_val |= CFG_PAGE_SIZE_2048;
+ else if (our_mtd->writesize == 4096)
+ *reg_val |= CFG_PAGE_SIZE_4096;
+ else {
+ debug("%s: Unsupported page size %d\n", __func__,
+ our_mtd->writesize);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Page read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @param page page number
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -EIO when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int page, int with_ecc, int is_writing)
+{
+ u32 reg_val;
+ int tag_size;
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ /* 4*128=512 (byte) is the value that our HW can support. */
+ ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128);
+ char *tag_ptr;
+ struct nand_drv *info;
+ struct fdt_nand *config;
+
+ if ((uintptr_t)buf & 0x03) {
+ printf("buf %p has to be 4-byte aligned\n", buf);
+ return -EINVAL;
+ }
+
+ info = (struct nand_drv *)chip->priv;
+ config = &info->config;
+ if (set_bus_width_page_size(config, &reg_val))
+ return -EINVAL;
+
+ /* Need to be 4-byte aligned */
+ tag_ptr = (char *)tag_buf;
+
+ stop_command(info->reg);
+
+ writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
+ writel(virt_to_phys(buf), &info->reg->data_block_ptr);
+
+ if (with_ecc) {
+ writel(virt_to_phys(tag_ptr), &info->reg->tag_ptr);
+ if (is_writing)
+ memcpy(tag_ptr, chip->oob_poi + free->offset,
+ chip->ecc.layout->oobavail +
+ TAG_ECC_BYTES);
+ } else {
+ writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr);
+ }
+
+ /* Set ECC selection, configure ECC settings */
+ if (with_ecc) {
+ tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES;
+ reg_val |= (CFG_SKIP_SPARE_SEL_4
+ | CFG_SKIP_SPARE_ENABLE
+ | CFG_HW_ECC_CORRECTION_ENABLE
+ | CFG_ECC_EN_TAG_DISABLE
+ | CFG_HW_ECC_SEL_RS
+ | CFG_HW_ECC_ENABLE
+ | CFG_TVAL4
+ | (tag_size - 1));
+
+ if (!is_writing)
+ tag_size += SKIPPED_SPARE_BYTES;
+ dma_prepare(tag_ptr, tag_size, is_writing);
+ } else {
+ tag_size = mtd->oobsize;
+ reg_val |= (CFG_SKIP_SPARE_DISABLE
+ | CFG_HW_ECC_CORRECTION_DISABLE
+ | CFG_ECC_EN_TAG_DISABLE
+ | CFG_HW_ECC_DISABLE
+ | (tag_size - 1));
+ dma_prepare(chip->oob_poi, tag_size, is_writing);
+ }
+ writel(reg_val, &info->reg->config);
+
+ dma_prepare(buf, 1 << chip->page_shift, is_writing);
+
+ writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+ writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+ nand_clear_interrupt_status(info->reg);
+
+ reg_val = CMD_CLE | CMD_ALE
+ | CMD_SEC_CMD
+ | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+ | CMD_A_VALID
+ | CMD_B_VALID
+ | (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0;
+ if (!is_writing)
+ reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+ else
+ reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+ writel(reg_val, &info->reg->command);
+
+ /* Setup DMA engine */
+ reg_val = DMA_MST_CTRL_GO_ENABLE
+ | DMA_MST_CTRL_BURST_8WORDS
+ | DMA_MST_CTRL_EN_A_ENABLE
+ | DMA_MST_CTRL_EN_B_ENABLE;
+
+ if (!is_writing)
+ reg_val |= DMA_MST_CTRL_DIR_READ;
+ else
+ reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+ writel(reg_val, &info->reg->dma_mst_ctrl);
+
+ start_command(info->reg);
+
+ if (!nand_waitfor_cmd_completion(info->reg)) {
+ if (!is_writing)
+ printf("Read Page 0x%X timeout ", page);
+ else
+ printf("Write Page 0x%X timeout ", page);
+ if (with_ecc)
+ printf("with ECC");
+ else
+ printf("without ECC");
+ printf("\n");
+ return -EIO;
+ }
+
+ if (with_ecc && !is_writing) {
+ memcpy(chip->oob_poi, tag_ptr,
+ SKIPPED_SPARE_BYTES);
+ memcpy(chip->oob_poi + free->offset,
+ tag_ptr + SKIPPED_SPARE_BYTES,
+ chip->ecc.layout->oobavail);
+ reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf,
+ 1 << chip->page_shift,
+ (u8 *)(tag_ptr + SKIPPED_SPARE_BYTES),
+ chip->ecc.layout->oobavail);
+ if (reg_val & ECC_TAG_ERROR)
+ printf("Read Page 0x%X tag ECC error\n", page);
+ if (reg_val & ECC_DATA_ERROR)
+ printf("Read Page 0x%X data ECC error\n",
+ page);
+ if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR))
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * Hardware ecc based page read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -EIO when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ return nand_rw_page(mtd, chip, buf, page, 1, 0);
+}
+
+/**
+ * Hardware ecc based page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ */
+static void nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ int page;
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)chip->priv;
+
+ page = (readl(&info->reg->addr_reg1) >> 16) |
+ (readl(&info->reg->addr_reg2) << 16);
+
+ nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
+}
+
+
+/**
+ * Read raw page data without ecc
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ return nand_rw_page(mtd, chip, buf, page, 0, 0);
+}
+
+/**
+ * Raw page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ */
+static void nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ int page;
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)chip->priv;
+ page = (readl(&info->reg->addr_reg1) >> 16) |
+ (readl(&info->reg->addr_reg2) << 16);
+
+ nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1);
+}
+
+/**
+ * OOB data read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int with_ecc, int is_writing)
+{
+ u32 reg_val;
+ int tag_size;
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ struct nand_drv *info;
+
+ if (((int)chip->oob_poi) & 0x03)
+ return -EINVAL;
+ info = (struct nand_drv *)chip->priv;
+ if (set_bus_width_page_size(&info->config, &reg_val))
+ return -EINVAL;
+
+ stop_command(info->reg);
+
+ writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr);
+
+ /* Set ECC selection */
+ tag_size = mtd->oobsize;
+ if (with_ecc)
+ reg_val |= CFG_ECC_EN_TAG_ENABLE;
+ else
+ reg_val |= (CFG_ECC_EN_TAG_DISABLE);
+
+ reg_val |= ((tag_size - 1) |
+ CFG_SKIP_SPARE_DISABLE |
+ CFG_HW_ECC_CORRECTION_DISABLE |
+ CFG_HW_ECC_DISABLE);
+ writel(reg_val, &info->reg->config);
+
+ dma_prepare(chip->oob_poi, tag_size, is_writing);
+
+ writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+ if (is_writing && with_ecc)
+ tag_size -= TAG_ECC_BYTES;
+
+ writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+ nand_clear_interrupt_status(info->reg);
+
+ reg_val = CMD_CLE | CMD_ALE
+ | CMD_SEC_CMD
+ | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+ | CMD_B_VALID
+ | CMD_CE0;
+ if (!is_writing)
+ reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+ else
+ reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+ writel(reg_val, &info->reg->command);
+
+ /* Setup DMA engine */
+ reg_val = DMA_MST_CTRL_GO_ENABLE
+ | DMA_MST_CTRL_BURST_8WORDS
+ | DMA_MST_CTRL_EN_B_ENABLE;
+ if (!is_writing)
+ reg_val |= DMA_MST_CTRL_DIR_READ;
+ else
+ reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+ writel(reg_val, &info->reg->dma_mst_ctrl);
+
+ start_command(info->reg);
+
+ if (!nand_waitfor_cmd_completion(info->reg)) {
+ if (!is_writing)
+ printf("Read OOB of Page 0x%X timeout\n", page);
+ else
+ printf("Write OOB of Page 0x%X timeout\n", page);
+ return -EIO;
+ }
+
+ if (with_ecc && !is_writing) {
+ reg_val = (u32)check_ecc_error(info->reg, 0, 0,
+ (u8 *)(chip->oob_poi + free->offset),
+ chip->ecc.layout->oobavail);
+ if (reg_val & ECC_TAG_ERROR)
+ printf("Read OOB of Page 0x%X tag ECC error\n", page);
+ }
+ return 0;
+}
+
+/**
+ * OOB data read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ * @param sndcmd flag whether to issue read command or not
+ * @return 1 - issue read command next time
+ * 0 - not to issue
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ if (sndcmd) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ sndcmd = 0;
+ }
+ nand_rw_oob(mtd, chip, page, 0, 0);
+ return sndcmd;
+}
+
+/**
+ * OOB data write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to write
+ * @return 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+
+ return nand_rw_oob(mtd, chip, page, 0, 1);
+}
+
+/**
+ * Set up NAND memory timings according to the provided parameters
+ *
+ * @param timing Timing parameters
+ * @param reg NAND controller register address
+ */
+static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT],
+ struct nand_ctlr *reg)
+{
+ u32 reg_val, clk_rate, clk_period, time_val;
+
+ clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH,
+ CLOCK_ID_PERIPH) / 1000000;
+ clk_period = 1000 / clk_rate;
+ reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+ TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWB] / clk_period) <<
+ TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK;
+ time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period;
+ if (time_val > 2)
+ reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) &
+ TIMING_TCR_TAR_TRR_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) <<
+ TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK;
+ time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period;
+ if (time_val > 1)
+ reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) &
+ TIMING_TCS_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWH] / clk_period) <<
+ TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWP] / clk_period) <<
+ TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TRH] / clk_period) <<
+ TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+ TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK;
+ writel(reg_val, &reg->timing);
+
+ reg_val = 0;
+ time_val = timing[FDT_NAND_TADL] / clk_period;
+ if (time_val > 2)
+ reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK;
+ writel(reg_val, &reg->timing2);
+}
+
+/**
+ * Decode NAND parameters from the device tree
+ *
+ * @param blob Device tree blob
+ * @param node Node containing "nand-flash" compatble node
+ * @return 0 if ok, -ve on error (FDT_ERR_...)
+ */
+static int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config)
+{
+ int err;
+
+ config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg");
+ config->enabled = fdtdec_get_is_enabled(blob, node);
+ config->width = fdtdec_get_int(blob, node, "nvidia,nand-width", 8);
+ err = fdtdec_decode_gpio(blob, node, "nvidia,wp-gpios",
+ &config->wp_gpio);
+ if (err)
+ return err;
+ err = fdtdec_get_int_array(blob, node, "nvidia,timing",
+ config->timing, FDT_NAND_TIMING_COUNT);
+ if (err < 0)
+ return err;
+
+ /* Now look up the controller and decode that */
+ node = fdt_next_node(blob, node, NULL);
+ if (node < 0)
+ return node;
+
+ return 0;
+}
+
+/**
+ * Board-specific NAND initialization
+ *
+ * @param nand nand chip info structure
+ * @return 0, after initialized, -1 on error
+ */
+int tegra_nand_init(struct nand_chip *nand, int devnum)
+{
+ struct nand_drv *info = &nand_ctrl;
+ struct fdt_nand *config = &info->config;
+ int node, ret;
+
+ node = fdtdec_next_compatible(gd->fdt_blob, 0,
+ COMPAT_NVIDIA_TEGRA20_NAND);
+ if (node < 0)
+ return -1;
+ if (fdt_decode_nand(gd->fdt_blob, node, config)) {
+ printf("Could not decode nand-flash in device tree\n");
+ return -1;
+ }
+ if (!config->enabled)
+ return -1;
+ info->reg = config->reg;
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &eccoob;
+
+ nand->options = LP_OPTIONS;
+ nand->cmdfunc = nand_command;
+ nand->read_byte = read_byte;
+ nand->ecc.read_page = nand_read_page_hwecc;
+ nand->ecc.write_page = nand_write_page_hwecc;
+ nand->ecc.read_page_raw = nand_read_page_raw;
+ nand->ecc.write_page_raw = nand_write_page_raw;
+ nand->ecc.read_oob = nand_read_oob;
+ nand->ecc.write_oob = nand_write_oob;
+ nand->select_chip = nand_select_chip;
+ nand->dev_ready = nand_dev_ready;
+ nand->priv = &nand_ctrl;
+
+ /* Adjust controller clock rate */
+ clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000);
+
+ /* Adjust timing for NAND device */
+ setup_timing(config->timing, info->reg);
+
+ funcmux_select(PERIPH_ID_NDFLASH, FUNCMUX_DEFAULT);
+ fdtdec_setup_gpio(&config->wp_gpio);
+ gpio_direction_output(config->wp_gpio.gpio, 1);
+
+ our_mtd = &nand_info[devnum];
+ our_mtd->priv = nand;
+ ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+ if (ret)
+ return ret;
+
+ nand->ecc.size = our_mtd->writesize;
+ nand->ecc.bytes = our_mtd->oobsize;
+
+ ret = nand_scan_tail(our_mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(devnum);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void board_nand_init(void)
+{
+ struct nand_chip *nand = &nand_chip[0];
+
+ if (tegra_nand_init(nand, 0))
+ puts("Tegra NAND init failed\n");
+}
diff --git a/drivers/mtd/nand/tegra_nand.h b/drivers/mtd/nand/tegra_nand.h
new file mode 100644
index 0000000000..7e74be75f8
--- /dev/null
+++ b/drivers/mtd/nand/tegra_nand.h
@@ -0,0 +1,257 @@
+/*
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ *
+ * 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
+ */
+
+/* register offset */
+#define COMMAND_0 0x00
+#define CMD_GO (1 << 31)
+#define CMD_CLE (1 << 30)
+#define CMD_ALE (1 << 29)
+#define CMD_PIO (1 << 28)
+#define CMD_TX (1 << 27)
+#define CMD_RX (1 << 26)
+#define CMD_SEC_CMD (1 << 25)
+#define CMD_AFT_DAT_MASK (1 << 24)
+#define CMD_AFT_DAT_DISABLE 0
+#define CMD_AFT_DAT_ENABLE (1 << 24)
+#define CMD_TRANS_SIZE_SHIFT 20
+#define CMD_TRANS_SIZE_PAGE 8
+#define CMD_A_VALID (1 << 19)
+#define CMD_B_VALID (1 << 18)
+#define CMD_RD_STATUS_CHK (1 << 17)
+#define CMD_R_BSY_CHK (1 << 16)
+#define CMD_CE7 (1 << 15)
+#define CMD_CE6 (1 << 14)
+#define CMD_CE5 (1 << 13)
+#define CMD_CE4 (1 << 12)
+#define CMD_CE3 (1 << 11)
+#define CMD_CE2 (1 << 10)
+#define CMD_CE1 (1 << 9)
+#define CMD_CE0 (1 << 8)
+#define CMD_CLE_BYTE_SIZE_SHIFT 4
+enum {
+ CMD_CLE_BYTES1 = 0,
+ CMD_CLE_BYTES2,
+ CMD_CLE_BYTES3,
+ CMD_CLE_BYTES4,
+};
+#define CMD_ALE_BYTE_SIZE_SHIFT 0
+enum {
+ CMD_ALE_BYTES1 = 0,
+ CMD_ALE_BYTES2,
+ CMD_ALE_BYTES3,
+ CMD_ALE_BYTES4,
+ CMD_ALE_BYTES5,
+ CMD_ALE_BYTES6,
+ CMD_ALE_BYTES7,
+ CMD_ALE_BYTES8
+};
+
+#define STATUS_0 0x04
+#define STATUS_RBSY0 (1 << 8)
+
+#define ISR_0 0x08
+#define ISR_IS_CMD_DONE (1 << 5)
+#define ISR_IS_ECC_ERR (1 << 4)
+
+#define IER_0 0x0C
+
+#define CFG_0 0x10
+#define CFG_HW_ECC_MASK (1 << 31)
+#define CFG_HW_ECC_DISABLE 0
+#define CFG_HW_ECC_ENABLE (1 << 31)
+#define CFG_HW_ECC_SEL_MASK (1 << 30)
+#define CFG_HW_ECC_SEL_HAMMING 0
+#define CFG_HW_ECC_SEL_RS (1 << 30)
+#define CFG_HW_ECC_CORRECTION_MASK (1 << 29)
+#define CFG_HW_ECC_CORRECTION_DISABLE 0
+#define CFG_HW_ECC_CORRECTION_ENABLE (1 << 29)
+#define CFG_PIPELINE_EN_MASK (1 << 28)
+#define CFG_PIPELINE_EN_DISABLE 0
+#define CFG_PIPELINE_EN_ENABLE (1 << 28)
+#define CFG_ECC_EN_TAG_MASK (1 << 27)
+#define CFG_ECC_EN_TAG_DISABLE 0
+#define CFG_ECC_EN_TAG_ENABLE (1 << 27)
+#define CFG_TVALUE_MASK (3 << 24)
+enum {
+ CFG_TVAL4 = 0 << 24,
+ CFG_TVAL6 = 1 << 24,
+ CFG_TVAL8 = 2 << 24
+};
+#define CFG_SKIP_SPARE_MASK (1 << 23)
+#define CFG_SKIP_SPARE_DISABLE 0
+#define CFG_SKIP_SPARE_ENABLE (1 << 23)
+#define CFG_COM_BSY_MASK (1 << 22)
+#define CFG_COM_BSY_DISABLE 0
+#define CFG_COM_BSY_ENABLE (1 << 22)
+#define CFG_BUS_WIDTH_MASK (1 << 21)
+#define CFG_BUS_WIDTH_8BIT 0
+#define CFG_BUS_WIDTH_16BIT (1 << 21)
+#define CFG_LPDDR1_MODE_MASK (1 << 20)
+#define CFG_LPDDR1_MODE_DISABLE 0
+#define CFG_LPDDR1_MODE_ENABLE (1 << 20)
+#define CFG_EDO_MODE_MASK (1 << 19)
+#define CFG_EDO_MODE_DISABLE 0
+#define CFG_EDO_MODE_ENABLE (1 << 19)
+#define CFG_PAGE_SIZE_SEL_MASK (7 << 16)
+enum {
+ CFG_PAGE_SIZE_256 = 0 << 16,
+ CFG_PAGE_SIZE_512 = 1 << 16,
+ CFG_PAGE_SIZE_1024 = 2 << 16,
+ CFG_PAGE_SIZE_2048 = 3 << 16,
+ CFG_PAGE_SIZE_4096 = 4 << 16
+};
+#define CFG_SKIP_SPARE_SEL_MASK (3 << 14)
+enum {
+ CFG_SKIP_SPARE_SEL_4 = 0 << 14,
+ CFG_SKIP_SPARE_SEL_8 = 1 << 14,
+ CFG_SKIP_SPARE_SEL_12 = 2 << 14,
+ CFG_SKIP_SPARE_SEL_16 = 3 << 14
+};
+#define CFG_TAG_BYTE_SIZE_MASK 0x1FF
+
+#define TIMING_0 0x14
+#define TIMING_TRP_RESP_CNT_SHIFT 28
+#define TIMING_TRP_RESP_CNT_MASK (0xf << TIMING_TRP_RESP_CNT_SHIFT)
+#define TIMING_TWB_CNT_SHIFT 24
+#define TIMING_TWB_CNT_MASK (0xf << TIMING_TWB_CNT_SHIFT)
+#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20
+#define TIMING_TCR_TAR_TRR_CNT_MASK (0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT)
+#define TIMING_TWHR_CNT_SHIFT 16
+#define TIMING_TWHR_CNT_MASK (0xf << TIMING_TWHR_CNT_SHIFT)
+#define TIMING_TCS_CNT_SHIFT 14
+#define TIMING_TCS_CNT_MASK (3 << TIMING_TCS_CNT_SHIFT)
+#define TIMING_TWH_CNT_SHIFT 12
+#define TIMING_TWH_CNT_MASK (3 << TIMING_TWH_CNT_SHIFT)
+#define TIMING_TWP_CNT_SHIFT 8
+#define TIMING_TWP_CNT_MASK (0xf << TIMING_TWP_CNT_SHIFT)
+#define TIMING_TRH_CNT_SHIFT 4
+#define TIMING_TRH_CNT_MASK (3 << TIMING_TRH_CNT_SHIFT)
+#define TIMING_TRP_CNT_SHIFT 0
+#define TIMING_TRP_CNT_MASK (0xf << TIMING_TRP_CNT_SHIFT)
+
+#define RESP_0 0x18
+
+#define TIMING2_0 0x1C
+#define TIMING2_TADL_CNT_SHIFT 0
+#define TIMING2_TADL_CNT_MASK (0xf << TIMING2_TADL_CNT_SHIFT)
+
+#define CMD_REG1_0 0x20
+#define CMD_REG2_0 0x24
+#define ADDR_REG1_0 0x28
+#define ADDR_REG2_0 0x2C
+
+#define DMA_MST_CTRL_0 0x30
+#define DMA_MST_CTRL_GO_MASK (1 << 31)
+#define DMA_MST_CTRL_GO_DISABLE 0
+#define DMA_MST_CTRL_GO_ENABLE (1 << 31)
+#define DMA_MST_CTRL_DIR_MASK (1 << 30)
+#define DMA_MST_CTRL_DIR_READ 0
+#define DMA_MST_CTRL_DIR_WRITE (1 << 30)
+#define DMA_MST_CTRL_PERF_EN_MASK (1 << 29)
+#define DMA_MST_CTRL_PERF_EN_DISABLE 0
+#define DMA_MST_CTRL_PERF_EN_ENABLE (1 << 29)
+#define DMA_MST_CTRL_REUSE_BUFFER_MASK (1 << 27)
+#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0
+#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE (1 << 27)
+#define DMA_MST_CTRL_BURST_SIZE_SHIFT 24
+#define DMA_MST_CTRL_BURST_SIZE_MASK (7 << DMA_MST_CTRL_BURST_SIZE_SHIFT)
+enum {
+ DMA_MST_CTRL_BURST_1WORDS = 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
+ DMA_MST_CTRL_BURST_4WORDS = 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
+ DMA_MST_CTRL_BURST_8WORDS = 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
+ DMA_MST_CTRL_BURST_16WORDS = 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT
+};
+#define DMA_MST_CTRL_IS_DMA_DONE (1 << 20)
+#define DMA_MST_CTRL_EN_A_MASK (1 << 2)
+#define DMA_MST_CTRL_EN_A_DISABLE 0
+#define DMA_MST_CTRL_EN_A_ENABLE (1 << 2)
+#define DMA_MST_CTRL_EN_B_MASK (1 << 1)
+#define DMA_MST_CTRL_EN_B_DISABLE 0
+#define DMA_MST_CTRL_EN_B_ENABLE (1 << 1)
+
+#define DMA_CFG_A_0 0x34
+#define DMA_CFG_B_0 0x38
+#define FIFO_CTRL_0 0x3C
+#define DATA_BLOCK_PTR_0 0x40
+#define TAG_PTR_0 0x44
+#define ECC_PTR_0 0x48
+
+#define DEC_STATUS_0 0x4C
+#define DEC_STATUS_A_ECC_FAIL (1 << 1)
+#define DEC_STATUS_B_ECC_FAIL (1 << 0)
+
+#define BCH_CONFIG_0 0xCC
+#define BCH_CONFIG_BCH_TVALUE_SHIFT 4
+#define BCH_CONFIG_BCH_TVALUE_MASK (3 << BCH_CONFIG_BCH_TVALUE_SHIFT)
+enum {
+ BCH_CONFIG_BCH_TVAL4 = 0 << BCH_CONFIG_BCH_TVALUE_SHIFT,
+ BCH_CONFIG_BCH_TVAL8 = 1 << BCH_CONFIG_BCH_TVALUE_SHIFT,
+ BCH_CONFIG_BCH_TVAL14 = 2 << BCH_CONFIG_BCH_TVALUE_SHIFT,
+ BCH_CONFIG_BCH_TVAL16 = 3 << BCH_CONFIG_BCH_TVALUE_SHIFT
+};
+#define BCH_CONFIG_BCH_ECC_MASK (1 << 0)
+#define BCH_CONFIG_BCH_ECC_DISABLE 0
+#define BCH_CONFIG_BCH_ECC_ENABLE (1 << 0)
+
+#define BCH_DEC_RESULT_0 0xD0
+#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK (1 << 8)
+#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF
+
+#define BCH_DEC_STATUS_BUF_0 0xD4
+#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000
+#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000
+#define BCH_DEC_STATUS_FAIL_TAG_MASK (1 << 14)
+#define BCH_DEC_STATUS_CORR_TAG_MASK (1 << 13)
+#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8)
+#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF
+
+#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR)
+
+struct nand_ctlr {
+ u32 command; /* offset 00h */
+ u32 status; /* offset 04h */
+ u32 isr; /* offset 08h */
+ u32 ier; /* offset 0Ch */
+ u32 config; /* offset 10h */
+ u32 timing; /* offset 14h */
+ u32 resp; /* offset 18h */
+ u32 timing2; /* offset 1Ch */
+ u32 cmd_reg1; /* offset 20h */
+ u32 cmd_reg2; /* offset 24h */
+ u32 addr_reg1; /* offset 28h */
+ u32 addr_reg2; /* offset 2Ch */
+ u32 dma_mst_ctrl; /* offset 30h */
+ u32 dma_cfg_a; /* offset 34h */
+ u32 dma_cfg_b; /* offset 38h */
+ u32 fifo_ctrl; /* offset 3Ch */
+ u32 data_block_ptr; /* offset 40h */
+ u32 tag_ptr; /* offset 44h */
+ u32 resv1; /* offset 48h */
+ u32 dec_status; /* offset 4Ch */
+ u32 hwstatus_cmd; /* offset 50h */
+ u32 hwstatus_mask; /* offset 54h */
+ u32 resv2[29];
+ u32 bch_config; /* offset CCh */
+ u32 bch_dec_result; /* offset D0h */
+ u32 bch_dec_status_buf;
+ /* offset D4h */
+};
diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c
index 9a114ac6a1..32b76e0e90 100644
--- a/drivers/mtd/spi/spansion.c
+++ b/drivers/mtd/spi/spansion.c
@@ -96,6 +96,13 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
.nr_sectors = 256,
.name = "S25FL129P_64K",
},
+ {
+ .idcode1 = 0x2019,
+ .idcode2 = 0x4d01,
+ .pages_per_sector = 256,
+ .nr_sectors = 512,
+ .name = "S25FL256S",
+ },
};
struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c
index dbd1fc1854..30b626a39f 100644
--- a/drivers/mtd/spi/stmicro.c
+++ b/drivers/mtd/spi/stmicro.c
@@ -37,7 +37,7 @@
#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
struct stmicro_spi_flash_params {
- u8 idcode1;
+ u16 id;
u16 pages_per_sector;
u16 nr_sectors;
const char *name;
@@ -45,55 +45,67 @@ struct stmicro_spi_flash_params {
static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
{
- .idcode1 = 0x11,
+ .id = 0x2011,
.pages_per_sector = 128,
.nr_sectors = 4,
.name = "M25P10",
},
{
- .idcode1 = 0x15,
+ .id = 0x2015,
.pages_per_sector = 256,
.nr_sectors = 32,
.name = "M25P16",
},
{
- .idcode1 = 0x12,
+ .id = 0x2012,
.pages_per_sector = 256,
.nr_sectors = 4,
.name = "M25P20",
},
{
- .idcode1 = 0x16,
+ .id = 0x2016,
.pages_per_sector = 256,
.nr_sectors = 64,
.name = "M25P32",
},
{
- .idcode1 = 0x13,
+ .id = 0x2013,
.pages_per_sector = 256,
.nr_sectors = 8,
.name = "M25P40",
},
{
- .idcode1 = 0x17,
+ .id = 0x2017,
.pages_per_sector = 256,
.nr_sectors = 128,
.name = "M25P64",
},
{
- .idcode1 = 0x14,
+ .id = 0x2014,
.pages_per_sector = 256,
.nr_sectors = 16,
.name = "M25P80",
},
{
- .idcode1 = 0x18,
+ .id = 0x2018,
.pages_per_sector = 1024,
.nr_sectors = 64,
.name = "M25P128",
},
{
- .idcode1 = 0x19,
+ .id = 0xba18,
+ .pages_per_sector = 256,
+ .nr_sectors = 256,
+ .name = "N25Q128",
+ },
+ {
+ .id = 0xbb18,
+ .pages_per_sector = 256,
+ .nr_sectors = 256,
+ .name = "N25Q128A",
+ },
+ {
+ .id = 0xba19,
.pages_per_sector = 256,
.nr_sectors = 512,
.name = "N25Q256",
@@ -105,6 +117,7 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
const struct stmicro_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
+ u16 id;
if (idcode[0] == 0xff) {
i = spi_flash_cmd(spi, CMD_M25PXX_RES,
@@ -119,15 +132,17 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
return NULL;
}
+ id = ((idcode[1] << 8) | idcode[2]);
+
for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
params = &stmicro_spi_flash_table[i];
- if (params->idcode1 == idcode[2]) {
+ if (params->id == id) {
break;
}
}
if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
- debug("SF: Unsupported STMicro ID %02x\n", idcode[1]);
+ debug("SF: Unsupported STMicro ID %04x\n", id);
return NULL;
}
diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c
index 427b71fcdc..f6aab3d32f 100644
--- a/drivers/mtd/spi/winbond.c
+++ b/drivers/mtd/spi/winbond.c
@@ -62,6 +62,11 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.nr_blocks = 256,
.name = "W25Q128",
},
+ {
+ .id = 0x5014,
+ .nr_blocks = 128,
+ .name = "W25Q80",
+ },
};
struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
@@ -94,7 +99,7 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
flash->write = spi_flash_cmd_write_multi;
flash->erase = spi_flash_cmd_erase;
flash->read = spi_flash_cmd_read_fast;
- flash->page_size = 4096;
+ flash->page_size = 256;
flash->sector_size = 4096;
flash->size = 4096 * 16 * params->nr_blocks;
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 430f90ceaf..011cd5191c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -71,6 +71,7 @@ COBJS-$(CONFIG_SMC91111) += smc91111.o
COBJS-$(CONFIG_SMC911X) += smc911x.o
COBJS-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
COBJS-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o
+COBJS-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o
COBJS-$(CONFIG_FMAN_ENET) += fsl_mdio.o
COBJS-$(CONFIG_TSI108_ETH) += tsi108_eth.o
COBJS-$(CONFIG_ULI526X) += uli526x.o
diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c
new file mode 100644
index 0000000000..af3d8593eb
--- /dev/null
+++ b/drivers/net/cpsw.c
@@ -0,0 +1,991 @@
+/*
+ * CPSW Ethernet Switch Driver
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <net.h>
+#include <miiphy.h>
+#include <malloc.h>
+#include <net.h>
+#include <netdev.h>
+#include <cpsw.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <phy.h>
+
+#define BITMASK(bits) (BIT(bits) - 1)
+#define PHY_REG_MASK 0x1f
+#define PHY_ID_MASK 0x1f
+#define NUM_DESCS (PKTBUFSRX * 2)
+#define PKT_MIN 60
+#define PKT_MAX (1500 + 14 + 4 + 4)
+#define CLEAR_BIT 1
+#define GIGABITEN BIT(7)
+#define FULLDUPLEXEN BIT(0)
+#define MIIEN BIT(15)
+
+/* DMA Registers */
+#define CPDMA_TXCONTROL 0x004
+#define CPDMA_RXCONTROL 0x014
+#define CPDMA_SOFTRESET 0x01c
+#define CPDMA_RXFREE 0x0e0
+#define CPDMA_TXHDP_VER1 0x100
+#define CPDMA_TXHDP_VER2 0x200
+#define CPDMA_RXHDP_VER1 0x120
+#define CPDMA_RXHDP_VER2 0x220
+#define CPDMA_TXCP_VER1 0x140
+#define CPDMA_TXCP_VER2 0x240
+#define CPDMA_RXCP_VER1 0x160
+#define CPDMA_RXCP_VER2 0x260
+
+#define CPDMA_RAM_ADDR 0x4a102000
+
+/* Descriptor mode bits */
+#define CPDMA_DESC_SOP BIT(31)
+#define CPDMA_DESC_EOP BIT(30)
+#define CPDMA_DESC_OWNER BIT(29)
+#define CPDMA_DESC_EOQ BIT(28)
+
+/*
+ * This timeout definition is a worst-case ultra defensive measure against
+ * unexpected controller lock ups. Ideally, we should never ever hit this
+ * scenario in practice.
+ */
+#define MDIO_TIMEOUT 100 /* msecs */
+#define CPDMA_TIMEOUT 100 /* msecs */
+
+struct cpsw_mdio_regs {
+ u32 version;
+ u32 control;
+#define CONTROL_IDLE BIT(31)
+#define CONTROL_ENABLE BIT(30)
+
+ u32 alive;
+ u32 link;
+ u32 linkintraw;
+ u32 linkintmasked;
+ u32 __reserved_0[2];
+ u32 userintraw;
+ u32 userintmasked;
+ u32 userintmaskset;
+ u32 userintmaskclr;
+ u32 __reserved_1[20];
+
+ struct {
+ u32 access;
+ u32 physel;
+#define USERACCESS_GO BIT(31)
+#define USERACCESS_WRITE BIT(30)
+#define USERACCESS_ACK BIT(29)
+#define USERACCESS_READ (0)
+#define USERACCESS_DATA (0xffff)
+ } user[0];
+};
+
+struct cpsw_regs {
+ u32 id_ver;
+ u32 control;
+ u32 soft_reset;
+ u32 stat_port_en;
+ u32 ptype;
+};
+
+struct cpsw_slave_regs {
+ u32 max_blks;
+ u32 blk_cnt;
+ u32 flow_thresh;
+ u32 port_vlan;
+ u32 tx_pri_map;
+ u32 gap_thresh;
+ u32 sa_lo;
+ u32 sa_hi;
+};
+
+struct cpsw_host_regs {
+ u32 max_blks;
+ u32 blk_cnt;
+ u32 flow_thresh;
+ u32 port_vlan;
+ u32 tx_pri_map;
+ u32 cpdma_tx_pri_map;
+ u32 cpdma_rx_chan_map;
+};
+
+struct cpsw_sliver_regs {
+ u32 id_ver;
+ u32 mac_control;
+ u32 mac_status;
+ u32 soft_reset;
+ u32 rx_maxlen;
+ u32 __reserved_0;
+ u32 rx_pause;
+ u32 tx_pause;
+ u32 __reserved_1;
+ u32 rx_pri_map;
+};
+
+#define ALE_ENTRY_BITS 68
+#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
+
+/* ALE Registers */
+#define ALE_CONTROL 0x08
+#define ALE_UNKNOWNVLAN 0x18
+#define ALE_TABLE_CONTROL 0x20
+#define ALE_TABLE 0x34
+#define ALE_PORTCTL 0x40
+
+#define ALE_TABLE_WRITE BIT(31)
+
+#define ALE_TYPE_FREE 0
+#define ALE_TYPE_ADDR 1
+#define ALE_TYPE_VLAN 2
+#define ALE_TYPE_VLAN_ADDR 3
+
+#define ALE_UCAST_PERSISTANT 0
+#define ALE_UCAST_UNTOUCHED 1
+#define ALE_UCAST_OUI 2
+#define ALE_UCAST_TOUCHED 3
+
+#define ALE_MCAST_FWD 0
+#define ALE_MCAST_BLOCK_LEARN_FWD 1
+#define ALE_MCAST_FWD_LEARN 2
+#define ALE_MCAST_FWD_2 3
+
+enum cpsw_ale_port_state {
+ ALE_PORT_STATE_DISABLE = 0x00,
+ ALE_PORT_STATE_BLOCK = 0x01,
+ ALE_PORT_STATE_LEARN = 0x02,
+ ALE_PORT_STATE_FORWARD = 0x03,
+};
+
+/* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */
+#define ALE_SECURE 1
+#define ALE_BLOCKED 2
+
+struct cpsw_slave {
+ struct cpsw_slave_regs *regs;
+ struct cpsw_sliver_regs *sliver;
+ int slave_num;
+ u32 mac_control;
+ struct cpsw_slave_data *data;
+};
+
+struct cpdma_desc {
+ /* hardware fields */
+ u32 hw_next;
+ u32 hw_buffer;
+ u32 hw_len;
+ u32 hw_mode;
+ /* software fields */
+ u32 sw_buffer;
+ u32 sw_len;
+};
+
+struct cpdma_chan {
+ struct cpdma_desc *head, *tail;
+ void *hdp, *cp, *rxfree;
+};
+
+#define desc_write(desc, fld, val) __raw_writel((u32)(val), &(desc)->fld)
+#define desc_read(desc, fld) __raw_readl(&(desc)->fld)
+#define desc_read_ptr(desc, fld) ((void *)__raw_readl(&(desc)->fld))
+
+#define chan_write(chan, fld, val) __raw_writel((u32)(val), (chan)->fld)
+#define chan_read(chan, fld) __raw_readl((chan)->fld)
+#define chan_read_ptr(chan, fld) ((void *)__raw_readl((chan)->fld))
+
+#define for_each_slave(slave, priv) \
+ for (slave = (priv)->slaves; slave != (priv)->slaves + \
+ (priv)->data.slaves; slave++)
+
+struct cpsw_priv {
+ struct eth_device *dev;
+ struct cpsw_platform_data data;
+ int host_port;
+
+ struct cpsw_regs *regs;
+ void *dma_regs;
+ struct cpsw_host_regs *host_port_regs;
+ void *ale_regs;
+
+ struct cpdma_desc *descs;
+ struct cpdma_desc *desc_free;
+ struct cpdma_chan rx_chan, tx_chan;
+
+ struct cpsw_slave *slaves;
+ struct phy_device *phydev;
+ struct mii_dev *bus;
+};
+
+static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
+{
+ int idx;
+
+ idx = start / 32;
+ start -= idx * 32;
+ idx = 2 - idx; /* flip */
+ return (ale_entry[idx] >> start) & BITMASK(bits);
+}
+
+static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
+ u32 value)
+{
+ int idx;
+
+ value &= BITMASK(bits);
+ idx = start / 32;
+ start -= idx * 32;
+ idx = 2 - idx; /* flip */
+ ale_entry[idx] &= ~(BITMASK(bits) << start);
+ ale_entry[idx] |= (value << start);
+}
+
+#define DEFINE_ALE_FIELD(name, start, bits) \
+static inline int cpsw_ale_get_##name(u32 *ale_entry) \
+{ \
+ return cpsw_ale_get_field(ale_entry, start, bits); \
+} \
+static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \
+{ \
+ cpsw_ale_set_field(ale_entry, start, bits, value); \
+}
+
+DEFINE_ALE_FIELD(entry_type, 60, 2)
+DEFINE_ALE_FIELD(mcast_state, 62, 2)
+DEFINE_ALE_FIELD(port_mask, 66, 3)
+DEFINE_ALE_FIELD(ucast_type, 62, 2)
+DEFINE_ALE_FIELD(port_num, 66, 2)
+DEFINE_ALE_FIELD(blocked, 65, 1)
+DEFINE_ALE_FIELD(secure, 64, 1)
+DEFINE_ALE_FIELD(mcast, 40, 1)
+
+/* The MAC address field in the ALE entry cannot be macroized as above */
+static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++)
+ addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
+}
+
+static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++)
+ cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
+}
+
+static int cpsw_ale_read(struct cpsw_priv *priv, int idx, u32 *ale_entry)
+{
+ int i;
+
+ __raw_writel(idx, priv->ale_regs + ALE_TABLE_CONTROL);
+
+ for (i = 0; i < ALE_ENTRY_WORDS; i++)
+ ale_entry[i] = __raw_readl(priv->ale_regs + ALE_TABLE + 4 * i);
+
+ return idx;
+}
+
+static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry)
+{
+ int i;
+
+ for (i = 0; i < ALE_ENTRY_WORDS; i++)
+ __raw_writel(ale_entry[i], priv->ale_regs + ALE_TABLE + 4 * i);
+
+ __raw_writel(idx | ALE_TABLE_WRITE, priv->ale_regs + ALE_TABLE_CONTROL);
+
+ return idx;
+}
+
+static int cpsw_ale_match_addr(struct cpsw_priv *priv, u8* addr)
+{
+ u32 ale_entry[ALE_ENTRY_WORDS];
+ int type, idx;
+
+ for (idx = 0; idx < priv->data.ale_entries; idx++) {
+ u8 entry_addr[6];
+
+ cpsw_ale_read(priv, idx, ale_entry);
+ type = cpsw_ale_get_entry_type(ale_entry);
+ if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
+ continue;
+ cpsw_ale_get_addr(ale_entry, entry_addr);
+ if (memcmp(entry_addr, addr, 6) == 0)
+ return idx;
+ }
+ return -ENOENT;
+}
+
+static int cpsw_ale_match_free(struct cpsw_priv *priv)
+{
+ u32 ale_entry[ALE_ENTRY_WORDS];
+ int type, idx;
+
+ for (idx = 0; idx < priv->data.ale_entries; idx++) {
+ cpsw_ale_read(priv, idx, ale_entry);
+ type = cpsw_ale_get_entry_type(ale_entry);
+ if (type == ALE_TYPE_FREE)
+ return idx;
+ }
+ return -ENOENT;
+}
+
+static int cpsw_ale_find_ageable(struct cpsw_priv *priv)
+{
+ u32 ale_entry[ALE_ENTRY_WORDS];
+ int type, idx;
+
+ for (idx = 0; idx < priv->data.ale_entries; idx++) {
+ cpsw_ale_read(priv, idx, ale_entry);
+ type = cpsw_ale_get_entry_type(ale_entry);
+ if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
+ continue;
+ if (cpsw_ale_get_mcast(ale_entry))
+ continue;
+ type = cpsw_ale_get_ucast_type(ale_entry);
+ if (type != ALE_UCAST_PERSISTANT &&
+ type != ALE_UCAST_OUI)
+ return idx;
+ }
+ return -ENOENT;
+}
+
+static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr,
+ int port, int flags)
+{
+ u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+ int idx;
+
+ cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+ cpsw_ale_set_addr(ale_entry, addr);
+ cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
+ cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
+ cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
+ cpsw_ale_set_port_num(ale_entry, port);
+
+ idx = cpsw_ale_match_addr(priv, addr);
+ if (idx < 0)
+ idx = cpsw_ale_match_free(priv);
+ if (idx < 0)
+ idx = cpsw_ale_find_ageable(priv);
+ if (idx < 0)
+ return -ENOMEM;
+
+ cpsw_ale_write(priv, idx, ale_entry);
+ return 0;
+}
+
+static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask)
+{
+ u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+ int idx, mask;
+
+ idx = cpsw_ale_match_addr(priv, addr);
+ if (idx >= 0)
+ cpsw_ale_read(priv, idx, ale_entry);
+
+ cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+ cpsw_ale_set_addr(ale_entry, addr);
+ cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2);
+
+ mask = cpsw_ale_get_port_mask(ale_entry);
+ port_mask |= mask;
+ cpsw_ale_set_port_mask(ale_entry, port_mask);
+
+ if (idx < 0)
+ idx = cpsw_ale_match_free(priv);
+ if (idx < 0)
+ idx = cpsw_ale_find_ageable(priv);
+ if (idx < 0)
+ return -ENOMEM;
+
+ cpsw_ale_write(priv, idx, ale_entry);
+ return 0;
+}
+
+static inline void cpsw_ale_control(struct cpsw_priv *priv, int bit, int val)
+{
+ u32 tmp, mask = BIT(bit);
+
+ tmp = __raw_readl(priv->ale_regs + ALE_CONTROL);
+ tmp &= ~mask;
+ tmp |= val ? mask : 0;
+ __raw_writel(tmp, priv->ale_regs + ALE_CONTROL);
+}
+
+#define cpsw_ale_enable(priv, val) cpsw_ale_control(priv, 31, val)
+#define cpsw_ale_clear(priv, val) cpsw_ale_control(priv, 30, val)
+#define cpsw_ale_vlan_aware(priv, val) cpsw_ale_control(priv, 2, val)
+
+static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int port,
+ int val)
+{
+ int offset = ALE_PORTCTL + 4 * port;
+ u32 tmp, mask = 0x3;
+
+ tmp = __raw_readl(priv->ale_regs + offset);
+ tmp &= ~mask;
+ tmp |= val & mask;
+ __raw_writel(tmp, priv->ale_regs + offset);
+}
+
+static struct cpsw_mdio_regs *mdio_regs;
+
+/* wait until hardware is ready for another user access */
+static inline u32 wait_for_user_access(void)
+{
+ u32 reg = 0;
+ int timeout = MDIO_TIMEOUT;
+
+ while (timeout-- &&
+ ((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO))
+ udelay(10);
+
+ if (timeout == -1) {
+ printf("wait_for_user_access Timeout\n");
+ return -ETIMEDOUT;
+ }
+ return reg;
+}
+
+/* wait until hardware state machine is idle */
+static inline void wait_for_idle(void)
+{
+ int timeout = MDIO_TIMEOUT;
+
+ while (timeout-- &&
+ ((__raw_readl(&mdio_regs->control) & CONTROL_IDLE) == 0))
+ udelay(10);
+
+ if (timeout == -1)
+ printf("wait_for_idle Timeout\n");
+}
+
+static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
+ int dev_addr, int phy_reg)
+{
+ unsigned short data;
+ u32 reg;
+
+ if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+ return -EINVAL;
+
+ wait_for_user_access();
+ reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
+ (phy_id << 16));
+ __raw_writel(reg, &mdio_regs->user[0].access);
+ reg = wait_for_user_access();
+
+ data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
+ return data;
+}
+
+static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr,
+ int phy_reg, u16 data)
+{
+ u32 reg;
+
+ if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+ return -EINVAL;
+
+ wait_for_user_access();
+ reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) |
+ (phy_id << 16) | (data & USERACCESS_DATA));
+ __raw_writel(reg, &mdio_regs->user[0].access);
+ wait_for_user_access();
+
+ return 0;
+}
+
+static void cpsw_mdio_init(char *name, u32 mdio_base, u32 div)
+{
+ struct mii_dev *bus = mdio_alloc();
+
+ mdio_regs = (struct cpsw_mdio_regs *)mdio_base;
+
+ /* set enable and clock divider */
+ __raw_writel(div | CONTROL_ENABLE, &mdio_regs->control);
+
+ /*
+ * wait for scan logic to settle:
+ * the scan time consists of (a) a large fixed component, and (b) a
+ * small component that varies with the mii bus frequency. These
+ * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
+ * silicon. Since the effect of (b) was found to be largely
+ * negligible, we keep things simple here.
+ */
+ udelay(1000);
+
+ bus->read = cpsw_mdio_read;
+ bus->write = cpsw_mdio_write;
+ sprintf(bus->name, name);
+
+ mdio_register(bus);
+}
+
+/* Set a self-clearing bit in a register, and wait for it to clear */
+static inline void setbit_and_wait_for_clear32(void *addr)
+{
+ __raw_writel(CLEAR_BIT, addr);
+ while (__raw_readl(addr) & CLEAR_BIT)
+ ;
+}
+
+#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \
+ ((mac)[2] << 16) | ((mac)[3] << 24))
+#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8))
+
+static void cpsw_set_slave_mac(struct cpsw_slave *slave,
+ struct cpsw_priv *priv)
+{
+ __raw_writel(mac_hi(priv->dev->enetaddr), &slave->regs->sa_hi);
+ __raw_writel(mac_lo(priv->dev->enetaddr), &slave->regs->sa_lo);
+}
+
+static void cpsw_slave_update_link(struct cpsw_slave *slave,
+ struct cpsw_priv *priv, int *link)
+{
+ struct phy_device *phy = priv->phydev;
+ u32 mac_control = 0;
+
+ phy_startup(phy);
+ *link = phy->link;
+
+ if (*link) { /* link up */
+ mac_control = priv->data.mac_control;
+ if (phy->speed == 1000)
+ mac_control |= GIGABITEN;
+ if (phy->duplex == DUPLEX_FULL)
+ mac_control |= FULLDUPLEXEN;
+ if (phy->speed == 100)
+ mac_control |= MIIEN;
+ }
+
+ if (mac_control == slave->mac_control)
+ return;
+
+ if (mac_control) {
+ printf("link up on port %d, speed %d, %s duplex\n",
+ slave->slave_num, phy->speed,
+ (phy->duplex == DUPLEX_FULL) ? "full" : "half");
+ } else {
+ printf("link down on port %d\n", slave->slave_num);
+ }
+
+ __raw_writel(mac_control, &slave->sliver->mac_control);
+ slave->mac_control = mac_control;
+}
+
+static int cpsw_update_link(struct cpsw_priv *priv)
+{
+ int link = 0;
+ struct cpsw_slave *slave;
+
+ for_each_slave(slave, priv)
+ cpsw_slave_update_link(slave, priv, &link);
+
+ return link;
+}
+
+static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
+{
+ if (priv->host_port == 0)
+ return slave_num + 1;
+ else
+ return slave_num;
+}
+
+static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv)
+{
+ u32 slave_port;
+
+ setbit_and_wait_for_clear32(&slave->sliver->soft_reset);
+
+ /* setup priority mapping */
+ __raw_writel(0x76543210, &slave->sliver->rx_pri_map);
+ __raw_writel(0x33221100, &slave->regs->tx_pri_map);
+
+ /* setup max packet size, and mac address */
+ __raw_writel(PKT_MAX, &slave->sliver->rx_maxlen);
+ cpsw_set_slave_mac(slave, priv);
+
+ slave->mac_control = 0; /* no link yet */
+
+ /* enable forwarding */
+ slave_port = cpsw_get_slave_port(priv, slave->slave_num);
+ cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD);
+
+ cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << slave_port);
+}
+
+static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv)
+{
+ struct cpdma_desc *desc = priv->desc_free;
+
+ if (desc)
+ priv->desc_free = desc_read_ptr(desc, hw_next);
+ return desc;
+}
+
+static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc)
+{
+ if (desc) {
+ desc_write(desc, hw_next, priv->desc_free);
+ priv->desc_free = desc;
+ }
+}
+
+static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan,
+ void *buffer, int len)
+{
+ struct cpdma_desc *desc, *prev;
+ u32 mode;
+
+ desc = cpdma_desc_alloc(priv);
+ if (!desc)
+ return -ENOMEM;
+
+ if (len < PKT_MIN)
+ len = PKT_MIN;
+
+ mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;
+
+ desc_write(desc, hw_next, 0);
+ desc_write(desc, hw_buffer, buffer);
+ desc_write(desc, hw_len, len);
+ desc_write(desc, hw_mode, mode | len);
+ desc_write(desc, sw_buffer, buffer);
+ desc_write(desc, sw_len, len);
+
+ if (!chan->head) {
+ /* simple case - first packet enqueued */
+ chan->head = desc;
+ chan->tail = desc;
+ chan_write(chan, hdp, desc);
+ goto done;
+ }
+
+ /* not the first packet - enqueue at the tail */
+ prev = chan->tail;
+ desc_write(prev, hw_next, desc);
+ chan->tail = desc;
+
+ /* next check if EOQ has been triggered already */
+ if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ)
+ chan_write(chan, hdp, desc);
+
+done:
+ if (chan->rxfree)
+ chan_write(chan, rxfree, 1);
+ return 0;
+}
+
+static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan,
+ void **buffer, int *len)
+{
+ struct cpdma_desc *desc = chan->head;
+ u32 status;
+
+ if (!desc)
+ return -ENOENT;
+
+ status = desc_read(desc, hw_mode);
+
+ if (len)
+ *len = status & 0x7ff;
+
+ if (buffer)
+ *buffer = desc_read_ptr(desc, sw_buffer);
+
+ if (status & CPDMA_DESC_OWNER) {
+ if (chan_read(chan, hdp) == 0) {
+ if (desc_read(desc, hw_mode) & CPDMA_DESC_OWNER)
+ chan_write(chan, hdp, desc);
+ }
+
+ return -EBUSY;
+ }
+
+ chan->head = desc_read_ptr(desc, hw_next);
+ chan_write(chan, cp, desc);
+
+ cpdma_desc_free(priv, desc);
+ return 0;
+}
+
+static int cpsw_init(struct eth_device *dev, bd_t *bis)
+{
+ struct cpsw_priv *priv = dev->priv;
+ struct cpsw_slave *slave;
+ int i, ret;
+
+ /* soft reset the controller and initialize priv */
+ setbit_and_wait_for_clear32(&priv->regs->soft_reset);
+
+ /* initialize and reset the address lookup engine */
+ cpsw_ale_enable(priv, 1);
+ cpsw_ale_clear(priv, 1);
+ cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */
+
+ /* setup host port priority mapping */
+ __raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map);
+ __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);
+
+ /* disable priority elevation and enable statistics on all ports */
+ __raw_writel(0, &priv->regs->ptype);
+
+ /* enable statistics collection only on the host port */
+ __raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en);
+
+ cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD);
+
+ cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port,
+ ALE_SECURE);
+ cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port);
+
+ for_each_slave(slave, priv)
+ cpsw_slave_init(slave, priv);
+
+ cpsw_update_link(priv);
+
+ /* init descriptor pool */
+ for (i = 0; i < NUM_DESCS; i++) {
+ desc_write(&priv->descs[i], hw_next,
+ (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]);
+ }
+ priv->desc_free = &priv->descs[0];
+
+ /* initialize channels */
+ if (priv->data.version == CPSW_CTRL_VERSION_2) {
+ memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
+ priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER2;
+ priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER2;
+ priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE;
+
+ memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
+ priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER2;
+ priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER2;
+ } else {
+ memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
+ priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER1;
+ priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER1;
+ priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE;
+
+ memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
+ priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER1;
+ priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER1;
+ }
+
+ /* clear dma state */
+ setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET);
+
+ if (priv->data.version == CPSW_CTRL_VERSION_2) {
+ for (i = 0; i < priv->data.channels; i++) {
+ __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4
+ * i);
+ }
+ } else {
+ for (i = 0; i < priv->data.channels; i++) {
+ __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4
+ * i);
+ __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4
+ * i);
+
+ }
+ }
+
+ __raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL);
+ __raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL);
+
+ /* submit rx descs */
+ for (i = 0; i < PKTBUFSRX; i++) {
+ ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i],
+ PKTSIZE);
+ if (ret < 0) {
+ printf("error %d submitting rx desc\n", ret);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void cpsw_halt(struct eth_device *dev)
+{
+ struct cpsw_priv *priv = dev->priv;
+
+ writel(0, priv->dma_regs + CPDMA_TXCONTROL);
+ writel(0, priv->dma_regs + CPDMA_RXCONTROL);
+
+ /* soft reset the controller and initialize priv */
+ setbit_and_wait_for_clear32(&priv->regs->soft_reset);
+
+ /* clear dma state */
+ setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET);
+
+ priv->data.control(0);
+}
+
+static int cpsw_send(struct eth_device *dev, void *packet, int length)
+{
+ struct cpsw_priv *priv = dev->priv;
+ void *buffer;
+ int len;
+ int timeout = CPDMA_TIMEOUT;
+
+ if (!cpsw_update_link(priv))
+ return -EIO;
+
+ flush_dcache_range((unsigned long)packet,
+ (unsigned long)packet + length);
+
+ /* first reap completed packets */
+ while (timeout-- &&
+ (cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0))
+ ;
+
+ if (timeout == -1) {
+ printf("cpdma_process timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return cpdma_submit(priv, &priv->tx_chan, packet, length);
+}
+
+static int cpsw_recv(struct eth_device *dev)
+{
+ struct cpsw_priv *priv = dev->priv;
+ void *buffer;
+ int len;
+
+ cpsw_update_link(priv);
+
+ while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) {
+ invalidate_dcache_range((unsigned long)buffer,
+ (unsigned long)buffer + PKTSIZE_ALIGN);
+ NetReceive(buffer, len);
+ cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE);
+ }
+
+ return 0;
+}
+
+static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num,
+ struct cpsw_priv *priv)
+{
+ void *regs = priv->regs;
+ struct cpsw_slave_data *data = priv->data.slave_data + slave_num;
+ slave->slave_num = slave_num;
+ slave->data = data;
+ slave->regs = regs + data->slave_reg_ofs;
+ slave->sliver = regs + data->sliver_reg_ofs;
+}
+
+static int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave)
+{
+ struct cpsw_priv *priv = (struct cpsw_priv *)dev->priv;
+ struct phy_device *phydev;
+ u32 supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full);
+
+ phydev = phy_connect(priv->bus, 0, dev, slave->data->phy_if);
+
+ phydev->supported &= supported;
+ phydev->advertising = phydev->supported;
+
+ priv->phydev = phydev;
+ phy_config(phydev);
+
+ return 1;
+}
+
+int cpsw_register(struct cpsw_platform_data *data)
+{
+ struct cpsw_priv *priv;
+ struct cpsw_slave *slave;
+ void *regs = (void *)data->cpsw_base;
+ struct eth_device *dev;
+
+ dev = calloc(sizeof(*dev), 1);
+ if (!dev)
+ return -ENOMEM;
+
+ priv = calloc(sizeof(*priv), 1);
+ if (!priv) {
+ free(dev);
+ return -ENOMEM;
+ }
+
+ priv->data = *data;
+ priv->dev = dev;
+
+ priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves);
+ if (!priv->slaves) {
+ free(dev);
+ free(priv);
+ return -ENOMEM;
+ }
+
+ priv->descs = (void *)CPDMA_RAM_ADDR;
+ priv->host_port = data->host_port_num;
+ priv->regs = regs;
+ priv->host_port_regs = regs + data->host_port_reg_ofs;
+ priv->dma_regs = regs + data->cpdma_reg_ofs;
+ priv->ale_regs = regs + data->ale_reg_ofs;
+
+ int idx = 0;
+
+ for_each_slave(slave, priv) {
+ cpsw_slave_setup(slave, idx, priv);
+ idx = idx + 1;
+ }
+
+ strcpy(dev->name, "cpsw");
+ dev->iobase = 0;
+ dev->init = cpsw_init;
+ dev->halt = cpsw_halt;
+ dev->send = cpsw_send;
+ dev->recv = cpsw_recv;
+ dev->priv = priv;
+
+ eth_register(dev);
+
+ cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div);
+ priv->bus = miiphy_get_dev_by_name(dev->name);
+ for_each_slave(slave, priv)
+ cpsw_phy_init(dev, slave);
+
+ return 1;
+}
diff --git a/drivers/net/greth.c b/drivers/net/greth.c
index 08206c8711..3103a741a1 100644
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -483,7 +483,7 @@ int greth_recv(struct eth_device *dev)
greth_regs *regs = greth->regs;
greth_bd *rxbd;
unsigned int status, len = 0, bad;
- unsigned char *d;
+ char *d;
int enable = 0;
int i;
@@ -504,7 +504,7 @@ int greth_recv(struct eth_device *dev)
goto done;
}
- debug("greth_recv: packet 0x%lx, 0x%lx, len: %d\n",
+ debug("greth_recv: packet 0x%x, 0x%x, len: %d\n",
(unsigned int)rxbd, status, status & GRETH_BD_LEN);
/* Check status for errors.
@@ -620,7 +620,7 @@ int greth_initialize(bd_t * bis)
greth->regs = (greth_regs *) apbdev.address;
greth->irq = apbdev.irq;
- debug("Found GRETH at 0x%lx, irq %d\n", greth->regs, greth->irq);
+ debug("Found GRETH at %p, irq %d\n", greth->regs, greth->irq);
dev->priv = (void *)greth;
dev->iobase = (unsigned int)greth->regs;
dev->init = greth_init;
@@ -652,7 +652,7 @@ int greth_initialize(bd_t * bis)
/* initiate PHY, select speed/duplex depending on connected PHY */
if (greth_init_phy(greth, bis)) {
/* Failed to init PHY (timedout) */
- debug("GRETH[0x%08x]: Failed to init PHY\n", greth->regs);
+ debug("GRETH[%p]: Failed to init PHY\n", greth->regs);
return -1;
}
@@ -681,6 +681,6 @@ int greth_initialize(bd_t * bis)
/* set and remember MAC address */
greth_set_hwaddr(greth, addr);
- debug("GRETH[0x%08x]: Initialized successfully\n", greth->regs);
+ debug("GRETH[%p]: Initialized successfully\n", greth->regs);
return 0;
}
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 45784678ba..0e1ced71c5 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -364,7 +364,7 @@ static int macb_phy_find(struct macb_device *macb)
}
/* PHY isn't up to snuff */
- printf("%s: PHY not found", macb->netdev.name);
+ printf("%s: PHY not found\n", macb->netdev.name);
return 0;
}
@@ -471,7 +471,7 @@ static int macb_init(struct eth_device *netdev, bd_t *bd)
#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \
- defined(CONFIG_AT91SAM9XE)
+ defined(CONFIG_AT91SAM9XE) || defined(CONFIG_AT91SAM9X5)
macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, 0);
@@ -480,7 +480,7 @@ static int macb_init(struct eth_device *netdev, bd_t *bd)
#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \
- defined(CONFIG_AT91SAM9XE)
+ defined(CONFIG_AT91SAM9XE) || defined(CONFIG_AT91SAM9X5)
macb_writel(macb, USRIO, MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, MACB_BIT(MII));
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 398542b9e2..d864f137f5 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -151,13 +151,14 @@ void pci_register_hose(struct pci_controller* hose)
*phose = hose;
}
-struct pci_controller *pci_bus_to_hose (int bus)
+struct pci_controller *pci_bus_to_hose(int bus)
{
struct pci_controller *hose;
- for (hose = hose_head; hose; hose = hose->next)
+ for (hose = hose_head; hose; hose = hose->next) {
if (bus >= hose->first_busno && bus <= hose->last_busno)
return hose;
+ }
printf("pci_bus_to_hose() failed\n");
return NULL;
@@ -196,21 +197,20 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
pci_dev_t bdf;
int i, bus, found_multi = 0;
- for (hose = hose_head; hose; hose = hose->next)
- {
+ for (hose = hose_head; hose; hose = hose->next) {
#ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE
for (bus = hose->last_busno; bus >= hose->first_busno; bus--)
#else
for (bus = hose->first_busno; bus <= hose->last_busno; bus++)
#endif
- for (bdf = PCI_BDF(bus,0,0);
+ for (bdf = PCI_BDF(bus, 0, 0);
#if defined(CONFIG_ELPPC) || defined(CONFIG_PPMC7XX)
- bdf < PCI_BDF(bus,PCI_MAX_PCI_DEVICES-1,PCI_MAX_PCI_FUNCTIONS-1);
+ bdf < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1,
+ PCI_MAX_PCI_FUNCTIONS - 1);
#else
- bdf < PCI_BDF(bus+1,0,0);
+ bdf < PCI_BDF(bus + 1, 0, 0);
#endif
- bdf += PCI_BDF(0,0,1))
- {
+ bdf += PCI_BDF(0, 0, 1)) {
if (!PCI_FUNC(bdf)) {
pci_read_config_byte(bdf,
PCI_HEADER_TYPE,
@@ -229,19 +229,19 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
PCI_DEVICE_ID,
&device);
- for (i=0; ids[i].vendor != 0; i++)
+ for (i = 0; ids[i].vendor != 0; i++) {
if (vendor == ids[i].vendor &&
- device == ids[i].device)
- {
+ device == ids[i].device) {
if (index <= 0)
return bdf;
index--;
}
+ }
}
}
- return (-1);
+ return -1;
}
pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
@@ -258,7 +258,7 @@ pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
*
*/
-int __pci_hose_phys_to_bus (struct pci_controller *hose,
+int __pci_hose_phys_to_bus(struct pci_controller *hose,
phys_addr_t phys_addr,
unsigned long flags,
unsigned long skip_mask,
@@ -297,12 +297,14 @@ pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose,
int ret;
if (!hose) {
- puts ("pci_hose_phys_to_bus: invalid hose\n");
+ puts("pci_hose_phys_to_bus: invalid hose\n");
return bus_addr;
}
- /* if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set */
+ /*
+ * if PCI_REGION_MEM is set we do a two pass search with preference
+ * on matches that don't have PCI_REGION_SYS_MEMORY set
+ */
if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {
ret = __pci_hose_phys_to_bus(hose, phys_addr,
flags, PCI_REGION_SYS_MEMORY, &bus_addr);
@@ -313,12 +315,12 @@ pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose,
ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr);
if (ret)
- puts ("pci_hose_phys_to_bus: invalid physical address\n");
+ puts("pci_hose_phys_to_bus: invalid physical address\n");
return bus_addr;
}
-int __pci_hose_bus_to_phys (struct pci_controller *hose,
+int __pci_hose_bus_to_phys(struct pci_controller *hose,
pci_addr_t bus_addr,
unsigned long flags,
unsigned long skip_mask,
@@ -354,12 +356,14 @@ phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose,
int ret;
if (!hose) {
- puts ("pci_hose_bus_to_phys: invalid hose\n");
+ puts("pci_hose_bus_to_phys: invalid hose\n");
return phys_addr;
}
- /* if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set */
+ /*
+ * if PCI_REGION_MEM is set we do a two pass search with preference
+ * on matches that don't have PCI_REGION_SYS_MEMORY set
+ */
if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {
ret = __pci_hose_bus_to_phys(hose, bus_addr,
flags, PCI_REGION_SYS_MEMORY, &phys_addr);
@@ -370,7 +374,7 @@ phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose,
ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);
if (ret)
- puts ("pci_hose_bus_to_phys: invalid physical address\n");
+ puts("pci_hose_bus_to_phys: invalid physical address\n");
return phys_addr;
}
@@ -385,20 +389,21 @@ int pci_hose_config_device(struct pci_controller *hose,
pci_addr_t mem,
unsigned long command)
{
- unsigned int bar_response, old_command;
+ u32 bar_response;
+ unsigned int old_command;
pci_addr_t bar_value;
pci_size_t bar_size;
unsigned char pin;
int bar, found_mem64;
- debug ("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n",
- io, (u64)mem, command);
+ debug("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", io,
+ (u64)mem, command);
- pci_hose_write_config_dword (hose, dev, PCI_COMMAND, 0);
+ pci_hose_write_config_dword(hose, dev, PCI_COMMAND, 0);
for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) {
- pci_hose_write_config_dword (hose, dev, bar, 0xffffffff);
- pci_hose_read_config_dword (hose, dev, bar, &bar_response);
+ pci_hose_write_config_dword(hose, dev, bar, 0xffffffff);
+ pci_hose_read_config_dword(hose, dev, bar, &bar_response);
if (!bar_response)
continue;
@@ -418,8 +423,10 @@ int pci_hose_config_device(struct pci_controller *hose,
PCI_BASE_ADDRESS_MEM_TYPE_64) {
u32 bar_response_upper;
u64 bar64;
- pci_hose_write_config_dword(hose, dev, bar+4, 0xffffffff);
- pci_hose_read_config_dword(hose, dev, bar+4, &bar_response_upper);
+ pci_hose_write_config_dword(hose, dev, bar + 4,
+ 0xffffffff);
+ pci_hose_read_config_dword(hose, dev, bar + 4,
+ &bar_response_upper);
bar64 = ((u64)bar_response_upper << 32) | bar_response;
@@ -442,27 +449,28 @@ int pci_hose_config_device(struct pci_controller *hose,
if (found_mem64) {
bar += 4;
#ifdef CONFIG_SYS_PCI_64BIT
- pci_hose_write_config_dword(hose, dev, bar, (u32)(bar_value>>32));
+ pci_hose_write_config_dword(hose, dev, bar,
+ (u32)(bar_value >> 32));
#else
- pci_hose_write_config_dword (hose, dev, bar, 0x00000000);
+ pci_hose_write_config_dword(hose, dev, bar, 0x00000000);
#endif
}
}
/* Configure Cache Line Size Register */
- pci_hose_write_config_byte (hose, dev, PCI_CACHE_LINE_SIZE, 0x08);
+ pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08);
/* Configure Latency Timer */
- pci_hose_write_config_byte (hose, dev, PCI_LATENCY_TIMER, 0x80);
+ pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
/* Disable interrupt line, if device says it wants to use interrupts */
- pci_hose_read_config_byte (hose, dev, PCI_INTERRUPT_PIN, &pin);
+ pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &pin);
if (pin != 0) {
- pci_hose_write_config_byte (hose, dev, PCI_INTERRUPT_LINE, 0xff);
+ pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, 0xff);
}
- pci_hose_read_config_dword (hose, dev, PCI_COMMAND, &old_command);
- pci_hose_write_config_dword (hose, dev, PCI_COMMAND,
+ pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &old_command);
+ pci_hose_write_config_dword(hose, dev, PCI_COMMAND,
(old_command & 0xffff0000) | command);
return 0;
@@ -500,7 +508,8 @@ void pci_cfgfunc_config_device(struct pci_controller *hose,
pci_dev_t dev,
struct pci_config_table *entry)
{
- pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1], entry->priv[2]);
+ pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1],
+ entry->priv[2]);
}
void pci_cfgfunc_do_nothing(struct pci_controller *hose,
@@ -509,10 +518,7 @@ void pci_cfgfunc_do_nothing(struct pci_controller *hose,
}
/*
- *
- */
-
-/* HJF: Changed this to return int. I think this is required
+ * HJF: Changed this to return int. I think this is required
* to get the correct result when scanning bridges
*/
extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
@@ -618,10 +624,12 @@ int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
int pci_hose_scan_bus(struct pci_controller *hose, int bus)
{
- unsigned int sub_bus, found_multi=0;
+ unsigned int sub_bus, found_multi = 0;
unsigned short vendor, device, class;
unsigned char header_type;
+#ifndef CONFIG_PCI_PNP
struct pci_config_table *cfg;
+#endif
pci_dev_t dev;
#ifdef CONFIG_PCI_SCAN_SHOW
static int indent = 0;
@@ -630,8 +638,9 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus)
sub_bus = bus;
for (dev = PCI_BDF(bus,0,0);
- dev < PCI_BDF(bus,PCI_MAX_PCI_DEVICES-1,PCI_MAX_PCI_FUNCTIONS-1);
- dev += PCI_BDF(0,0,1)) {
+ dev < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1,
+ PCI_MAX_PCI_FUNCTIONS - 1);
+ dev += PCI_BDF(0, 0, 1)) {
if (pci_skip_dev(hose, dev))
continue;
@@ -649,8 +658,8 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus)
if (!PCI_FUNC(dev))
found_multi = header_type & 0x80;
- debug ("PCI Scan: Found Bus %d, Device %d, Function %d\n",
- PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev) );
+ debug("PCI Scan: Found Bus %d, Device %d, Function %d\n",
+ PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));
pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device);
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
@@ -668,18 +677,16 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus)
}
#endif
+#ifdef CONFIG_PCI_PNP
+ sub_bus = max(pciauto_config_device(hose, dev), sub_bus);
+#else
cfg = pci_find_config(hose, class, vendor, device,
PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));
if (cfg) {
cfg->config_device(hose, dev, cfg);
sub_bus = max(sub_bus, hose->current_busno);
-#ifdef CONFIG_PCI_PNP
- } else {
- int n = pciauto_config_device(hose, dev);
-
- sub_bus = max(sub_bus, n);
-#endif
}
+#endif
#ifdef CONFIG_PCI_SCAN_SHOW
indent--;
@@ -711,10 +718,11 @@ int pci_hose_scan(struct pci_controller *hose)
}
#endif /* CONFIG_PCI_BOOTDELAY */
- /* Start scan at current_busno.
+ /*
+ * Start scan at current_busno.
* PCIe will start scan at first_busno+1.
*/
- /* For legacy support, ensure current>=first */
+ /* For legacy support, ensure current >= first */
if (hose->first_busno > hose->current_busno)
hose->current_busno = hose->first_busno;
#ifdef CONFIG_PCI_PNP
diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c
index 87ee2c2408..cd78030cd6 100644
--- a/drivers/pci/pci_auto.c
+++ b/drivers/pci/pci_auto.c
@@ -35,7 +35,7 @@
*
*/
-void pciauto_region_init(struct pci_region* res)
+void pciauto_region_init(struct pci_region *res)
{
/*
* Avoid allocating PCI resources from address 0 -- this is illegal
@@ -50,7 +50,8 @@ void pciauto_region_align(struct pci_region *res, pci_size_t size)
res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1;
}
-int pciauto_region_allocate(struct pci_region* res, pci_size_t size, pci_addr_t *bar)
+int pciauto_region_allocate(struct pci_region *res, pci_size_t size,
+ pci_addr_t *bar)
{
pci_addr_t addr;
@@ -88,58 +89,77 @@ void pciauto_setup_device(struct pci_controller *hose,
struct pci_region *prefetch,
struct pci_region *io)
{
- unsigned int bar_response;
- pci_addr_t bar_value;
+ u32 bar_response;
pci_size_t bar_size;
- unsigned int cmdstat = 0;
- struct pci_region *bar_res;
+ u16 cmdstat = 0;
int bar, bar_nr = 0;
+#ifndef CONFIG_PCI_ENUM_ONLY
+ pci_addr_t bar_value;
+ struct pci_region *bar_res;
int found_mem64 = 0;
+#endif
- pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat);
+ pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat);
cmdstat = (cmdstat & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) | PCI_COMMAND_MASTER;
- for (bar = PCI_BASE_ADDRESS_0; bar < PCI_BASE_ADDRESS_0 + (bars_num*4); bar += 4) {
+ for (bar = PCI_BASE_ADDRESS_0;
+ bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) {
/* Tickle the BAR and get the response */
+#ifndef CONFIG_PCI_ENUM_ONLY
pci_hose_write_config_dword(hose, dev, bar, 0xffffffff);
+#endif
pci_hose_read_config_dword(hose, dev, bar, &bar_response);
/* If BAR is not implemented go to the next BAR */
if (!bar_response)
continue;
+#ifndef CONFIG_PCI_ENUM_ONLY
found_mem64 = 0;
+#endif
/* Check the BAR type and set our address mask */
if (bar_response & PCI_BASE_ADDRESS_SPACE) {
bar_size = ((~(bar_response & PCI_BASE_ADDRESS_IO_MASK))
& 0xffff) + 1;
+#ifndef CONFIG_PCI_ENUM_ONLY
bar_res = io;
+#endif
DEBUGF("PCI Autoconfig: BAR %d, I/O, size=0x%llx, ", bar_nr, (u64)bar_size);
} else {
- if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
PCI_BASE_ADDRESS_MEM_TYPE_64) {
u32 bar_response_upper;
u64 bar64;
- pci_hose_write_config_dword(hose, dev, bar+4, 0xffffffff);
- pci_hose_read_config_dword(hose, dev, bar+4, &bar_response_upper);
+
+#ifndef CONFIG_PCI_ENUM_ONLY
+ pci_hose_write_config_dword(hose, dev, bar + 4,
+ 0xffffffff);
+#endif
+ pci_hose_read_config_dword(hose, dev, bar + 4,
+ &bar_response_upper);
bar64 = ((u64)bar_response_upper << 32) | bar_response;
bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1;
+#ifndef CONFIG_PCI_ENUM_ONLY
found_mem64 = 1;
+#endif
} else {
bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1);
}
+#ifndef CONFIG_PCI_ENUM_ONLY
if (prefetch && (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH))
bar_res = prefetch;
else
bar_res = mem;
+#endif
DEBUGF("PCI Autoconfig: BAR %d, Mem, size=0x%llx, ", bar_nr, (u64)bar_size);
}
+#ifndef CONFIG_PCI_ENUM_ONLY
if (pciauto_region_allocate(bar_res, bar_size, &bar_value) == 0) {
/* Write it out and update our limit */
pci_hose_write_config_dword(hose, dev, bar, (u32)bar_value);
@@ -158,16 +178,17 @@ void pciauto_setup_device(struct pci_controller *hose,
#endif
}
- cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ?
- PCI_COMMAND_IO : PCI_COMMAND_MEMORY;
}
+#endif
+ cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ?
+ PCI_COMMAND_IO : PCI_COMMAND_MEMORY;
DEBUGF("\n");
bar_nr++;
}
- pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat);
+ pci_hose_write_config_word(hose, dev, PCI_COMMAND, cmdstat);
pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE,
CONFIG_SYS_PCI_CACHE_LINE_SIZE);
pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
@@ -179,9 +200,9 @@ void pciauto_prescan_setup_bridge(struct pci_controller *hose,
struct pci_region *pci_mem = hose->pci_mem;
struct pci_region *pci_prefetch = hose->pci_prefetch;
struct pci_region *pci_io = hose->pci_io;
- unsigned int cmdstat;
+ u16 cmdstat;
- pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat);
+ pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat);
/* Configure bus number registers */
pci_hose_write_config_byte(hose, dev, PCI_PRIMARY_BUS,
@@ -229,7 +250,8 @@ void pciauto_prescan_setup_bridge(struct pci_controller *hose,
}
/* Enable memory and I/O accesses, enable bus master */
- pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER);
+ pci_hose_write_config_word(hose, dev, PCI_COMMAND,
+ cmdstat | PCI_COMMAND_MASTER);
}
void pciauto_postscan_setup_bridge(struct pci_controller *hose,
@@ -248,7 +270,7 @@ void pciauto_postscan_setup_bridge(struct pci_controller *hose,
pciauto_region_align(pci_mem, 0x100000);
pci_hose_write_config_word(hose, dev, PCI_MEMORY_LIMIT,
- (pci_mem->bus_lower-1) >> 16);
+ (pci_mem->bus_lower - 1) >> 16);
}
if (pci_prefetch) {
@@ -256,7 +278,7 @@ void pciauto_postscan_setup_bridge(struct pci_controller *hose,
pciauto_region_align(pci_prefetch, 0x100000);
pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT,
- (pci_prefetch->bus_lower-1) >> 16);
+ (pci_prefetch->bus_lower - 1) >> 16);
}
if (pci_io) {
@@ -264,9 +286,9 @@ void pciauto_postscan_setup_bridge(struct pci_controller *hose,
pciauto_region_align(pci_io, 0x1000);
pci_hose_write_config_byte(hose, dev, PCI_IO_LIMIT,
- ((pci_io->bus_lower-1) & 0x0000f000) >> 8);
+ ((pci_io->bus_lower - 1) & 0x0000f000) >> 8);
pci_hose_write_config_word(hose, dev, PCI_IO_LIMIT_UPPER16,
- ((pci_io->bus_lower-1) & 0xffff0000) >> 16);
+ ((pci_io->bus_lower - 1) & 0xffff0000) >> 16);
}
}
@@ -280,7 +302,7 @@ void pciauto_config_init(struct pci_controller *hose)
hose->pci_io = hose->pci_mem = NULL;
- for (i=0; i<hose->region_count; i++) {
+ for (i = 0; i < hose->region_count; i++) {
switch(hose->regions[i].flags) {
case PCI_REGION_IO:
if (!hose->pci_io ||
@@ -338,7 +360,8 @@ void pciauto_config_init(struct pci_controller *hose)
}
}
-/* HJF: Changed this to return int. I think this is required
+/*
+ * HJF: Changed this to return int. I think this is required
* to get the correct result when scanning bridges
*/
int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
@@ -350,16 +373,11 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
- switch(class) {
- case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */
- DEBUGF("PCI AutoConfig: Found PowerPC device\n");
- pciauto_setup_device(hose, dev, 6, hose->pci_mem,
- hose->pci_prefetch, hose->pci_io);
- break;
-
+ switch (class) {
case PCI_CLASS_BRIDGE_PCI:
hose->current_busno++;
- pciauto_setup_device(hose, dev, 2, hose->pci_mem, hose->pci_prefetch, hose->pci_io);
+ pciauto_setup_device(hose, dev, 2, hose->pci_mem,
+ hose->pci_prefetch, hose->pci_io);
DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_DEV(dev));
@@ -385,14 +403,20 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
return sub_bus;
}
- pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io);
+ pciauto_setup_device(hose, dev, 6, hose->pci_mem,
+ hose->pci_prefetch, hose->pci_io);
break;
case PCI_CLASS_BRIDGE_CARDBUS:
- /* just do a minimal setup of the bridge, let the OS take care of the rest */
- pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_prefetch, hose->pci_io);
+ /*
+ * just do a minimal setup of the bridge,
+ * let the OS take care of the rest
+ */
+ pciauto_setup_device(hose, dev, 0, hose->pci_mem,
+ hose->pci_prefetch, hose->pci_io);
- DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n", PCI_DEV(dev));
+ DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n",
+ PCI_DEV(dev));
hose->current_busno++;
break;
@@ -412,11 +436,17 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
* the PIMMR window to be allocated (BAR0 - 1MB size)
*/
DEBUGF("PCI Autoconfig: Broken bridge found, only minimal config\n");
- pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_prefetch, hose->pci_io);
+ pciauto_setup_device(hose, dev, 0, hose->pci_mem,
+ hose->pci_prefetch, hose->pci_io);
break;
#endif
+
+ case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */
+ DEBUGF("PCI AutoConfig: Found PowerPC device\n");
+
default:
- pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io);
+ pciauto_setup_device(hose, dev, 6, hose->pci_mem,
+ hose->pci_prefetch, hose->pci_io);
break;
}
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 9cfdbf9753..8316e8f2e4 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -44,6 +44,7 @@ COBJS-$(CONFIG_RTC_DS164x) += ds164x.o
COBJS-$(CONFIG_RTC_DS174x) += ds174x.o
COBJS-$(CONFIG_RTC_DS3231) += ds3231.o
COBJS-$(CONFIG_RTC_FTRTC010) += ftrtc010.o
+COBJS-$(CONFIG_RTC_IMXDI) += imxdi.o
COBJS-$(CONFIG_RTC_ISL1208) += isl1208.o
COBJS-$(CONFIG_RTC_M41T11) += m41t11.o
COBJS-$(CONFIG_RTC_M41T60) += m41t60.o
@@ -58,6 +59,7 @@ COBJS-$(CONFIG_RTC_MK48T59) += mk48t59.o
COBJS-$(CONFIG_RTC_MPC5200) += mpc5xxx.o
COBJS-$(CONFIG_RTC_MPC8xx) += mpc8xx.o
COBJS-$(CONFIG_RTC_MV) += mvrtc.o
+COBJS-$(CONFIG_RTC_MX27) += mx27rtc.o
COBJS-$(CONFIG_RTC_MXS) += mxsrtc.o
COBJS-$(CONFIG_RTC_PCF8563) += pcf8563.o
COBJS-$(CONFIG_RTC_PL031) += pl031.o
diff --git a/drivers/rtc/imxdi.c b/drivers/rtc/imxdi.c
new file mode 100644
index 0000000000..985ce93acf
--- /dev/null
+++ b/drivers/rtc/imxdi.c
@@ -0,0 +1,244 @@
+/*
+ * (C) Copyright 2009-2012 ADVANSEE
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on the Linux rtc-imxdi.c driver, which is:
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ *
+ * 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
+ */
+
+/*
+ * Date & Time support for Freescale i.MX DryIce RTC
+ */
+
+#include <common.h>
+#include <command.h>
+#include <linux/compat.h>
+#include <rtc.h>
+
+#if defined(CONFIG_CMD_DATE)
+
+#include <asm/io.h>
+#include <asm/arch/imx-regs.h>
+
+/* DryIce Register Definitions */
+
+struct imxdi_regs {
+ u32 dtcmr; /* Time Counter MSB Reg */
+ u32 dtclr; /* Time Counter LSB Reg */
+ u32 dcamr; /* Clock Alarm MSB Reg */
+ u32 dcalr; /* Clock Alarm LSB Reg */
+ u32 dcr; /* Control Reg */
+ u32 dsr; /* Status Reg */
+ u32 dier; /* Interrupt Enable Reg */
+};
+
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+
+/* Driver Private Data */
+
+struct imxdi_data {
+ struct imxdi_regs __iomem *regs;
+ int init_done;
+};
+
+static struct imxdi_data data;
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(void)
+{
+ int cnt;
+
+ puts("### Warning: RTC - Register write error!\n");
+
+ /* clear the write error flag */
+ __raw_writel(DSR_WEF, &data.regs->dsr);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ puts("### Error: RTC - Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * Use interrupt flags to determine when the write has completed.
+ */
+#define DI_WRITE_WAIT(val, reg) \
+( \
+ /* do the register write */ \
+ __raw_writel((val), &data.regs->reg), \
+ \
+ di_write_wait((val), #reg) \
+)
+static int di_write_wait(u32 val, const char *reg)
+{
+ int cnt;
+ int ret = 0;
+ int rc = 0;
+
+ /* wait for the write to finish */
+ for (cnt = 0; cnt < 100; cnt++) {
+ if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) {
+ ret = 1;
+ break;
+ }
+ udelay(10);
+ }
+ if (ret == 0)
+ printf("### Warning: RTC - Write-wait timeout "
+ "val = 0x%.8x reg = %s\n", val, reg);
+
+ /* check for write error */
+ if (__raw_readl(&data.regs->dsr) & DSR_WEF) {
+ clear_write_error();
+ rc = -1;
+ }
+
+ return rc;
+}
+
+/*
+ * Initialize dryice hardware
+ */
+static int di_init(void)
+{
+ int rc = 0;
+
+ data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE;
+
+ /* mask all interrupts */
+ __raw_writel(0, &data.regs->dier);
+
+ /* put dryice into valid state */
+ if (__raw_readl(&data.regs->dsr) & DSR_NVF) {
+ rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr);
+ if (rc)
+ goto err;
+ }
+
+ /* initialize alarm */
+ rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr);
+ if (rc)
+ goto err;
+ rc = DI_WRITE_WAIT(0, dcalr);
+ if (rc)
+ goto err;
+
+ /* clear alarm flag */
+ if (__raw_readl(&data.regs->dsr) & DSR_CAF) {
+ rc = DI_WRITE_WAIT(DSR_CAF, dsr);
+ if (rc)
+ goto err;
+ }
+
+ /* the timer won't count if it has never been written to */
+ if (__raw_readl(&data.regs->dtcmr) == 0) {
+ rc = DI_WRITE_WAIT(0, dtcmr);
+ if (rc)
+ goto err;
+ }
+
+ /* start keeping time */
+ if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) {
+ rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr);
+ if (rc)
+ goto err;
+ }
+
+ data.init_done = 1;
+ return 0;
+
+err:
+ return rc;
+}
+
+int rtc_get(struct rtc_time *tmp)
+{
+ unsigned long now;
+ int rc = 0;
+
+ if (!data.init_done) {
+ rc = di_init();
+ if (rc)
+ goto err;
+ }
+
+ now = __raw_readl(&data.regs->dtcmr);
+ to_tm(now, tmp);
+
+err:
+ return rc;
+}
+
+int rtc_set(struct rtc_time *tmp)
+{
+ unsigned long now;
+ int rc;
+
+ if (!data.init_done) {
+ rc = di_init();
+ if (rc)
+ goto err;
+ }
+
+ now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ /* zero the fractional part first */
+ rc = DI_WRITE_WAIT(0, dtclr);
+ if (rc == 0)
+ rc = DI_WRITE_WAIT(now, dtcmr);
+
+err:
+ return rc;
+}
+
+void rtc_reset(void)
+{
+ di_init();
+}
+
+#endif
diff --git a/drivers/rtc/mx27rtc.c b/drivers/rtc/mx27rtc.c
new file mode 100644
index 0000000000..7628dec3a9
--- /dev/null
+++ b/drivers/rtc/mx27rtc.c
@@ -0,0 +1,83 @@
+/*
+ * Freescale i.MX27 RTC Driver
+ *
+ * Copyright (C) 2012 Philippe Reynes <tremyfr@yahoo.fr>
+ *
+ * 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 <rtc.h>
+#include <asm/io.h>
+#include <asm/arch/imx-regs.h>
+
+#define HOUR_SHIFT 8
+#define HOUR_MASK 0x1f
+#define MIN_SHIFT 0
+#define MIN_MASK 0x3f
+
+int rtc_get(struct rtc_time *time)
+{
+ struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE;
+ uint32_t day, hour, min, sec;
+
+ day = readl(&rtc_regs->dayr);
+ hour = readl(&rtc_regs->hourmin);
+ sec = readl(&rtc_regs->seconds);
+
+ min = (hour >> MIN_SHIFT) & MIN_MASK;
+ hour = (hour >> HOUR_SHIFT) & HOUR_MASK;
+
+ sec += min * 60 + hour * 3600 + day * 24 * 3600;
+
+ to_tm(sec, time);
+
+ return 0;
+}
+
+int rtc_set(struct rtc_time *time)
+{
+ struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE;
+ uint32_t day, hour, min, sec;
+
+ sec = mktime(time->tm_year, time->tm_mon, time->tm_mday,
+ time->tm_hour, time->tm_min, time->tm_sec);
+
+ day = sec / (24 * 3600);
+ sec = sec % (24 * 3600);
+ hour = sec / 3600;
+ sec = sec % 3600;
+ min = sec / 60;
+ sec = sec % 60;
+
+ hour = (hour & HOUR_MASK) << HOUR_SHIFT;
+ hour |= (min & MIN_MASK) << MIN_SHIFT;
+
+ writel(day, &rtc_regs->dayr);
+ writel(hour, &rtc_regs->hourmin);
+ writel(sec, &rtc_regs->seconds);
+
+ return 0;
+}
+
+void rtc_reset(void)
+{
+ struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE;
+
+ writel(0, &rtc_regs->dayr);
+ writel(0, &rtc_regs->hourmin);
+ writel(0, &rtc_regs->seconds);
+}
diff --git a/drivers/rtc/mxsrtc.c b/drivers/rtc/mxsrtc.c
index 5beb1a0440..ffefb91881 100644
--- a/drivers/rtc/mxsrtc.c
+++ b/drivers/rtc/mxsrtc.c
@@ -31,7 +31,7 @@
/* Set time in seconds since 1970-01-01 */
int mxs_rtc_set_time(uint32_t secs)
{
- struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE;
+ struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;
int ret;
writel(secs, &rtc_regs->hw_rtc_seconds);
@@ -41,7 +41,7 @@ int mxs_rtc_set_time(uint32_t secs)
* is taken from the linux kernel driver for the STMP37xx RTC since
* documentation doesn't mention it.
*/
- ret = mx28_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg,
+ ret = mxs_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg,
0x80 << RTC_STAT_STALE_REGS_OFFSET, MXS_RTC_MAX_TIMEOUT);
if (ret)
@@ -52,7 +52,7 @@ int mxs_rtc_set_time(uint32_t secs)
int rtc_get(struct rtc_time *time)
{
- struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE;
+ struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;
uint32_t secs;
secs = readl(&rtc_regs->hw_rtc_seconds);
@@ -73,14 +73,14 @@ int rtc_set(struct rtc_time *time)
void rtc_reset(void)
{
- struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE;
+ struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;
int ret;
/* Set time to 1970-01-01 */
mxs_rtc_set_time(0);
/* Reset the RTC block */
- ret = mx28_reset_block(&rtc_regs->hw_rtc_ctrl_reg);
+ ret = mxs_reset_block(&rtc_regs->hw_rtc_ctrl_reg);
if (ret)
printf("MXS RTC: Block reset timeout\n");
}
diff --git a/drivers/rtc/pcf8563.c b/drivers/rtc/pcf8563.c
index 339e5f6080..a028533084 100644
--- a/drivers/rtc/pcf8563.c
+++ b/drivers/rtc/pcf8563.c
@@ -72,7 +72,7 @@ int rtc_get (struct rtc_time *tmp)
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon_cent & 0x1F);
- tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 2000 : 1900);
+ tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 1900 : 2000);
tmp->tm_wday = bcd2bin (wday & 0x07);
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
@@ -94,7 +94,7 @@ int rtc_set (struct rtc_time *tmp)
rtc_write (0x08, bin2bcd(tmp->tm_year % 100));
- century = (tmp->tm_year >= 2000) ? 0x80 : 0;
+ century = (tmp->tm_year >= 2000) ? 0 : 0x80;
rtc_write (0x07, bin2bcd(tmp->tm_mon) | century);
rtc_write (0x06, bin2bcd(tmp->tm_wday));
diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c
index e326b2bcff..943ef70fa6 100644
--- a/drivers/serial/atmel_usart.c
+++ b/drivers/serial/atmel_usart.c
@@ -49,17 +49,26 @@ int serial_init(void)
{
atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE;
+ /*
+ * Just in case: drain transmitter register
+ * 1000us is enough for baudrate >= 9600
+ */
+ if (!(readl(&usart->csr) & USART3_BIT(TXEMPTY)))
+ __udelay(1000);
+
writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr);
serial_setbrg();
- writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr);
writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL)
| USART3_BF(USCLKS, USART3_USCLKS_MCK)
| USART3_BF(CHRL, USART3_CHRL_8)
| USART3_BF(PAR, USART3_PAR_NONE)
| USART3_BF(NBSTOP, USART3_NBSTOP_1)),
&usart->mr);
+ writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr);
+ /* 100us is enough for the new settings to be settled */
+ __udelay(100);
return 0;
}
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
index 0c23955318..facadd2f5c 100644
--- a/drivers/serial/ns16550.c
+++ b/drivers/serial/ns16550.c
@@ -52,7 +52,7 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
serial_out(UART_LCRVAL, &com_port->lcr);
#if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \
- defined(CONFIG_AM33XX)
+ defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX)
#if defined(CONFIG_APTIX)
/* /13 mode so Aptix 6MHz can hit 115200 */
diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
index 0d6ad6283a..b10bab70d0 100644
--- a/drivers/serial/serial.c
+++ b/drivers/serial/serial.c
@@ -84,9 +84,6 @@ static NS16550_t serial_ports[4] = {
};
#define PORT serial_ports[port-1]
-#if defined(CONFIG_CONS_INDEX)
-#define CONSOLE (serial_ports[CONFIG_CONS_INDEX-1])
-#endif
#if defined(CONFIG_SERIAL_MULTI)
diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c
index ed581ae22b..d4c5137092 100644
--- a/drivers/serial/serial_pl01x.c
+++ b/drivers/serial/serial_pl01x.c
@@ -156,6 +156,8 @@ int serial_init (void)
writel(lcr, &regs->fr);
writel(lcr, &regs->pl011_rlcr);
+ /* lcrh needs to be set again for change to be effective */
+ writel(lcr, &regs->pl011_lcrh);
}
#endif
/* Finally, enable the UART */
diff --git a/drivers/serial/serial_xuartlite.c b/drivers/serial/serial_xuartlite.c
index 3a38f9e327..2bdb68ba4a 100644
--- a/drivers/serial/serial_xuartlite.c
+++ b/drivers/serial/serial_xuartlite.c
@@ -89,11 +89,17 @@ int uartlite_serial_tstc(const int port)
return in_be32(&regs->status) & SR_RX_FIFO_VALID_DATA;
}
+static int uartlite_serial_init(const int port)
+{
+ if (userial_ports[port])
+ return 0;
+ return -1;
+}
+
#if !defined(CONFIG_SERIAL_MULTI)
int serial_init(void)
{
- /* FIXME: Nothing for now. We should initialize fifo, etc */
- return 0;
+ return uartlite_serial_init(0);
}
void serial_setbrg(void)
@@ -126,7 +132,7 @@ int serial_tstc(void)
/* Multi serial device functions */
#define DECLARE_ESERIAL_FUNCTIONS(port) \
int userial##port##_init(void) \
- { return(0); } \
+ { return uartlite_serial_init(port); } \
void userial##port##_setbrg(void) {} \
int userial##port##_getc(void) \
{ return uartlite_serial_getc(port); } \
@@ -163,17 +169,15 @@ struct serial_device uartlite_serial3_device =
__weak struct serial_device *default_serial_console(void)
{
-# ifdef XILINX_UARTLITE_BASEADDR
- return &uartlite_serial0_device;
-# endif /* XILINX_UARTLITE_BASEADDR */
-# ifdef XILINX_UARTLITE_BASEADDR1
- return &uartlite_serial1_device;
-# endif /* XILINX_UARTLITE_BASEADDR1 */
-# ifdef XILINX_UARTLITE_BASEADDR2
- return &uartlite_serial2_device;
-# endif /* XILINX_UARTLITE_BASEADDR2 */
-# ifdef XILINX_UARTLITE_BASEADDR3
- return &uartlite_serial3_device;
-# endif /* XILINX_UARTLITE_BASEADDR3 */
+ if (userial_ports[0])
+ return &uartlite_serial0_device;
+ if (userial_ports[1])
+ return &uartlite_serial1_device;
+ if (userial_ports[2])
+ return &uartlite_serial2_device;
+ if (userial_ports[3])
+ return &uartlite_serial3_device;
+
+ return NULL;
}
#endif /* CONFIG_SERIAL_MULTI */
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 80b981f10f..f0b82c67f5 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -32,6 +32,7 @@ COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o
COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o
COBJS-$(CONFIG_CF_SPI) += cf_spi.o
+COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 83ef8e8b19..c7a51f7f39 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -92,6 +92,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
as->slave.cs = cs;
as->regs = regs;
as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
+#if defined(CONFIG_AT91SAM9X5)
+ | ATMEL_SPI_MR_WDRBT
+#endif
| ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
spi_writel(as, CSR(cs), csrx);
diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h
index 8b69a6d215..057de9adee 100644
--- a/drivers/spi/atmel_spi.h
+++ b/drivers/spi/atmel_spi.h
@@ -26,6 +26,7 @@
#define ATMEL_SPI_MR_PCSDEC (1 << 2)
#define ATMEL_SPI_MR_FDIV (1 << 3)
#define ATMEL_SPI_MR_MODFDIS (1 << 4)
+#define ATMEL_SPI_MR_WDRBT (1 << 5)
#define ATMEL_SPI_MR_LLB (1 << 7)
#define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16)
#define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24)
diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c
new file mode 100644
index 0000000000..72dd1a520d
--- /dev/null
+++ b/drivers/spi/cf_qspi.c
@@ -0,0 +1,373 @@
+/*
+ * Freescale Coldfire Queued SPI driver
+ *
+ * NOTE:
+ * This driver is written to transfer 8 bit at-a-time and uses the dedicated
+ * SPI slave select pins as bit-banged GPIO to work with spi_flash subsystem.
+ *
+ *
+ * Copyright (C) 2011 Ruggedcom, Inc.
+ * Richard Retanubun (richardretanubun@freescale.com)
+ *
+ * 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 <malloc.h>
+#include <spi.h>
+#include <asm/immap.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define clamp(x, low, high) (min(max(low, x), high))
+#define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, s)
+
+struct cf_qspi_slave {
+ struct spi_slave slave; /* Specific bus:cs ID for each device */
+ qspi_t *regs; /* Pointer to SPI controller registers */
+ u16 qmr; /* QMR: Queued Mode Register */
+ u16 qwr; /* QWR: Queued Wrap Register */
+ u16 qcr; /* QCR: Queued Command Ram */
+};
+
+/* Register write wrapper functions */
+static void write_qmr(volatile qspi_t *qspi, u16 val) { qspi->mr = val; }
+static void write_qdlyr(volatile qspi_t *qspi, u16 val) { qspi->dlyr = val; }
+static void write_qwr(volatile qspi_t *qspi, u16 val) { qspi->wr = val; }
+static void write_qir(volatile qspi_t *qspi, u16 val) { qspi->ir = val; }
+static void write_qar(volatile qspi_t *qspi, u16 val) { qspi->ar = val; }
+static void write_qdr(volatile qspi_t *qspi, u16 val) { qspi->dr = val; }
+/* Register read wrapper functions */
+static u16 read_qdlyr(volatile qspi_t *qspi) { return qspi->dlyr; }
+static u16 read_qwr(volatile qspi_t *qspi) { return qspi->wr; }
+static u16 read_qir(volatile qspi_t *qspi) { return qspi->ir; }
+static u16 read_qdr(volatile qspi_t *qspi) { return qspi->dr; }
+
+/* These call points may be different for each ColdFire CPU */
+extern void cfspi_port_conf(void);
+static void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high);
+static void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high);
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ return 0;
+}
+void spi_release_bus(struct spi_slave *slave)
+{
+}
+
+__attribute__((weak))
+void spi_init(void)
+{
+ cfspi_port_conf();
+}
+
+__attribute__((weak))
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+
+ cfspi_cs_activate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV));
+}
+
+__attribute__((weak))
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+
+ cfspi_cs_deactivate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV));
+}
+
+__attribute__((weak))
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ /* Only 1 bus and 4 chipselect per controller */
+ if (bus == 0 && (cs >= 0 && cs < 4))
+ return 1;
+ else
+ return 0;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+
+ free(dev);
+}
+
+/* Translate information given by spi_setup_slave to members of cf_qspi_slave */
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct cf_qspi_slave *dev = NULL;
+
+ if (!spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ dev = malloc(sizeof(struct cf_qspi_slave));
+ if (!dev)
+ return NULL;
+
+ /* Initialize to known value */
+ dev->slave.bus = bus;
+ dev->slave.cs = cs;
+ dev->regs = (qspi_t *)MMAP_QSPI;
+ dev->qmr = 0;
+ dev->qwr = 0;
+ dev->qcr = 0;
+
+
+ /* Map max_hz to QMR[BAUD] */
+ if (max_hz == 0) /* Go as fast as possible */
+ dev->qmr = 2u;
+ else /* Get the closest baud rate */
+ dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz,
+ 2u, 255u);
+
+ /* Map mode to QMR[CPOL] and QMR[CPHA] */
+ if (mode & SPI_CPOL)
+ dev->qmr |= QSPI_QMR_CPOL;
+
+ if (mode & SPI_CPHA)
+ dev->qmr |= QSPI_QMR_CPHA;
+
+ /* Hardcode bit length to 8 bit per transter */
+ dev->qmr |= QSPI_QMR_BITS_8;
+
+ /* Set QMR[MSTR] to enable QSPI as master */
+ dev->qmr |= QSPI_QMR_MSTR;
+
+ /*
+ * Set QCR and QWR to default values for spi flash operation.
+ * If more custom QCR and QRW are needed, overload mode variable
+ */
+ dev->qcr = (QSPI_QDR_CONT | QSPI_QDR_BITSE);
+
+ if (!(mode & SPI_CS_HIGH))
+ dev->qwr |= QSPI_QWR_CSIV;
+
+ return &dev->slave;
+}
+
+/* Transfer 8 bit at a time */
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
+{
+ struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
+ volatile qspi_t *qspi = dev->regs;
+ u8 *txbuf = (u8 *)dout;
+ u8 *rxbuf = (u8 *)din;
+ u32 count = ((bitlen / 8) + (bitlen % 8 ? 1 : 0));
+ u32 n, i = 0;
+
+ /* Sanitize arguments */
+ if (slave == NULL) {
+ printf("%s: NULL slave ptr\n", __func__);
+ return -1;
+ }
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ /* There is something to send, lets process it. spi_xfer is also called
+ * just to toggle chip select, so bitlen of 0 is valid */
+ if (count > 0) {
+ /*
+ * NOTE: Since chip select is driven as a bit-bang-ed GPIO
+ * using spi_cs_activate() and spi_cs_deactivate(),
+ * the chip select settings inside the controller
+ * (i.e. QCR[CONT] and QWR[CSIV]) are moot. The bits are set to
+ * keep the controller settings consistent with the actual
+ * operation of the bus.
+ */
+
+ /* Write the slave device's settings for the controller.*/
+ write_qmr(qspi, dev->qmr);
+ write_qwr(qspi, dev->qwr);
+
+ /* Limit transfer to 16 at a time */
+ n = min(count, 16u);
+ do {
+ /* Setup queue end point */
+ write_qwr(qspi, ((read_qwr(qspi) & QSPI_QWR_ENDQP_MASK)
+ | QSPI_QWR_ENDQP((n-1))));
+
+ /* Write Command RAM */
+ write_qar(qspi, QSPI_QAR_CMD);
+ for (i = 0; i < n; ++i)
+ write_qdr(qspi, dev->qcr);
+
+ /* Write TxBuf, if none given, fill with ZEROes */
+ write_qar(qspi, QSPI_QAR_TRANS);
+ if (txbuf) {
+ for (i = 0; i < n; ++i)
+ write_qdr(qspi, *txbuf++);
+ } else {
+ for (i = 0; i < n; ++i)
+ write_qdr(qspi, 0);
+ }
+
+ /* Clear QIR[SPIF] by writing a 1 to it */
+ write_qir(qspi, read_qir(qspi) | QSPI_QIR_SPIF);
+ /* Set QDLYR[SPE] to start sending */
+ write_qdlyr(qspi, read_qdlyr(qspi) | QSPI_QDLYR_SPE);
+
+ /* Poll QIR[SPIF] for transfer completion */
+ while ((read_qir(qspi) & QSPI_QIR_SPIF) != 1)
+ udelay(1);
+
+ /* If given read RxBuf, load data to it */
+ if (rxbuf) {
+ write_qar(qspi, QSPI_QAR_RECV);
+ for (i = 0; i < n; ++i)
+ *rxbuf++ = read_qdr(qspi);
+ }
+
+ /* Decrement count */
+ count -= n;
+ } while (count);
+ }
+
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
+
+/* Each MCF CPU may have different pin assignments for chip selects. */
+#if defined(CONFIG_M5271)
+/* Assert chip select, val = [1|0] , dir = out, mode = GPIO */
+void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high)
+{
+ debug("%s: bus %d cs %d cs_active_high %d\n",
+ __func__, bus, cs, cs_active_high);
+
+ switch (cs) {
+ case 0: /* QSPI_CS[0] = PQSPI[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x08);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF);
+ break;
+ case 1: /* QSPI_CS[1] = PQSPI[4] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x10);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F);
+ break;
+ case 2: /* QSPI_CS[2] = PTIMER[7] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x80);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF);
+ break;
+ case 3: /* QSPI_CS[3] = PTIMER[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08);
+ else
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x08);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F);
+ break;
+ }
+}
+
+/* Deassert chip select, val = [1|0], dir = in, mode = GPIO
+ * direction set as IN to undrive the pin, external pullup/pulldown will bring
+ * bus to deassert state.
+ */
+void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high)
+{
+ debug("%s: bus %d cs %d cs_active_high %d\n",
+ __func__, bus, cs, cs_active_high);
+
+ switch (cs) {
+ case 0: /* QSPI_CS[0] = PQSPI[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xF7);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF);
+ break;
+ case 1: /* QSPI_CS[1] = PQSPI[4] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10);
+
+ mbar_writeByte(MCF_GPIO_PDDR_QSPI,
+ mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xEF);
+
+ mbar_writeByte(MCF_GPIO_PAR_QSPI,
+ mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F);
+ break;
+ case 2: /* QSPI_CS[2] = PTIMER[7] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0x7F);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF);
+ break;
+ case 3: /* QSPI_CS[3] = PTIMER[3] */
+ if (cs_active_high)
+ mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7);
+ else
+ mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08);
+
+ mbar_writeByte(MCF_GPIO_PDDR_TIMER,
+ mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0xF7);
+
+ mbar_writeShort(MCF_GPIO_PAR_TIMER,
+ mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F);
+ break;
+ }
+}
+#endif /* CONFIG_M5271 */
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index f4523a3929..a7cda751bd 100644
--- a/drivers/spi/kirkwood_spi.c
+++ b/drivers/spi/kirkwood_spi.c
@@ -56,8 +56,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl);
/* calculate spi clock prescaller using max_hz */
- data = ((CONFIG_SYS_TCLK / 2) / max_hz) & KWSPI_CLKPRESCL_MASK;
- data |= 0x10;
+ data = ((CONFIG_SYS_TCLK / 2) / max_hz) + 0x10;
+ data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data;
+ data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;
/* program spi clock prescaller using max_hz */
writel(KWSPI_ADRLEN_3BYTE | data, &spireg->cfg);
diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c
index 44ab39dd3f..4e46041dff 100644
--- a/drivers/spi/mpc8xxx_spi.c
+++ b/drivers/spi/mpc8xxx_spi.c
@@ -124,6 +124,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
* len > 16 0
*/
+ spi->mode &= ~SPI_MODE_EN;
+
if (bitlen <= 16) {
if (bitlen <= 4)
spi->mode = (spi->mode & 0xff0fffff) |
@@ -138,6 +140,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
dout += 4;
}
+ spi->mode |= SPI_MODE_EN;
+
spi->tx = tmpdout; /* Write the data out */
debug("*** spi_xfer: ... %08x written\n", tmpdout);
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index 2e1531858d..13bebe8ac1 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -96,7 +96,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,
clk_src = mxc_get_clock(MXC_CSPI_CLK);
- div = clk_src / max_hz;
+ div = DIV_ROUND_UP(clk_src, max_hz);
div = get_cspi_div(div);
debug("clk %d Hz, div %d, real clk %d Hz\n",
@@ -147,7 +147,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,
* The following computation is taken directly from Freescale's code.
*/
if (clk_src > max_hz) {
- pre_div = clk_src / max_hz;
+ pre_div = DIV_ROUND_UP(clk_src, max_hz);
if (pre_div > 16) {
post_div = pre_div / 16;
pre_div = 15;
@@ -408,7 +408,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
if (bus >= ARRAY_SIZE(spi_bases))
return NULL;
- mxcs = malloc(sizeof(struct mxc_spi_slave));
+ mxcs = calloc(sizeof(struct mxc_spi_slave), 1);
if (!mxcs) {
puts("mxc_spi: SPI Slave not allocated !\n");
return NULL;
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 7859536a67..42e4c9952e 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -31,17 +31,31 @@
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
+#include <asm/arch/dma.h>
#define MXS_SPI_MAX_TIMEOUT 1000000
#define MXS_SPI_PORT_OFFSET 0x2000
#define MXS_SSP_CHIPSELECT_MASK 0x00300000
#define MXS_SSP_CHIPSELECT_SHIFT 20
+#define MXSSSP_SMALL_TRANSFER 512
+
+/*
+ * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI
+ * host. Use with utmost caution!
+ *
+ * Enabling this is not yet recommended since this
+ * still doesn't support transfers to/from unaligned
+ * addresses. Therefore this driver will not work
+ * for example with saving environment. This is
+ * caused by DMA alignment constraints on MXS.
+ */
+
struct mxs_spi_slave {
struct spi_slave slave;
uint32_t max_khz;
uint32_t mode;
- struct mx28_ssp_regs *regs;
+ struct mxs_ssp_regs *regs;
};
static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
@@ -67,7 +81,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
{
struct mxs_spi_slave *mxs_slave;
uint32_t addr;
- struct mx28_ssp_regs *ssp_regs;
+ struct mxs_ssp_regs *ssp_regs;
int reg;
if (!spi_cs_is_valid(bus, cs)) {
@@ -75,17 +89,20 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- mxs_slave = malloc(sizeof(struct mxs_spi_slave));
+ mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1);
if (!mxs_slave)
return NULL;
+ if (mxs_dma_init_channel(bus))
+ goto err_init;
+
addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET);
mxs_slave->slave.bus = bus;
mxs_slave->slave.cs = cs;
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
- mxs_slave->regs = (struct mx28_ssp_regs *)addr;
+ mxs_slave->regs = (struct mxs_ssp_regs *)addr;
ssp_regs = mxs_slave->regs;
reg = readl(&ssp_regs->hw_ssp_ctrl0);
@@ -94,6 +111,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
writel(reg, &ssp_regs->hw_ssp_ctrl0);
return &mxs_slave->slave;
+
+err_init:
+ free(mxs_slave);
+ return NULL;
}
void spi_free_slave(struct spi_slave *slave)
@@ -105,10 +126,10 @@ void spi_free_slave(struct spi_slave *slave)
int spi_claim_bus(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
- struct mx28_ssp_regs *ssp_regs = mxs_slave->regs;
+ struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
uint32_t reg = 0;
- mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
+ mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0);
@@ -128,79 +149,63 @@ void spi_release_bus(struct spi_slave *slave)
{
}
-static void mxs_spi_start_xfer(struct mx28_ssp_regs *ssp_regs)
+static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);
}
-static void mxs_spi_end_xfer(struct mx28_ssp_regs *ssp_regs)
+static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
}
-int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
+static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
+ char *data, int length, int write, unsigned long flags)
{
- struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
- struct mx28_ssp_regs *ssp_regs = mxs_slave->regs;
- int len = bitlen / 8;
- const char *tx = dout;
- char *rx = din;
- char dummy;
-
- if (bitlen == 0) {
- if (flags & SPI_XFER_END) {
- rx = &dummy;
- len = 1;
- } else
- return 0;
- }
-
- if (!rx && !tx)
- return 0;
+ struct mxs_ssp_regs *ssp_regs = slave->regs;
if (flags & SPI_XFER_BEGIN)
mxs_spi_start_xfer(ssp_regs);
- while (len--) {
+ while (length--) {
/* We transfer 1 byte */
writel(1, &ssp_regs->hw_ssp_xfer_size);
- if ((flags & SPI_XFER_END) && !len)
+ if ((flags & SPI_XFER_END) && !length)
mxs_spi_end_xfer(ssp_regs);
- if (tx)
+ if (write)
writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
else
writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set);
writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);
- if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
+ if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for start\n");
return -ETIMEDOUT;
}
- if (tx)
- writel(*tx++, &ssp_regs->hw_ssp_data);
+ if (write)
+ writel(*data++, &ssp_regs->hw_ssp_data);
writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);
- if (rx) {
- if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
+ if (!write) {
+ if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for data\n");
return -ETIMEDOUT;
}
- *rx = readl(&ssp_regs->hw_ssp_data);
- rx++;
+ *data = readl(&ssp_regs->hw_ssp_data);
+ data++;
}
- if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
+ if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for finish\n");
return -ETIMEDOUT;
@@ -209,3 +214,174 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
return 0;
}
+
+static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
+ char *data, int length, int write, unsigned long flags)
+{
+ const int xfer_max_sz = 0xff00;
+ const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1;
+ struct mxs_ssp_regs *ssp_regs = slave->regs;
+ struct mxs_dma_desc *dp;
+ uint32_t ctrl0;
+ uint32_t cache_data_count;
+ const uint32_t dstart = (uint32_t)data;
+ int dmach;
+ int tl;
+ int ret = 0;
+
+ ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count);
+
+ memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count);
+
+ ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0);
+ ctrl0 |= SSP_CTRL0_DATA_XFER;
+
+ if (flags & SPI_XFER_BEGIN)
+ ctrl0 |= SSP_CTRL0_LOCK_CS;
+ if (!write)
+ ctrl0 |= SSP_CTRL0_READ;
+
+ if (length % ARCH_DMA_MINALIGN)
+ cache_data_count = roundup(length, ARCH_DMA_MINALIGN);
+ else
+ cache_data_count = length;
+
+ /* Flush data to DRAM so DMA can pick them up */
+ if (write)
+ flush_dcache_range(dstart, dstart + cache_data_count);
+
+ /* Invalidate the area, so no writeback into the RAM races with DMA */
+ invalidate_dcache_range(dstart, dstart + cache_data_count);
+
+ dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus;
+
+ dp = desc;
+ while (length) {
+ dp->address = (dma_addr_t)dp;
+ dp->cmd.address = (dma_addr_t)data;
+
+ /*
+ * This is correct, even though it does indeed look insane.
+ * I hereby have to, wholeheartedly, thank Freescale Inc.,
+ * for always inventing insane hardware and keeping me busy
+ * and employed ;-)
+ */
+ if (write)
+ dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
+ else
+ dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
+
+ /*
+ * The DMA controller can transfer large chunks (64kB) at
+ * time by setting the transfer length to 0. Setting tl to
+ * 0x10000 will overflow below and make .data contain 0.
+ * Otherwise, 0xff00 is the transfer maximum.
+ */
+ if (length >= 0x10000)
+ tl = 0x10000;
+ else
+ tl = min(length, xfer_max_sz);
+
+ dp->cmd.data |=
+ ((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) |
+ (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
+ MXS_DMA_DESC_HALT_ON_TERMINATE |
+ MXS_DMA_DESC_TERMINATE_FLUSH;
+
+ data += tl;
+ length -= tl;
+
+ if (!length) {
+ dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM;
+
+ if (flags & SPI_XFER_END) {
+ ctrl0 &= ~SSP_CTRL0_LOCK_CS;
+ ctrl0 |= SSP_CTRL0_IGNORE_CRC;
+ }
+ }
+
+ /*
+ * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is
+ * essential that the XFER_SIZE register is written on
+ * a per-descriptor basis with the same size as is the
+ * descriptor!
+ */
+ dp->cmd.pio_words[0] = ctrl0;
+ dp->cmd.pio_words[1] = 0;
+ dp->cmd.pio_words[2] = 0;
+ dp->cmd.pio_words[3] = tl;
+
+ mxs_dma_desc_append(dmach, dp);
+
+ dp++;
+ }
+
+ if (mxs_dma_go(dmach))
+ ret = -EINVAL;
+
+ /* The data arrived into DRAM, invalidate cache over them */
+ if (!write)
+ invalidate_dcache_range(dstart, dstart + cache_data_count);
+
+ return ret;
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
+ struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
+ int len = bitlen / 8;
+ char dummy;
+ int write = 0;
+ char *data = NULL;
+
+#ifdef CONFIG_MXS_SPI_DMA_ENABLE
+ int dma = 1;
+#else
+ int dma = 0;
+#endif
+
+ if (bitlen == 0) {
+ if (flags & SPI_XFER_END) {
+ din = (void *)&dummy;
+ len = 1;
+ } else
+ return 0;
+ }
+
+ /* Half-duplex only */
+ if (din && dout)
+ return -EINVAL;
+ /* No data */
+ if (!din && !dout)
+ return 0;
+
+ if (dout) {
+ data = (char *)dout;
+ write = 1;
+ } else if (din) {
+ data = (char *)din;
+ write = 0;
+ }
+
+ /*
+ * Check for alignment, if the buffer is aligned, do DMA transfer,
+ * PIO otherwise. This is a temporary workaround until proper bounce
+ * buffer is in place.
+ */
+ if (dma) {
+ if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1))
+ dma = 0;
+ if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1))
+ dma = 0;
+ }
+
+ if (!dma || (len < MXSSSP_SMALL_TRANSFER)) {
+ writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
+ return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags);
+ } else {
+ writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
+ return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags);
+ }
+}
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 9346c0b5b4..e40a632caa 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -86,15 +86,21 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
case 0:
ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
break;
+#ifdef OMAP3_MCSPI2_BASE
case 1:
ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
break;
+#endif
+#ifdef OMAP3_MCSPI3_BASE
case 2:
ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
break;
+#endif
+#ifdef OMAP3_MCSPI4_BASE
case 3:
ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
break;
+#endif
default:
printf("SPI error: unsupported bus %i. \
Supported busses 0 - 3\n", bus);
@@ -167,8 +173,18 @@ int spi_claim_bus(struct spi_slave *slave)
/* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
* REVISIT: this controller could support SPI_3WIRE mode.
*/
+#ifdef CONFIG_AM33XX
+ /*
+ * The reference design on AM33xx has D0 and D1 wired up opposite
+ * of how it has been done on previous platforms. We assume that
+ * custom hardware will also follow this convention.
+ */
+ conf &= OMAP3_MCSPI_CHCONF_DPE0;
+ conf |= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
+#else
conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
conf |= OMAP3_MCSPI_CHCONF_DPE0;
+#endif
/* wordlength */
conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h
index 0ac801cb25..bffa43cb6c 100644
--- a/drivers/spi/omap3_spi.h
+++ b/drivers/spi/omap3_spi.h
@@ -30,10 +30,15 @@
#ifndef _OMAP3_SPI_H_
#define _OMAP3_SPI_H_
+#ifdef CONFIG_AM33XX
+#define OMAP3_MCSPI1_BASE 0x48030100
+#define OMAP3_MCSPI2_BASE 0x481A0100
+#else
#define OMAP3_MCSPI1_BASE 0x48098000
#define OMAP3_MCSPI2_BASE 0x4809A000
#define OMAP3_MCSPI3_BASE 0x480B8000
#define OMAP3_MCSPI4_BASE 0x480BA000
+#endif
#define OMAP3_MCSPI_MAX_FREQ 48000000
diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c
index 4a3e7996f9..18b00b2cae 100644
--- a/drivers/spi/tegra_spi.c
+++ b/drivers/spi/tegra_spi.c
@@ -54,7 +54,7 @@ static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave)
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
- /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */
+ /* Tegra20 SPI-Flash - only 1 device ('bus/cs') */
if (bus != 0 || cs != 0)
return 0;
else
@@ -72,9 +72,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
- if (max_hz > TEGRA2_SPI_MAX_FREQ) {
+ if (max_hz > TEGRA_SPI_MAX_FREQ) {
printf("SPI error: unsupported frequency %d Hz. Max frequency"
- " is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ);
+ " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ);
return NULL;
}
@@ -86,7 +86,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
spi->slave.bus = bus;
spi->slave.cs = cs;
spi->freq = max_hz;
- spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE;
+ spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE;
spi->mode = mode;
return &spi->slave;
@@ -130,7 +130,7 @@ int spi_claim_bus(struct spi_slave *slave)
debug("spi_init: COMMAND = %08x\n", readl(&regs->command));
/*
- * SPI pins on Tegra2 are muxed - change pinmux later due to UART
+ * SPI pins on Tegra20 are muxed - change pinmux later due to UART
* issue.
*/
pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH);
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index e563c19051..52a4134f18 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -78,7 +78,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct xilinx_spi_slave *xilspi;
- struct xilinx_spi_reg *regs;
if (!spi_cs_is_valid(bus, cs)) {
printf("XILSPI error: %s: unsupported bus %d / cs %d\n",
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 87d1918cb9..5bbdd368f4 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -29,6 +29,8 @@ LIB := $(obj)libusb_gadget.o
ifdef CONFIG_USB_GADGET
COBJS-y += epautoconf.o config.o usbstring.o
COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
+COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o
+COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o
endif
ifdef CONFIG_USB_ETHER
COBJS-y += ether.o epautoconf.o config.o usbstring.o
diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c
new file mode 100644
index 0000000000..3ec4c65e74
--- /dev/null
+++ b/drivers/usb/gadget/f_dfu.c
@@ -0,0 +1,749 @@
+/*
+ * f_dfu.c -- Device Firmware Update USB function
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * 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 <errno.h>
+#include <common.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <usbdescriptors.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include <dfu.h>
+#include "f_dfu.h"
+
+struct f_dfu {
+ struct usb_function usb_function;
+
+ struct usb_descriptor_header **function;
+ struct usb_string *strings;
+
+ /* when configured, we have one config */
+ u8 config;
+ u8 altsetting;
+ enum dfu_state dfu_state;
+ unsigned int dfu_status;
+
+ /* Send/received block number is handy for data integrity check */
+ int blk_seq_num;
+};
+
+typedef int (*dfu_state_fn) (struct f_dfu *,
+ const struct usb_ctrlrequest *,
+ struct usb_gadget *,
+ struct usb_request *);
+
+static inline struct f_dfu *func_to_dfu(struct usb_function *f)
+{
+ return container_of(f, struct f_dfu, usb_function);
+}
+
+static const struct dfu_function_descriptor dfu_func = {
+ .bLength = sizeof dfu_func,
+ .bDescriptorType = DFU_DT_FUNC,
+ .bmAttributes = DFU_BIT_WILL_DETACH |
+ DFU_BIT_MANIFESTATION_TOLERANT |
+ DFU_BIT_CAN_UPLOAD |
+ DFU_BIT_CAN_DNLOAD,
+ .wDetachTimeOut = 0,
+ .wTransferSize = DFU_USB_BUFSIZ,
+ .bcdDFUVersion = __constant_cpu_to_le16(0x0110),
+};
+
+static struct usb_interface_descriptor dfu_intf_runtime = {
+ .bLength = sizeof dfu_intf_runtime,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_APP_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_descriptor_header *dfu_runtime_descs[] = {
+ (struct usb_descriptor_header *) &dfu_intf_runtime,
+ NULL,
+};
+
+static const struct usb_qualifier_descriptor dev_qualifier = {
+ .bLength = sizeof dev_qualifier,
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bNumConfigurations = 1,
+};
+
+static const char dfu_name[] = "Device Firmware Upgrade";
+
+/*
+ * static strings, in UTF-8
+ *
+ * dfu_generic configuration
+ */
+static struct usb_string strings_dfu_generic[] = {
+ [0].s = dfu_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dfu_generic = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dfu_generic,
+};
+
+static struct usb_gadget_strings *dfu_generic_strings[] = {
+ &stringtab_dfu_generic,
+ NULL,
+};
+
+/*
+ * usb_function specific
+ */
+static struct usb_gadget_strings stringtab_dfu = {
+ .language = 0x0409, /* en-us */
+ /*
+ * .strings
+ *
+ * assigned during initialization,
+ * depends on number of flash entities
+ *
+ */
+};
+
+static struct usb_gadget_strings *dfu_strings[] = {
+ &stringtab_dfu,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
+ req->length, f_dfu->blk_seq_num);
+
+ if (req->length == 0)
+ puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
+}
+
+static void handle_getstatus(struct usb_request *req)
+{
+ struct dfu_status *dstat = (struct dfu_status *)req->buf;
+ struct f_dfu *f_dfu = req->context;
+
+ switch (f_dfu->dfu_state) {
+ case DFU_STATE_dfuDNLOAD_SYNC:
+ case DFU_STATE_dfuDNBUSY:
+ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+ break;
+ case DFU_STATE_dfuMANIFEST_SYNC:
+ break;
+ default:
+ break;
+ }
+
+ /* send status response */
+ dstat->bStatus = f_dfu->dfu_status;
+ dstat->bState = f_dfu->dfu_state;
+ dstat->iString = 0;
+}
+
+static void handle_getstate(struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ ((u8 *)req->buf)[0] = f_dfu->dfu_state;
+ req->actual = sizeof(u8);
+}
+
+static inline void to_dfu_mode(struct f_dfu *f_dfu)
+{
+ f_dfu->usb_function.strings = dfu_strings;
+ f_dfu->usb_function.hs_descriptors = f_dfu->function;
+}
+
+static inline void to_runtime_mode(struct f_dfu *f_dfu)
+{
+ f_dfu->usb_function.strings = NULL;
+ f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
+}
+
+static int handle_upload(struct usb_request *req, u16 len)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf,
+ req->length, f_dfu->blk_seq_num);
+}
+
+static int handle_dnload(struct usb_gadget *gadget, u16 len)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_request *req = cdev->req;
+ struct f_dfu *f_dfu = req->context;
+
+ if (len == 0)
+ f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+ req->complete = dnload_request_complete;
+
+ return len;
+}
+
+/*-------------------------------------------------------------------------*/
+/* DFU state machine */
+static int state_app_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ case USB_REQ_DFU_DETACH:
+ f_dfu->dfu_state = DFU_STATE_appDETACH;
+ to_dfu_mode(f_dfu);
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ value = RET_ZLP;
+ break;
+ default:
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_app_detach(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_appIDLE;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_DNLOAD:
+ if (len == 0) {
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ f_dfu->blk_seq_num = w_value;
+ value = handle_dnload(gadget, len);
+ break;
+ case USB_REQ_DFU_UPLOAD:
+ f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+ f_dfu->blk_seq_num = 0;
+ value = handle_upload(req, len);
+ break;
+ case USB_REQ_DFU_ABORT:
+ /* no zlp? */
+ value = RET_ZLP;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ case USB_REQ_DFU_DETACH:
+ /*
+ * Proprietary extension: 'detach' from idle mode and
+ * get back to runtime mode in case of USB Reset. As
+ * much as I dislike this, we just can't use every USB
+ * bus reset to switch back to runtime mode, since at
+ * least the Linux USB stack likes to send a number of
+ * resets in a row :(
+ */
+ f_dfu->dfu_state =
+ DFU_STATE_dfuMANIFEST_WAIT_RST;
+ to_runtime_mode(f_dfu);
+ f_dfu->dfu_state = DFU_STATE_appIDLE;
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_dnload_sync(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_dnbusy(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_dnload_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_DNLOAD:
+ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ f_dfu->blk_seq_num = w_value;
+ value = handle_dnload(gadget, len);
+ break;
+ case USB_REQ_DFU_ABORT:
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ value = RET_ZLP;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_manifest_sync(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ /* We're MainfestationTolerant */
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ handle_getstatus(req);
+ f_dfu->blk_seq_num = 0;
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_upload_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_UPLOAD:
+ /* state transition if less data then requested */
+ f_dfu->blk_seq_num = w_value;
+ value = handle_upload(req, len);
+ if (value >= 0 && value < len)
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ break;
+ case USB_REQ_DFU_ABORT:
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ /* no zlp? */
+ value = RET_ZLP;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_error(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ case USB_REQ_DFU_CLRSTATUS:
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ f_dfu->dfu_status = DFU_STATUS_OK;
+ /* no zlp? */
+ value = RET_ZLP;
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static dfu_state_fn dfu_state[] = {
+ state_app_idle, /* DFU_STATE_appIDLE */
+ state_app_detach, /* DFU_STATE_appDETACH */
+ state_dfu_idle, /* DFU_STATE_dfuIDLE */
+ state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */
+ state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */
+ state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */
+ state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */
+ NULL, /* DFU_STATE_dfuMANIFEST */
+ NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */
+ state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */
+ state_dfu_error /* DFU_STATE_dfuERROR */
+};
+
+static int
+dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_dfu *f_dfu = f->config->cdev->req->context;
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ int value = 0;
+ u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
+
+ debug("w_value: 0x%x len: 0x%x\n", w_value, len);
+ debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
+ req_type, ctrl->bRequest, f_dfu->dfu_state);
+
+ if (req_type == USB_TYPE_STANDARD) {
+ if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR &&
+ (w_value >> 8) == DFU_DT_FUNC) {
+ value = min(len, (u16) sizeof(dfu_func));
+ memcpy(req->buf, &dfu_func, value);
+ }
+ } else /* DFU specific request */
+ value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req);
+
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < len;
+ value = usb_ep_queue(gadget->ep0, req, 0);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ }
+ }
+
+ return value;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+dfu_prepare_strings(struct f_dfu *f_dfu, int n)
+{
+ struct dfu_entity *de = NULL;
+ int i = 0;
+
+ f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
+ if (!f_dfu->strings)
+ goto enomem;
+
+ for (i = 0; i < n; ++i) {
+ de = dfu_get_entity(i);
+ f_dfu->strings[i].s = de->name;
+ }
+
+ f_dfu->strings[i].id = 0;
+ f_dfu->strings[i].s = NULL;
+
+ return 0;
+
+enomem:
+ while (i)
+ f_dfu->strings[--i].s = NULL;
+
+ free(f_dfu->strings);
+
+ return -ENOMEM;
+}
+
+static int dfu_prepare_function(struct f_dfu *f_dfu, int n)
+{
+ struct usb_interface_descriptor *d;
+ int i = 0;
+
+ f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n);
+ if (!f_dfu->function)
+ goto enomem;
+
+ for (i = 0; i < n; ++i) {
+ d = calloc(sizeof(*d), 1);
+ if (!d)
+ goto enomem;
+
+ d->bLength = sizeof(*d);
+ d->bDescriptorType = USB_DT_INTERFACE;
+ d->bAlternateSetting = i;
+ d->bNumEndpoints = 0;
+ d->bInterfaceClass = USB_CLASS_APP_SPEC;
+ d->bInterfaceSubClass = 1;
+ d->bInterfaceProtocol = 2;
+
+ f_dfu->function[i] = (struct usb_descriptor_header *)d;
+ }
+ f_dfu->function[i] = NULL;
+
+ return 0;
+
+enomem:
+ while (i) {
+ free(f_dfu->function[--i]);
+ f_dfu->function[i] = NULL;
+ }
+ free(f_dfu->function);
+
+ return -ENOMEM;
+}
+
+static int dfu_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_dfu *f_dfu = func_to_dfu(f);
+ int alt_num = dfu_get_alt_number();
+ int rv, id, i;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ dfu_intf_runtime.bInterfaceNumber = id;
+
+ f_dfu->dfu_state = DFU_STATE_appIDLE;
+ f_dfu->dfu_status = DFU_STATUS_OK;
+
+ rv = dfu_prepare_function(f_dfu, alt_num);
+ if (rv)
+ goto error;
+
+ rv = dfu_prepare_strings(f_dfu, alt_num);
+ if (rv)
+ goto error;
+ for (i = 0; i < alt_num; i++) {
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ f_dfu->strings[i].id = id;
+ ((struct usb_interface_descriptor *)f_dfu->function[i])
+ ->iInterface = id;
+ }
+
+ stringtab_dfu.strings = f_dfu->strings;
+
+ cdev->req->context = f_dfu;
+
+error:
+ return rv;
+}
+
+static void dfu_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+ int alt_num = dfu_get_alt_number();
+ int i;
+
+ if (f_dfu->strings) {
+ i = alt_num;
+ while (i)
+ f_dfu->strings[--i].s = NULL;
+
+ free(f_dfu->strings);
+ }
+
+ if (f_dfu->function) {
+ i = alt_num;
+ while (i) {
+ free(f_dfu->function[--i]);
+ f_dfu->function[i] = NULL;
+ }
+ free(f_dfu->function);
+ }
+
+ free(f_dfu);
+}
+
+static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+
+ debug("%s: intf:%d alt:%d\n", __func__, intf, alt);
+
+ f_dfu->altsetting = alt;
+
+ return 0;
+}
+
+/* TODO: is this really what we need here? */
+static void dfu_disable(struct usb_function *f)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+ if (f_dfu->config == 0)
+ return;
+
+ debug("%s: reset config\n", __func__);
+
+ f_dfu->config = 0;
+}
+
+static int dfu_bind_config(struct usb_configuration *c)
+{
+ struct f_dfu *f_dfu;
+ int status;
+
+ f_dfu = calloc(sizeof(*f_dfu), 1);
+ if (!f_dfu)
+ return -ENOMEM;
+ f_dfu->usb_function.name = "dfu";
+ f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
+ f_dfu->usb_function.bind = dfu_bind;
+ f_dfu->usb_function.unbind = dfu_unbind;
+ f_dfu->usb_function.set_alt = dfu_set_alt;
+ f_dfu->usb_function.disable = dfu_disable;
+ f_dfu->usb_function.strings = dfu_generic_strings,
+ f_dfu->usb_function.setup = dfu_handle,
+
+ status = usb_add_function(c, &f_dfu->usb_function);
+ if (status)
+ free(f_dfu);
+
+ return status;
+}
+
+int dfu_add(struct usb_configuration *c)
+{
+ int id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ strings_dfu_generic[0].id = id;
+ dfu_intf_runtime.iInterface = id;
+
+ debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__,
+ c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
+
+ return dfu_bind_config(c);
+}
diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h
new file mode 100644
index 0000000000..023e1adacc
--- /dev/null
+++ b/drivers/usb/gadget/f_dfu.h
@@ -0,0 +1,100 @@
+/*
+ * f_dfu.h -- Device Firmware Update gadget
+ *
+ * Copyright (C) 2011-2012 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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 __F_DFU_H_
+#define __F_DFU_H_
+
+#include <linux/compiler.h>
+#include <linux/usb/composite.h>
+
+#define DFU_CONFIG_VAL 1
+#define DFU_DT_FUNC 0x21
+
+#define DFU_BIT_WILL_DETACH (0x1 << 3)
+#define DFU_BIT_MANIFESTATION_TOLERANT (0x1 << 2)
+#define DFU_BIT_CAN_UPLOAD (0x1 << 1)
+#define DFU_BIT_CAN_DNLOAD 0x1
+
+/* big enough to hold our biggest descriptor */
+#define DFU_USB_BUFSIZ 4096
+
+#define USB_REQ_DFU_DETACH 0x00
+#define USB_REQ_DFU_DNLOAD 0x01
+#define USB_REQ_DFU_UPLOAD 0x02
+#define USB_REQ_DFU_GETSTATUS 0x03
+#define USB_REQ_DFU_CLRSTATUS 0x04
+#define USB_REQ_DFU_GETSTATE 0x05
+#define USB_REQ_DFU_ABORT 0x06
+
+#define DFU_STATUS_OK 0x00
+#define DFU_STATUS_errTARGET 0x01
+#define DFU_STATUS_errFILE 0x02
+#define DFU_STATUS_errWRITE 0x03
+#define DFU_STATUS_errERASE 0x04
+#define DFU_STATUS_errCHECK_ERASED 0x05
+#define DFU_STATUS_errPROG 0x06
+#define DFU_STATUS_errVERIFY 0x07
+#define DFU_STATUS_errADDRESS 0x08
+#define DFU_STATUS_errNOTDONE 0x09
+#define DFU_STATUS_errFIRMWARE 0x0a
+#define DFU_STATUS_errVENDOR 0x0b
+#define DFU_STATUS_errUSBR 0x0c
+#define DFU_STATUS_errPOR 0x0d
+#define DFU_STATUS_errUNKNOWN 0x0e
+#define DFU_STATUS_errSTALLEDPKT 0x0f
+
+#define RET_STALL -1
+#define RET_ZLP 0
+#define RET_STAT_LEN 6
+
+enum dfu_state {
+ DFU_STATE_appIDLE = 0,
+ DFU_STATE_appDETACH = 1,
+ DFU_STATE_dfuIDLE = 2,
+ DFU_STATE_dfuDNLOAD_SYNC = 3,
+ DFU_STATE_dfuDNBUSY = 4,
+ DFU_STATE_dfuDNLOAD_IDLE = 5,
+ DFU_STATE_dfuMANIFEST_SYNC = 6,
+ DFU_STATE_dfuMANIFEST = 7,
+ DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
+ DFU_STATE_dfuUPLOAD_IDLE = 9,
+ DFU_STATE_dfuERROR = 10,
+};
+
+struct dfu_status {
+ __u8 bStatus;
+ __u8 bwPollTimeout[3];
+ __u8 bState;
+ __u8 iString;
+} __packed;
+
+struct dfu_function_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bmAttributes;
+ __le16 wDetachTimeOut;
+ __le16 wTransferSize;
+ __le16 bcdDFUVersion;
+} __packed;
+
+/* configuration-specific linkup */
+int dfu_add(struct usb_configuration *c);
+#endif /* __F_DFU_H_ */
diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c
new file mode 100644
index 0000000000..7d87050df3
--- /dev/null
+++ b/drivers/usb/gadget/g_dnl.c
@@ -0,0 +1,202 @@
+/*
+ * g_dnl.c -- USB Downloader Gadget
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * 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 <errno.h>
+#include <common.h>
+#include <malloc.h>
+
+#include <mmc.h>
+#include <part.h>
+
+#include <g_dnl.h>
+#include "f_dfu.h"
+
+#include "gadget_chips.h"
+#include "composite.c"
+
+/*
+ * One needs to define the following:
+ * CONFIG_G_DNL_VENDOR_NUM
+ * CONFIG_G_DNL_PRODUCT_NUM
+ * CONFIG_G_DNL_MANUFACTURER
+ * at e.g. ./include/configs/<board>.h
+ */
+
+#define STRING_MANUFACTURER 25
+#define STRING_PRODUCT 2
+#define STRING_USBDOWN 2
+#define CONFIG_USBDOWNLOADER 2
+
+#define DRIVER_VERSION "usb_dnl 2.0"
+
+static const char shortname[] = "usb_dnl_";
+static const char product[] = "USB download gadget";
+static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER;
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/
+
+ .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM),
+ .iProduct = STRING_PRODUCT,
+ .bNumConfigurations = 1,
+};
+
+/* static strings, in UTF-8 */
+static struct usb_string g_dnl_string_defs[] = {
+ { 0, manufacturer, },
+ { 1, product, },
+};
+
+static struct usb_gadget_strings g_dnl_string_tab = {
+ .language = 0x0409, /* en-us */
+ .strings = g_dnl_string_defs,
+};
+
+static struct usb_gadget_strings *g_dnl_composite_strings[] = {
+ &g_dnl_string_tab,
+ NULL,
+};
+
+static int g_dnl_unbind(struct usb_composite_dev *cdev)
+{
+ debug("%s\n", __func__);
+ return 0;
+}
+
+static int g_dnl_do_config(struct usb_configuration *c)
+{
+ const char *s = c->cdev->driver->name;
+ int ret = -1;
+
+ debug("%s: configuration: 0x%p composite dev: 0x%p\n",
+ __func__, c, c->cdev);
+
+ printf("GADGET DRIVER: %s\n", s);
+ if (!strcmp(s, "usb_dnl_dfu"))
+ ret = dfu_add(c);
+
+ return ret;
+}
+
+static int g_dnl_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .label = "usb_dnload",
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bConfigurationValue = CONFIG_USBDOWNLOADER,
+ .iConfiguration = STRING_USBDOWN,
+
+ .bind = g_dnl_do_config,
+ };
+
+ return usb_add_config(cdev, &config);
+}
+
+static int g_dnl_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int id, ret;
+ int gcnum;
+
+ debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
+
+ id = usb_string_id(cdev);
+
+ if (id < 0)
+ return id;
+ g_dnl_string_defs[0].id = id;
+ device_desc.iManufacturer = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+
+ g_dnl_string_defs[1].id = id;
+ device_desc.iProduct = id;
+
+ ret = g_dnl_config_register(cdev);
+ if (ret)
+ goto error;
+
+ gcnum = usb_gadget_controller_number(gadget);
+
+ debug("gcnum: %d\n", gcnum);
+ if (gcnum >= 0)
+ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+ else {
+ debug("%s: controller '%s' not recognized\n",
+ shortname, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+ return 0;
+
+ error:
+ g_dnl_unbind(cdev);
+ return -ENOMEM;
+}
+
+static struct usb_composite_driver g_dnl_driver = {
+ .name = NULL,
+ .dev = &device_desc,
+ .strings = g_dnl_composite_strings,
+
+ .bind = g_dnl_bind,
+ .unbind = g_dnl_unbind,
+};
+
+int g_dnl_register(const char *type)
+{
+ /* We only allow "dfu" atm, so 3 should be enough */
+ static char name[sizeof(shortname) + 3];
+ int ret;
+
+ if (!strcmp(type, "dfu")) {
+ strcpy(name, shortname);
+ strcat(name, type);
+ } else {
+ printf("%s: unknown command: %s\n", __func__, type);
+ return -EINVAL;
+ }
+
+ g_dnl_driver.name = name;
+
+ debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name);
+ ret = usb_composite_register(&g_dnl_driver);
+
+ if (ret) {
+ printf("%s: failed!, error: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void g_dnl_unregister(void)
+{
+ usb_composite_unregister(&g_dnl_driver);
+}
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 6de91640d6..bcb4662c47 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -28,6 +28,7 @@ LIB := $(obj)libusb_host.o
# ohci
COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o
COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o
+COBJS-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o
COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx-hcd.o
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 2a82a29125..392e286224 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -22,6 +22,7 @@
*/
#include <common.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#include <usb.h>
#include <asm/io.h>
#include <malloc.h>
@@ -163,7 +164,7 @@ static int ehci_reset(void)
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
cmd = ehci_readl(&hcor->or_txfilltuning);
- cmd &= ~TXFIFO_THRESH(0x3f);
+ cmd &= ~TXFIFO_THRESH_MASK;
cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);
ehci_writel(&hcor->or_txfilltuning, cmd);
#endif
@@ -183,10 +184,10 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN));
idx = 0;
- while (idx < 5) {
+ while (idx < QT_BUFFER_CNT) {
td->qt_buffer[idx] = cpu_to_hc32(addr);
td->qt_buffer_hi[idx] = 0;
- next = (addr + 4096) & ~4095;
+ next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1);
delta = next - addr;
if (delta >= sz)
break;
@@ -195,7 +196,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
idx++;
}
- if (idx == 5) {
+ if (idx == QT_BUFFER_CNT) {
printf("out of buffer pointers (%u bytes left)\n", sz);
return -1;
}
@@ -208,13 +209,14 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
{
ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN);
- ALLOC_ALIGN_BUFFER(struct qTD, qtd, 3, USB_DMA_MINALIGN);
+ struct qTD *qtd;
+ int qtd_count = 0;
int qtd_counter = 0;
volatile struct qTD *vtd;
unsigned long ts;
uint32_t *tdp;
- uint32_t endpt, token, usbsts;
+ uint32_t endpt, maxpacket, token, usbsts;
uint32_t c, toggle;
uint32_t cmd;
int timeout;
@@ -229,8 +231,73 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
le16_to_cpu(req->value), le16_to_cpu(req->value),
le16_to_cpu(req->index));
+#define PKT_ALIGN 512
+ /*
+ * The USB transfer is split into qTD transfers. Eeach qTD transfer is
+ * described by a transfer descriptor (the qTD). The qTDs form a linked
+ * list with a queue head (QH).
+ *
+ * Each qTD transfer starts with a new USB packet, i.e. a packet cannot
+ * have its beginning in a qTD transfer and its end in the following
+ * one, so the qTD transfer lengths have to be chosen accordingly.
+ *
+ * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to
+ * single pages. The first data buffer can start at any offset within a
+ * page (not considering the cache-line alignment issues), while the
+ * following buffers must be page-aligned. There is no alignment
+ * constraint on the size of a qTD transfer.
+ */
+ if (req != NULL)
+ /* 1 qTD will be needed for SETUP, and 1 for ACK. */
+ qtd_count += 1 + 1;
+ if (length > 0 || req == NULL) {
+ /*
+ * Determine the qTD transfer size that will be used for the
+ * data payload (not considering the first qTD transfer, which
+ * may be longer or shorter, and the final one, which may be
+ * shorter).
+ *
+ * In order to keep each packet within a qTD transfer, the qTD
+ * transfer size is aligned to PKT_ALIGN, which is a multiple of
+ * wMaxPacketSize (except in some cases for interrupt transfers,
+ * see comment in submit_int_msg()).
+ *
+ * By default, i.e. if the input buffer is aligned to PKT_ALIGN,
+ * QT_BUFFER_CNT full pages will be used.
+ */
+ int xfr_sz = QT_BUFFER_CNT;
+ /*
+ * However, if the input buffer is not aligned to PKT_ALIGN, the
+ * qTD transfer size will be one page shorter, and the first qTD
+ * data buffer of each transfer will be page-unaligned.
+ */
+ if ((uint32_t)buffer & (PKT_ALIGN - 1))
+ xfr_sz--;
+ /* Convert the qTD transfer size to bytes. */
+ xfr_sz *= EHCI_PAGE_SIZE;
+ /*
+ * Approximate by excess the number of qTDs that will be
+ * required for the data payload. The exact formula is way more
+ * complicated and saves at most 2 qTDs, i.e. a total of 128
+ * bytes.
+ */
+ qtd_count += 2 + length / xfr_sz;
+ }
+/*
+ * Threshold value based on the worst-case total size of the allocated qTDs for
+ * a mass-storage transfer of 65535 blocks of 512 bytes.
+ */
+#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024
+#warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI
+#endif
+ qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD));
+ if (qtd == NULL) {
+ printf("unable to allocate TDs\n");
+ return -1;
+ }
+
memset(qh, 0, sizeof(struct QH));
- memset(qtd, 0, 3 * sizeof(*qtd));
+ memset(qtd, 0, qtd_count * sizeof(*qtd));
toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
@@ -245,20 +312,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
* - qh_overlay.qt_altnext
*/
qh->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
- c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
- usb_pipeendpoint(pipe) == 0) ? 1 : 0;
- endpt = (8 << 28) |
- (c << 27) |
- (usb_maxpacket(dev, pipe) << 16) |
- (0 << 15) |
- (1 << 14) |
- (usb_pipespeed(pipe) << 12) |
- (usb_pipeendpoint(pipe) << 8) |
- (0 << 7) | (usb_pipedevice(pipe) << 0);
+ c = usb_pipespeed(pipe) != USB_SPEED_HIGH && !usb_pipeendpoint(pipe);
+ maxpacket = usb_maxpacket(dev, pipe);
+ endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) |
+ QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) |
+ QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) |
+ QH_ENDPT1_EPS(usb_pipespeed(pipe)) |
+ QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) |
+ QH_ENDPT1_DEVADDR(usb_pipedevice(pipe));
qh->qh_endpt1 = cpu_to_hc32(endpt);
- endpt = (1 << 30) |
- (dev->portnr << 23) |
- (dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
+ endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) |
+ QH_ENDPT2_HUBADDR(dev->parent->devnum) |
+ QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);
qh->qh_endpt2 = cpu_to_hc32(endpt);
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
@@ -276,12 +341,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
- token = (0 << 31) |
- (sizeof(*req) << 16) |
- (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
+ token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) |
+ QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
+ QT_TOKEN_PID(QT_TOKEN_PID_SETUP) |
+ QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
- if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) {
- printf("unable construct SETUP td\n");
+ if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) {
+ printf("unable to construct SETUP TD\n");
goto fail;
}
/* Update previous qTD! */
@@ -291,31 +357,71 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
}
if (length > 0 || req == NULL) {
- /*
- * Setup request qTD (3.5 in ehci-r10.pdf)
- *
- * qt_next ................ 03-00 H
- * qt_altnext ............. 07-04 H
- * qt_token ............... 0B-08 H
- *
- * [ buffer, buffer_hi ] loaded with "buffer".
- */
- qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
- qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
- token = (toggle << 31) |
- (length << 16) |
- ((req == NULL ? 1 : 0) << 15) |
- (0 << 12) |
- (3 << 10) |
- ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
- qtd[qtd_counter].qt_token = cpu_to_hc32(token);
- if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) {
- printf("unable construct DATA td\n");
- goto fail;
- }
- /* Update previous qTD! */
- *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
- tdp = &qtd[qtd_counter++].qt_next;
+ uint8_t *buf_ptr = buffer;
+ int left_length = length;
+
+ do {
+ /*
+ * Determine the size of this qTD transfer. By default,
+ * QT_BUFFER_CNT full pages can be used.
+ */
+ int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE;
+ /*
+ * However, if the input buffer is not page-aligned, the
+ * portion of the first page before the buffer start
+ * offset within that page is unusable.
+ */
+ xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1);
+ /*
+ * In order to keep each packet within a qTD transfer,
+ * align the qTD transfer size to PKT_ALIGN.
+ */
+ xfr_bytes &= ~(PKT_ALIGN - 1);
+ /*
+ * This transfer may be shorter than the available qTD
+ * transfer size that has just been computed.
+ */
+ xfr_bytes = min(xfr_bytes, left_length);
+
+ /*
+ * Setup request qTD (3.5 in ehci-r10.pdf)
+ *
+ * qt_next ................ 03-00 H
+ * qt_altnext ............. 07-04 H
+ * qt_token ............... 0B-08 H
+ *
+ * [ buffer, buffer_hi ] loaded with "buffer".
+ */
+ qtd[qtd_counter].qt_next =
+ cpu_to_hc32(QT_NEXT_TERMINATE);
+ qtd[qtd_counter].qt_altnext =
+ cpu_to_hc32(QT_NEXT_TERMINATE);
+ token = QT_TOKEN_DT(toggle) |
+ QT_TOKEN_TOTALBYTES(xfr_bytes) |
+ QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) |
+ QT_TOKEN_CERR(3) |
+ QT_TOKEN_PID(usb_pipein(pipe) ?
+ QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) |
+ QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
+ qtd[qtd_counter].qt_token = cpu_to_hc32(token);
+ if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr,
+ xfr_bytes)) {
+ printf("unable to construct DATA TD\n");
+ goto fail;
+ }
+ /* Update previous qTD! */
+ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
+ tdp = &qtd[qtd_counter++].qt_next;
+ /*
+ * Data toggle has to be adjusted since the qTD transfer
+ * size is not always an even multiple of
+ * wMaxPacketSize.
+ */
+ if ((xfr_bytes / maxpacket) & 1)
+ toggle ^= 1;
+ buf_ptr += xfr_bytes;
+ left_length -= xfr_bytes;
+ } while (left_length > 0);
}
if (req != NULL) {
@@ -328,12 +434,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
- token = (toggle << 31) |
- (0 << 16) |
- (1 << 15) |
- (0 << 12) |
- (3 << 10) |
- ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
+ token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) |
+ QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
+ QT_TOKEN_PID(usb_pipein(pipe) ?
+ QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) |
+ QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
@@ -346,7 +451,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
flush_dcache_range((uint32_t)qh_list,
ALIGN_END_ADDR(struct QH, qh_list, 1));
flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1));
- flush_dcache_range((uint32_t)qtd, ALIGN_END_ADDR(struct qTD, qtd, 3));
+ flush_dcache_range((uint32_t)qtd,
+ ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
/* Set async. queue head pointer. */
ehci_writel(&hcor->or_asynclistaddr, (uint32_t)qh_list);
@@ -359,10 +465,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
cmd |= CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);
- ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
+ ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, STS_ASS,
100 * 1000);
if (ret < 0) {
- printf("EHCI fail timeout STD_ASS set\n");
+ printf("EHCI fail timeout STS_ASS set\n");
goto fail;
}
@@ -377,10 +483,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
invalidate_dcache_range((uint32_t)qh,
ALIGN_END_ADDR(struct QH, qh, 1));
invalidate_dcache_range((uint32_t)qtd,
- ALIGN_END_ADDR(struct qTD, qtd, 3));
+ ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
token = hc32_to_cpu(vtd->qt_token);
- if (!(token & 0x80))
+ if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE))
break;
WATCHDOG_RESET();
} while (get_timer(ts) < timeout);
@@ -398,50 +504,50 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN));
/* Check that the TD processing happened */
- if (token & 0x80) {
+ if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
printf("EHCI timed out on TD - token=%#x\n", token);
- }
/* Disable async schedule. */
cmd = ehci_readl(&hcor->or_usbcmd);
cmd &= ~CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);
- ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
+ ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, 0,
100 * 1000);
if (ret < 0) {
- printf("EHCI fail timeout STD_ASS reset\n");
+ printf("EHCI fail timeout STS_ASS reset\n");
goto fail;
}
token = hc32_to_cpu(qh->qh_overlay.qt_token);
- if (!(token & 0x80)) {
+ if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {
debug("TOKEN=%#x\n", token);
- switch (token & 0xfc) {
+ switch (QT_TOKEN_GET_STATUS(token) &
+ ~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
case 0:
- toggle = token >> 31;
+ toggle = QT_TOKEN_GET_DT(token);
usb_settoggle(dev, usb_pipeendpoint(pipe),
usb_pipeout(pipe), toggle);
dev->status = 0;
break;
- case 0x40:
+ case QT_TOKEN_STATUS_HALTED:
dev->status = USB_ST_STALLED;
break;
- case 0xa0:
- case 0x20:
+ case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR:
+ case QT_TOKEN_STATUS_DATBUFERR:
dev->status = USB_ST_BUF_ERR;
break;
- case 0x50:
- case 0x10:
+ case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET:
+ case QT_TOKEN_STATUS_BABBLEDET:
dev->status = USB_ST_BABBLE_DET;
break;
default:
dev->status = USB_ST_CRC_ERR;
- if ((token & 0x40) == 0x40)
+ if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)
dev->status |= USB_ST_STALLED;
break;
}
- dev->act_len = length - ((token >> 16) & 0x7fff);
+ dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
} else {
dev->act_len = 0;
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
@@ -450,9 +556,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
ehci_readl(&hcor->or_portsc[1]));
}
+ free(qtd);
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
fail:
+ free(qtd);
return -1;
}
@@ -499,12 +607,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
case USB_DT_DEVICE:
debug("USB_DT_DEVICE request\n");
srcptr = &descriptor.device;
- srclen = 0x12;
+ srclen = descriptor.device.bLength;
break;
case USB_DT_CONFIG:
debug("USB_DT_CONFIG config\n");
srcptr = &descriptor.config;
- srclen = 0x19;
+ srclen = descriptor.config.bLength +
+ descriptor.interface.bLength +
+ descriptor.endpoint.bLength;
break;
case USB_DT_STRING:
debug("USB_DT_STRING config\n");
@@ -539,7 +649,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
case USB_DT_HUB:
debug("USB_DT_HUB config\n");
srcptr = &descriptor.hub;
- srclen = 0x8;
+ srclen = descriptor.hub.bLength;
break;
default:
debug("unknown value %x\n", le16_to_cpu(req->value));
@@ -577,13 +687,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (ehci_is_TDI()) {
- switch ((reg >> 26) & 3) {
- case 0:
+ switch (PORTSC_PSPD(reg)) {
+ case PORTSC_PSPD_FS:
break;
- case 1:
+ case PORTSC_PSPD_LS:
tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
break;
- case 2:
+ case PORTSC_PSPD_HS:
default:
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
break;
@@ -729,36 +839,40 @@ int usb_lowlevel_init(void)
uint32_t reg;
uint32_t cmd;
- if (ehci_hcd_init() != 0)
+ if (ehci_hcd_init())
return -1;
/* EHCI spec section 4.1 */
- if (ehci_reset() != 0)
+ if (ehci_reset())
return -1;
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
- if (ehci_hcd_init() != 0)
+ if (ehci_hcd_init())
return -1;
#endif
/* Set head of reclaim list */
memset(qh_list, 0, sizeof(*qh_list));
qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
- qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12));
+ qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) |
+ QH_ENDPT1_EPS(USB_SPEED_HIGH));
qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
- qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40);
+ qh_list->qh_overlay.qt_token =
+ cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED));
reg = ehci_readl(&hccr->cr_hcsparams);
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
/* Port Indicators */
if (HCS_INDICATOR(reg))
- descriptor.hub.wHubCharacteristics |= 0x80;
+ put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
+ | 0x80, &descriptor.hub.wHubCharacteristics);
/* Port Power Control */
if (HCS_PPC(reg))
- descriptor.hub.wHubCharacteristics |= 0x01;
+ put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
+ | 0x01, &descriptor.hub.wHubCharacteristics);
/* Start the host controller. */
cmd = ehci_readl(&hcor->or_usbcmd);
@@ -808,7 +922,7 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
}
if (usb_pipedevice(pipe) == rootdev) {
- if (rootdev == 0)
+ if (!rootdev)
dev->speed = USB_SPEED_HIGH;
return ehci_submit_root(dev, pipe, buffer, length, setup);
}
@@ -819,8 +933,24 @@ int
submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, int interval)
{
-
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
+
+ /*
+ * Interrupt transfers requiring several transactions are not supported
+ * because bInterval is ignored.
+ *
+ * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
+ * <= PKT_ALIGN if several qTDs are required, while the USB
+ * specification does not constrain this for interrupt transfers. That
+ * means that ehci_submit_async() would support interrupt transfers
+ * requiring several transactions only as long as the transfer size does
+ * not require more than a single qTD.
+ */
+ if (length > usb_maxpacket(dev, pipe)) {
+ printf("%s: Interrupt transfers requiring several transactions "
+ "are not supported.\n", __func__);
+ return -1;
+ }
return ehci_submit_async(dev, pipe, buffer, length, NULL);
}
diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c
index e1bd37ec8d..6e21669d5a 100644
--- a/drivers/usb/host/ehci-mxs.c
+++ b/drivers/usb/host/ehci-mxs.c
@@ -23,7 +23,7 @@
#include <asm/io.h>
#include <asm/arch/regs-common.h>
#include <asm/arch/regs-base.h>
-#include <asm/arch/regs-clkctrl.h>
+#include <asm/arch/regs-clkctrl-mx28.h>
#include <asm/arch/regs-usb.h>
#include <asm/arch/regs-usbphy.h>
@@ -39,8 +39,8 @@
#endif
static struct ehci_mxs {
- struct mx28_usb_regs *usb_regs;
- struct mx28_usbphy_regs *phy_regs;
+ struct mxs_usb_regs *usb_regs;
+ struct mxs_usbphy_regs *phy_regs;
} ehci_mxs;
int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port)
@@ -60,8 +60,8 @@ int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port)
return -1;
}
- mxs_usb->usb_regs = (struct mx28_usb_regs *)usb_base;
- mxs_usb->phy_regs = (struct mx28_usbphy_regs *)phy_base;
+ mxs_usb->usb_regs = (struct mxs_usb_regs *)usb_base;
+ mxs_usb->phy_regs = (struct mxs_usbphy_regs *)phy_base;
return 0;
}
@@ -75,10 +75,10 @@ int ehci_hcd_init(void)
int ret;
uint32_t usb_base, cap_base;
- struct mx28_register_32 *digctl_ctrl =
- (struct mx28_register_32 *)HW_DIGCTL_CTRL;
- struct mx28_clkctrl_regs *clkctrl_regs =
- (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
+ struct mxs_register_32 *digctl_ctrl =
+ (struct mxs_register_32 *)HW_DIGCTL_CTRL;
+ struct mxs_clkctrl_regs *clkctrl_regs =
+ (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);
if (ret)
@@ -119,10 +119,10 @@ int ehci_hcd_stop(void)
{
int ret;
uint32_t tmp;
- struct mx28_register_32 *digctl_ctrl =
- (struct mx28_register_32 *)HW_DIGCTL_CTRL;
- struct mx28_clkctrl_regs *clkctrl_regs =
- (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
+ struct mxs_register_32 *digctl_ctrl =
+ (struct mxs_register_32 *)HW_DIGCTL_CTRL;
+ struct mxs_clkctrl_regs *clkctrl_regs =
+ (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);
if (ret)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index cc00ce428b..39acdf9656 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -68,7 +68,7 @@ struct ehci_hcor {
#define CMD_RESET (1 << 1) /* reset HC not bus */
#define CMD_RUN (1 << 0) /* start/stop HC */
uint32_t or_usbsts;
-#define STD_ASS (1 << 15)
+#define STS_ASS (1 << 15)
#define STS_HALT (1 << 12)
uint32_t or_usbintr;
#define INTR_UE (1 << 0) /* USB interrupt enable */
@@ -83,11 +83,16 @@ struct ehci_hcor {
uint32_t _reserved_0_;
uint32_t or_burstsize;
uint32_t or_txfilltuning;
+#define TXFIFO_THRESH_MASK (0x3f << 16)
#define TXFIFO_THRESH(p) ((p & 0x3f) << 16)
uint32_t _reserved_1_[6];
uint32_t or_configflag;
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
+#define PORTSC_PSPD(x) (((x) >> 26) & 0x3)
+#define PORTSC_PSPD_FS 0x0
+#define PORTSC_PSPD_LS 0x1
+#define PORTSC_PSPD_HS 0x2
uint32_t or_systune;
} __attribute__ ((packed, aligned(4)));
@@ -171,16 +176,40 @@ struct usb_linux_config_descriptor {
/* Queue Element Transfer Descriptor (qTD). */
struct qTD {
/* this part defined by EHCI spec */
- uint32_t qt_next; /* see EHCI 3.5.1 */
+ uint32_t qt_next; /* see EHCI 3.5.1 */
#define QT_NEXT_TERMINATE 1
- uint32_t qt_altnext; /* see EHCI 3.5.2 */
- uint32_t qt_token; /* see EHCI 3.5.3 */
- uint32_t qt_buffer[5]; /* see EHCI 3.5.4 */
- uint32_t qt_buffer_hi[5]; /* Appendix B */
+ uint32_t qt_altnext; /* see EHCI 3.5.2 */
+ uint32_t qt_token; /* see EHCI 3.5.3 */
+#define QT_TOKEN_DT(x) (((x) & 0x1) << 31) /* Data Toggle */
+#define QT_TOKEN_GET_DT(x) (((x) >> 31) & 0x1)
+#define QT_TOKEN_TOTALBYTES(x) (((x) & 0x7fff) << 16) /* Total Bytes to Transfer */
+#define QT_TOKEN_GET_TOTALBYTES(x) (((x) >> 16) & 0x7fff)
+#define QT_TOKEN_IOC(x) (((x) & 0x1) << 15) /* Interrupt On Complete */
+#define QT_TOKEN_CPAGE(x) (((x) & 0x7) << 12) /* Current Page */
+#define QT_TOKEN_CERR(x) (((x) & 0x3) << 10) /* Error Counter */
+#define QT_TOKEN_PID(x) (((x) & 0x3) << 8) /* PID Code */
+#define QT_TOKEN_PID_OUT 0x0
+#define QT_TOKEN_PID_IN 0x1
+#define QT_TOKEN_PID_SETUP 0x2
+#define QT_TOKEN_STATUS(x) (((x) & 0xff) << 0) /* Status */
+#define QT_TOKEN_GET_STATUS(x) (((x) >> 0) & 0xff)
+#define QT_TOKEN_STATUS_ACTIVE 0x80
+#define QT_TOKEN_STATUS_HALTED 0x40
+#define QT_TOKEN_STATUS_DATBUFERR 0x20
+#define QT_TOKEN_STATUS_BABBLEDET 0x10
+#define QT_TOKEN_STATUS_XACTERR 0x08
+#define QT_TOKEN_STATUS_MISSEDUFRAME 0x04
+#define QT_TOKEN_STATUS_SPLITXSTATE 0x02
+#define QT_TOKEN_STATUS_PERR 0x01
+#define QT_BUFFER_CNT 5
+ uint32_t qt_buffer[QT_BUFFER_CNT]; /* see EHCI 3.5.4 */
+ uint32_t qt_buffer_hi[QT_BUFFER_CNT]; /* Appendix B */
/* pad struct for 32 byte alignment */
uint32_t unused[3];
};
+#define EHCI_PAGE_SIZE 4096
+
/* Queue Head (QH). */
struct QH {
uint32_t qh_link;
@@ -190,7 +219,26 @@ struct QH {
#define QH_LINK_TYPE_SITD 4
#define QH_LINK_TYPE_FSTN 6
uint32_t qh_endpt1;
+#define QH_ENDPT1_RL(x) (((x) & 0xf) << 28) /* NAK Count Reload */
+#define QH_ENDPT1_C(x) (((x) & 0x1) << 27) /* Control Endpoint Flag */
+#define QH_ENDPT1_MAXPKTLEN(x) (((x) & 0x7ff) << 16) /* Maximum Packet Length */
+#define QH_ENDPT1_H(x) (((x) & 0x1) << 15) /* Head of Reclamation List Flag */
+#define QH_ENDPT1_DTC(x) (((x) & 0x1) << 14) /* Data Toggle Control */
+#define QH_ENDPT1_DTC_IGNORE_QTD_TD 0x0
+#define QH_ENDPT1_DTC_DT_FROM_QTD 0x1
+#define QH_ENDPT1_EPS(x) (((x) & 0x3) << 12) /* Endpoint Speed */
+#define QH_ENDPT1_EPS_FS 0x0
+#define QH_ENDPT1_EPS_LS 0x1
+#define QH_ENDPT1_EPS_HS 0x2
+#define QH_ENDPT1_ENDPT(x) (((x) & 0xf) << 8) /* Endpoint Number */
+#define QH_ENDPT1_I(x) (((x) & 0x1) << 7) /* Inactivate on Next Transaction */
+#define QH_ENDPT1_DEVADDR(x) (((x) & 0x7f) << 0) /* Device Address */
uint32_t qh_endpt2;
+#define QH_ENDPT2_MULT(x) (((x) & 0x3) << 30) /* High-Bandwidth Pipe Multiplier */
+#define QH_ENDPT2_PORTNUM(x) (((x) & 0x7f) << 23) /* Port Number */
+#define QH_ENDPT2_HUBADDR(x) (((x) & 0x7f) << 16) /* Hub Address */
+#define QH_ENDPT2_UFCMASK(x) (((x) & 0xff) << 8) /* Split Completion Mask */
+#define QH_ENDPT2_UFSMASK(x) (((x) & 0xff) << 0) /* Interrupt Schedule Mask */
uint32_t qh_curtd;
struct qTD qh_overlay;
/*
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
new file mode 100644
index 0000000000..f0ccb83f7a
--- /dev/null
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 Sughosh Ganu <urwithsughosh@gmail.com>
+ *
+ * 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.
+ */
+
+#include <common.h>
+
+#include <asm/arch/da8xx-usb.h>
+
+int usb_cpu_init(void)
+{
+ /* enable psc for usb2.0 */
+ lpsc_on(DAVINCI_LPSC_USB20);
+
+ /* enable psc for usb1.0 */
+ lpsc_on(DAVINCI_LPSC_USB11);
+
+ /* start the on-chip usb phy and its pll */
+ if (usb_phy_on())
+ return 0;
+
+ return 1;
+}
+
+int usb_cpu_stop(void)
+{
+ usb_phy_off();
+
+ /* turn off the usb clock and assert the module reset */
+ lpsc_disable(DAVINCI_LPSC_USB11);
+ lpsc_disable(DAVINCI_LPSC_USB20);
+
+ return 0;
+}
+
+int usb_cpu_init_fail(void)
+{
+ return usb_cpu_stop();
+}
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index d24f2f1314..9f4735167a 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1261,19 +1261,11 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
int leni = transfer_len;
int len = 0;
int stat = 0;
- __u32 datab[4];
- union {
- void *ptr;
- __u8 *u8;
- __u16 *u16;
- __u32 *u32;
- } databuf;
__u16 bmRType_bReq;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
-
- databuf.u32 = (__u32 *)datab;
+ ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32));
#ifdef DEBUG
pkt_print(NULL, dev, pipe, buffer, transfer_len,
@@ -1304,20 +1296,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
*/
case RH_GET_STATUS:
- databuf.u16[0] = cpu_to_le16(1);
+ *(u16 *)databuf = cpu_to_le16(1);
OK(2);
case RH_GET_STATUS | RH_INTERFACE:
- databuf.u16[0] = cpu_to_le16(0);
+ *(u16 *)databuf = cpu_to_le16(0);
OK(2);
case RH_GET_STATUS | RH_ENDPOINT:
- databuf.u16[0] = cpu_to_le16(0);
+ *(u16 *)databuf = cpu_to_le16(0);
OK(2);
case RH_GET_STATUS | RH_CLASS:
- databuf.u32[0] = cpu_to_le32(
+ *(u32 *)databuf = cpu_to_le32(
RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
OK(4);
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
- databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT);
+ *(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT);
OK(4);
case RH_CLEAR_FEATURE | RH_ENDPOINT:
@@ -1381,14 +1373,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
min_t(unsigned int,
sizeof(root_hub_dev_des),
wLength));
- databuf.ptr = root_hub_dev_des; OK(len);
+ databuf = root_hub_dev_des; OK(len);
case (0x02): /* configuration descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_config_des),
wLength));
- databuf.ptr = root_hub_config_des; OK(len);
+ databuf = root_hub_config_des; OK(len);
case (0x03): /* string descriptors */
if (wValue == 0x0300) {
len = min_t(unsigned int,
@@ -1396,7 +1388,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
min_t(unsigned int,
sizeof(root_hub_str_index0),
wLength));
- databuf.ptr = root_hub_str_index0;
+ databuf = root_hub_str_index0;
OK(len);
}
if (wValue == 0x0301) {
@@ -1405,7 +1397,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
min_t(unsigned int,
sizeof(root_hub_str_index1),
wLength));
- databuf.ptr = root_hub_str_index1;
+ databuf = root_hub_str_index1;
OK(len);
}
default:
@@ -1417,40 +1409,40 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
{
__u32 temp = roothub_a(&gohci);
- databuf.u8[0] = 9; /* min length; */
- databuf.u8[1] = 0x29;
- databuf.u8[2] = temp & RH_A_NDP;
+ databuf[0] = 9; /* min length; */
+ databuf[1] = 0x29;
+ databuf[2] = temp & RH_A_NDP;
#ifdef CONFIG_AT91C_PQFP_UHPBUG
- databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0;
+ databuf[2] = (databuf[2] == 2) ? 1 : 0;
#endif
- databuf.u8[3] = 0;
+ databuf[3] = 0;
if (temp & RH_A_PSM) /* per-port power switching? */
- databuf.u8[3] |= 0x1;
+ databuf[3] |= 0x1;
if (temp & RH_A_NOCP) /* no overcurrent reporting? */
- databuf.u8[3] |= 0x10;
+ databuf[3] |= 0x10;
else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */
- databuf.u8[3] |= 0x8;
+ databuf[3] |= 0x8;
- /* corresponds to databuf.u8[4-7] */
- databuf.u8[1] = 0;
- databuf.u8[5] = (temp & RH_A_POTPGT) >> 24;
+ databuf[4] = 0;
+ databuf[5] = (temp & RH_A_POTPGT) >> 24;
+ databuf[6] = 0;
temp = roothub_b(&gohci);
- databuf.u8[7] = temp & RH_B_DR;
- if (databuf.u8[2] < 7) {
- databuf.u8[8] = 0xff;
+ databuf[7] = temp & RH_B_DR;
+ if (databuf[2] < 7) {
+ databuf[8] = 0xff;
} else {
- databuf.u8[0] += 2;
- databuf.u8[8] = (temp & RH_B_DR) >> 8;
- databuf.u8[10] = databuf.u8[9] = 0xff;
+ databuf[0] += 2;
+ databuf[8] = (temp & RH_B_DR) >> 8;
+ databuf[10] = databuf[9] = 0xff;
}
len = min_t(unsigned int, leni,
- min_t(unsigned int, databuf.u8[0], wLength));
+ min_t(unsigned int, databuf[0], wLength));
OK(len);
}
case RH_GET_CONFIGURATION:
- databuf.u8[0] = 0x01;
+ databuf[0] = 0x01;
OK(1);
case RH_SET_CONFIGURATION:
@@ -1469,8 +1461,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
#endif
len = min_t(int, len, leni);
- if (data != databuf.ptr)
- memcpy(data, databuf.ptr, len);
+ if (data != databuf)
+ memcpy(data, databuf, len);
dev->act_len = len;
dev->status = stat;
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 617d88e1a7..653410a573 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -23,7 +23,8 @@
*/
#include <common.h>
-#include "da8xx.h"
+#include "musb_core.h"
+#include <asm/arch/da8xx-usb.h>
/* MUSB platform configuration */
struct musb_config musb_cfg = {
diff --git a/drivers/usb/musb/da8xx.h b/drivers/usb/musb/da8xx.h
deleted file mode 100644
index be1cdaf1dc..0000000000
--- a/drivers/usb/musb/da8xx.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * da8xx.h -- TI's DA8xx platform specific usb wrapper definitions.
- *
- * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
- *
- * Based on drivers/usb/musb/davinci.h
- *
- * Copyright (C) 2009 Texas Instruments Incorporated
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __DA8XX_MUSB_H__
-#define __DA8XX_MUSB_H__
-
-#include <asm/arch/hardware.h>
-#include <asm/arch/gpio.h>
-#include "musb_core.h"
-
-/* Base address of da8xx usb0 wrapper */
-#define DA8XX_USB_OTG_BASE 0x01E00000
-
-/* Base address of da8xx musb core */
-#define DA8XX_USB_OTG_CORE_BASE (DA8XX_USB_OTG_BASE + 0x400)
-
-/* Timeout for DA8xx usb module */
-#define DA8XX_USB_OTG_TIMEOUT 0x3FFFFFF
-
-/*
- * DA8xx platform USB wrapper register overlay.
- */
-struct da8xx_usb_regs {
- dv_reg revision;
- dv_reg control;
- dv_reg status;
- dv_reg emulation;
- dv_reg mode;
- dv_reg autoreq;
- dv_reg srpfixtime;
- dv_reg teardown;
- dv_reg intsrc;
- dv_reg intsrc_set;
- dv_reg intsrc_clr;
- dv_reg intmsk;
- dv_reg intmsk_set;
- dv_reg intmsk_clr;
- dv_reg intsrcmsk;
- dv_reg eoi;
- dv_reg intvector;
- dv_reg grndis_size[4];
-};
-
-#define da8xx_usb_regs ((struct da8xx_usb_regs *)DA8XX_USB_OTG_BASE)
-
-/* DA8XX interrupt bits definitions */
-#define DA8XX_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */
-#define DA8XX_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */
-#define DA8XX_USB_TXINT_SHIFT 0
-#define DA8XX_USB_RXINT_SHIFT 8
-
-#define DA8XX_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */
-#define DA8XX_USB_TXINT_MASK \
- (DA8XX_USB_TX_ENDPTS_MASK << DA8XX_USB_TXINT_SHIFT)
-#define DA8XX_USB_RXINT_MASK \
- (DA8XX_USB_RX_ENDPTS_MASK << DA8XX_USB_RXINT_SHIFT)
-
-/* DA8xx CFGCHIP2 (USB 2.0 PHY Control) register bits */
-#define CFGCHIP2_PHYCLKGD (1 << 17)
-#define CFGCHIP2_VBUSSENSE (1 << 16)
-#define CFGCHIP2_RESET (1 << 15)
-#define CFGCHIP2_OTGMODE (3 << 13)
-#define CFGCHIP2_NO_OVERRIDE (0 << 13)
-#define CFGCHIP2_FORCE_HOST (1 << 13)
-#define CFGCHIP2_FORCE_DEVICE (2 << 13)
-#define CFGCHIP2_FORCE_HOST_VBUS_LOW (3 << 13)
-#define CFGCHIP2_USB1PHYCLKMUX (1 << 12)
-#define CFGCHIP2_USB2PHYCLKMUX (1 << 11)
-#define CFGCHIP2_PHYPWRDN (1 << 10)
-#define CFGCHIP2_OTGPWRDN (1 << 9)
-#define CFGCHIP2_DATPOL (1 << 8)
-#define CFGCHIP2_USB1SUSPENDM (1 << 7)
-#define CFGCHIP2_PHY_PLLON (1 << 6) /* override PLL suspend */
-#define CFGCHIP2_SESENDEN (1 << 5) /* Vsess_end comparator */
-#define CFGCHIP2_VBDTCTEN (1 << 4) /* Vbus comparator */
-#define CFGCHIP2_REFFREQ (0xf << 0)
-#define CFGCHIP2_REFFREQ_12MHZ (1 << 0)
-#define CFGCHIP2_REFFREQ_24MHZ (2 << 0)
-#define CFGCHIP2_REFFREQ_48MHZ (3 << 0)
-
-#define DA8XX_USB_VBUS_GPIO (1 << 15)
-#endif /* __DA8XX_MUSB_H__ */
diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c
index 2df52c1c33..8d44c4657f 100644
--- a/drivers/usb/musb/musb_hcd.c
+++ b/drivers/usb/musb/musb_hcd.c
@@ -1113,7 +1113,7 @@ int usb_lowlevel_init(void)
* should be a usb device connected.
*/
timeout = musb_cfg.timeout;
- while (timeout--)
+ while (--timeout)
if (readb(&musbr->devctl) & MUSB_DEVCTL_HM)
break;
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 2f8e2b521a..ebb6da823c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -29,9 +29,11 @@ COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
COBJS-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o
+COBJS-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o
COBJS-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o
COBJS-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
exynos_mipi_dsi_lowlevel.o
+COBJS-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o
COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o
COBJS-$(CONFIG_S6E63D6) += s6e63d6.o
diff --git a/drivers/video/exynos_dp.c b/drivers/video/exynos_dp.c
new file mode 100644
index 0000000000..53e410120a
--- /dev/null
+++ b/drivers/video/exynos_dp.c
@@ -0,0 +1,925 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <linux/err.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/dp_info.h>
+#include <asm/arch/dp.h>
+
+#include "exynos_dp_lowlevel.h"
+
+static struct exynos_dp_platform_data *dp_pd;
+
+static void exynos_dp_disp_info(struct edp_disp_info *disp_info)
+{
+ disp_info->h_total = disp_info->h_res + disp_info->h_sync_width +
+ disp_info->h_back_porch + disp_info->h_front_porch;
+ disp_info->v_total = disp_info->v_res + disp_info->v_sync_width +
+ disp_info->v_back_porch + disp_info->v_front_porch;
+
+ return;
+}
+
+static int exynos_dp_init_dp(void)
+{
+ int ret;
+ exynos_dp_reset();
+
+ /* SW defined function Normal operation */
+ exynos_dp_enable_sw_func(DP_ENABLE);
+
+ ret = exynos_dp_init_analog_func();
+ if (ret != EXYNOS_DP_SUCCESS)
+ return ret;
+
+ exynos_dp_init_hpd();
+ exynos_dp_init_aux();
+
+ return ret;
+}
+
+static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
+{
+ int i;
+ unsigned char sum = 0;
+
+ for (i = 0; i < EDID_BLOCK_LENGTH; i++)
+ sum = sum + edid_data[i];
+
+ return sum;
+}
+
+static unsigned int exynos_dp_read_edid(void)
+{
+ unsigned char edid[EDID_BLOCK_LENGTH * 2];
+ unsigned int extend_block = 0;
+ unsigned char sum;
+ unsigned char test_vector;
+ int retval;
+
+ /*
+ * EDID device address is 0x50.
+ * However, if necessary, you must have set upper address
+ * into E-EDID in I2C device, 0x30.
+ */
+
+ /* Read Extension Flag, Number of 128-byte EDID extension blocks */
+ exynos_dp_read_byte_from_i2c(I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG,
+ &extend_block);
+
+ if (extend_block > 0) {
+ printf("DP EDID data includes a single extension!\n");
+
+ /* Read EDID data */
+ retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR,
+ EDID_HEADER_PATTERN,
+ EDID_BLOCK_LENGTH,
+ &edid[EDID_HEADER_PATTERN]);
+ if (retval != 0) {
+ printf("DP EDID Read failed!\n");
+ return -1;
+ }
+ sum = exynos_dp_calc_edid_check_sum(edid);
+ if (sum != 0) {
+ printf("DP EDID bad checksum!\n");
+ return -1;
+ }
+
+ /* Read additional EDID data */
+ retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR,
+ EDID_BLOCK_LENGTH,
+ EDID_BLOCK_LENGTH,
+ &edid[EDID_BLOCK_LENGTH]);
+ if (retval != 0) {
+ printf("DP EDID Read failed!\n");
+ return -1;
+ }
+ sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
+ if (sum != 0) {
+ printf("DP EDID bad checksum!\n");
+ return -1;
+ }
+
+ exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST,
+ &test_vector);
+ if (test_vector & DPCD_TEST_EDID_READ) {
+ exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM,
+ edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
+ exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE,
+ DPCD_TEST_EDID_CHECKSUM_WRITE);
+ }
+ } else {
+ debug("DP EDID data does not include any extensions.\n");
+
+ /* Read EDID data */
+ retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR,
+ EDID_HEADER_PATTERN,
+ EDID_BLOCK_LENGTH,
+ &edid[EDID_HEADER_PATTERN]);
+
+ if (retval != 0) {
+ printf("DP EDID Read failed!\n");
+ return -1;
+ }
+ sum = exynos_dp_calc_edid_check_sum(edid);
+ if (sum != 0) {
+ printf("DP EDID bad checksum!\n");
+ return -1;
+ }
+
+ exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST,
+ &test_vector);
+ if (test_vector & DPCD_TEST_EDID_READ) {
+ exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM,
+ edid[EDID_CHECKSUM]);
+ exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE,
+ DPCD_TEST_EDID_CHECKSUM_WRITE);
+ }
+ }
+
+ debug("DP EDID Read success!\n");
+
+ return 0;
+}
+
+static unsigned int exynos_dp_handle_edid(struct edp_device_info *edp_info)
+{
+ unsigned char buf[12];
+ unsigned int ret;
+ unsigned char temp;
+ unsigned char retry_cnt;
+ unsigned char dpcd_rev[16];
+ unsigned char lane_bw[16];
+ unsigned char lane_cnt[16];
+
+ memset(dpcd_rev, 0, 16);
+ memset(lane_bw, 0, 16);
+ memset(lane_cnt, 0, 16);
+ memset(buf, 0, 12);
+
+ retry_cnt = 5;
+ while (retry_cnt) {
+ /* Read DPCD 0x0000-0x000b */
+ ret = exynos_dp_read_bytes_from_dpcd(DPCD_DPCD_REV, 12,
+ buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP read_byte_from_dpcd() failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ } else
+ break;
+ }
+
+ /* */
+ temp = buf[DPCD_DPCD_REV];
+ if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11)
+ edp_info->dpcd_rev = temp;
+ else {
+ printf("DP Wrong DPCD Rev : %x\n", temp);
+ return -ENODEV;
+ }
+
+ temp = buf[DPCD_MAX_LINK_RATE];
+ if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70)
+ edp_info->lane_bw = temp;
+ else {
+ printf("DP Wrong MAX LINK RATE : %x\n", temp);
+ return -EINVAL;
+ }
+
+ /*Refer VESA Display Port Stnadard Ver1.1a Page 120 */
+ if (edp_info->dpcd_rev == DP_DPCD_REV_11) {
+ temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f;
+ if (buf[DPCD_MAX_LANE_COUNT] & 0x80)
+ edp_info->dpcd_efc = 1;
+ else
+ edp_info->dpcd_efc = 0;
+ } else {
+ temp = buf[DPCD_MAX_LANE_COUNT];
+ edp_info->dpcd_efc = 0;
+ }
+
+ if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 ||
+ temp == DP_LANE_CNT_4) {
+ edp_info->lane_cnt = temp;
+ } else {
+ printf("DP Wrong MAX LANE COUNT : %x\n", temp);
+ return -EINVAL;
+ }
+
+ ret = exynos_dp_read_edid();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP exynos_dp_read_edid() failed\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static void exynos_dp_init_training(void)
+{
+ /*
+ * MACRO_RST must be applied after the PLL_LOCK to avoid
+ * the DP inter pair skew issue for at least 10 us
+ */
+ exynos_dp_reset_macro();
+
+ /* All DP analog module power up */
+ exynos_dp_set_analog_power_down(POWER_ALL, 0);
+}
+
+static unsigned int exynos_dp_link_start(struct edp_device_info *edp_info)
+{
+ unsigned char buf[5];
+ unsigned int ret = 0;
+
+ debug("DP: %s was called\n", __func__);
+
+ edp_info->lt_info.lt_status = DP_LT_CR;
+ edp_info->lt_info.ep_loop = 0;
+ edp_info->lt_info.cr_loop[0] = 0;
+ edp_info->lt_info.cr_loop[1] = 0;
+ edp_info->lt_info.cr_loop[2] = 0;
+ edp_info->lt_info.cr_loop[3] = 0;
+
+ /* Set sink to D0 (Sink Not Ready) mode. */
+ ret = exynos_dp_write_byte_to_dpcd(DPCD_SINK_POWER_STATE,
+ DPCD_SET_POWER_STATE_D0);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP write_dpcd_byte failed\n");
+ return ret;
+ }
+
+ /* Set link rate and count as you want to establish*/
+ exynos_dp_set_link_bandwidth(edp_info->lane_bw);
+ exynos_dp_set_lane_count(edp_info->lane_cnt);
+
+ /* Setup RX configuration */
+ buf[0] = edp_info->lane_bw;
+ buf[1] = edp_info->lane_cnt;
+
+ ret = exynos_dp_write_bytes_to_dpcd(DPCD_LINK_BW_SET, 2,
+ buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP write_dpcd_byte failed\n");
+ return ret;
+ }
+
+ exynos_dp_set_lane_pre_emphasis(PRE_EMPHASIS_LEVEL_0,
+ edp_info->lane_cnt);
+
+ /* Set training pattern 1 */
+ exynos_dp_set_training_pattern(TRAINING_PTN1);
+
+ /* Set RX training pattern */
+ buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1;
+
+ buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
+ DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
+ buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
+ DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
+ buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
+ DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
+ buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 |
+ DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0;
+
+ ret = exynos_dp_write_bytes_to_dpcd(DPCD_TRAINING_PATTERN_SET,
+ 5, buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP write_dpcd_byte failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_training_pattern_dis(void)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+
+ exynos_dp_set_training_pattern(DP_NONE);
+
+ ret = exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET,
+ DPCD_TRAINING_PATTERN_DISABLED);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP requst_link_traninig_req failed\n");
+ return -EAGAIN;
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_enable_rx_to_enhanced_mode(unsigned char enable)
+{
+ unsigned char data;
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+
+ ret = exynos_dp_read_byte_from_dpcd(DPCD_LANE_COUNT_SET,
+ &data);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read_from_dpcd failed\n");
+ return -EAGAIN;
+ }
+
+ if (enable)
+ data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data);
+ else
+ data = DPCD_LN_COUNT_SET(data);
+
+ ret = exynos_dp_write_byte_to_dpcd(DPCD_LANE_COUNT_SET,
+ data);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP write_to_dpcd failed\n");
+ return -EAGAIN;
+
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_set_enhanced_mode(unsigned char enhance_mode)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+
+ ret = exynos_dp_enable_rx_to_enhanced_mode(enhance_mode);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP rx_enhance_mode failed\n");
+ return -EAGAIN;
+ }
+
+ exynos_dp_enable_enhanced_mode(enhance_mode);
+
+ return ret;
+}
+
+static int exynos_dp_read_dpcd_lane_stat(struct edp_device_info *edp_info,
+ unsigned char *status)
+{
+ unsigned int ret, i;
+ unsigned char buf[2];
+ unsigned char lane_stat[DP_LANE_CNT_4] = {0,};
+ unsigned char shift_val[DP_LANE_CNT_4] = {0,};
+
+ shift_val[0] = 0;
+ shift_val[1] = 4;
+ shift_val[2] = 0;
+ shift_val[3] = 4;
+
+ ret = exynos_dp_read_bytes_from_dpcd(DPCD_LANE0_1_STATUS, 2, buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read lane status failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < edp_info->lane_cnt; i++) {
+ lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f;
+ if (lane_stat[0] != lane_stat[i]) {
+ printf("Wrong lane status\n");
+ return -EINVAL;
+ }
+ }
+
+ *status = lane_stat[0];
+
+ return ret;
+}
+
+static unsigned int exynos_dp_read_dpcd_adj_req(unsigned char lane_num,
+ unsigned char *sw, unsigned char *em)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+ unsigned char buf;
+ unsigned int dpcd_addr;
+ unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4};
+
+ /*lane_num value is used as arry index, so this range 0 ~ 3 */
+ dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2);
+
+ ret = exynos_dp_read_byte_from_dpcd(dpcd_addr, &buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read adjust request failed\n");
+ return -EAGAIN;
+ }
+
+ *sw = ((buf >> shift_val[lane_num]) & 0x03);
+ *em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2;
+
+ return ret;
+}
+
+static int exynos_dp_equalizer_err_link(struct edp_device_info *edp_info)
+{
+ int ret;
+
+ ret = exynos_dp_training_pattern_dis();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP training_patter_disable() failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ }
+
+ ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP set_enhanced_mode() failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ }
+
+ return ret;
+}
+
+static int exynos_dp_reduce_link_rate(struct edp_device_info *edp_info)
+{
+ int ret;
+
+ if (edp_info->lane_bw == DP_LANE_BW_2_70) {
+ edp_info->lane_bw = DP_LANE_BW_1_62;
+ printf("DP Change lane bw to 1.62Gbps\n");
+ edp_info->lt_info.lt_status = DP_LT_START;
+ ret = EXYNOS_DP_SUCCESS;
+ } else {
+ ret = exynos_dp_training_pattern_dis();
+ if (ret != EXYNOS_DP_SUCCESS)
+ printf("DP training_patter_disable() failed\n");
+
+ ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc);
+ if (ret != EXYNOS_DP_SUCCESS)
+ printf("DP set_enhanced_mode() failed\n");
+
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_process_clock_recovery(struct edp_device_info
+ *edp_info)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+ unsigned char lane_stat;
+ unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, };
+ unsigned int i;
+ unsigned char adj_req_sw;
+ unsigned char adj_req_em;
+ unsigned char buf[5];
+
+ debug("DP: %s was called\n", __func__);
+ mdelay(1);
+
+ ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read lane status failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ }
+
+ if (lane_stat & DP_LANE_STAT_CR_DONE) {
+ debug("DP clock Recovery training succeed\n");
+ exynos_dp_set_training_pattern(TRAINING_PTN2);
+
+ for (i = 0; i < edp_info->lane_cnt; i++) {
+ ret = exynos_dp_read_dpcd_adj_req(i, &adj_req_sw,
+ &adj_req_em);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ }
+
+ lt_ctl_val[i] = 0;
+ lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
+
+ if ((adj_req_sw == VOLTAGE_LEVEL_3)
+ || (adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
+ lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
+ MAX_PRE_EMPHASIS_REACH_3;
+ }
+ exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i);
+ }
+
+ buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2;
+ buf[1] = lt_ctl_val[0];
+ buf[2] = lt_ctl_val[1];
+ buf[3] = lt_ctl_val[2];
+ buf[4] = lt_ctl_val[3];
+
+ ret = exynos_dp_write_bytes_to_dpcd(
+ DPCD_TRAINING_PATTERN_SET, 5, buf);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP write traning pattern1 failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ } else
+ edp_info->lt_info.lt_status = DP_LT_ET;
+ } else {
+ for (i = 0; i < edp_info->lane_cnt; i++) {
+ lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(i);
+ ret = exynos_dp_read_dpcd_adj_req(i,
+ &adj_req_sw, &adj_req_em);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read adj req failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ }
+
+ if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
+ (adj_req_em == PRE_EMPHASIS_LEVEL_3))
+ ret = exynos_dp_reduce_link_rate(edp_info);
+
+ if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) ==
+ adj_req_sw) &&
+ (PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) ==
+ adj_req_em)) {
+ edp_info->lt_info.cr_loop[i]++;
+ if (edp_info->lt_info.cr_loop[i] == MAX_CR_LOOP)
+ ret = exynos_dp_reduce_link_rate(
+ edp_info);
+ }
+
+ lt_ctl_val[i] = 0;
+ lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
+
+ if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
+ (adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
+ lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 |
+ MAX_PRE_EMPHASIS_REACH_3;
+ }
+ exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i);
+ }
+
+ ret = exynos_dp_write_bytes_to_dpcd(
+ DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP write traning pattern2 failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_process_equalizer_training(struct edp_device_info
+ *edp_info)
+{
+ unsigned int ret = EXYNOS_DP_SUCCESS;
+ unsigned char lane_stat, adj_req_sw, adj_req_em, i;
+ unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,};
+ unsigned char interlane_aligned = 0;
+ unsigned char f_bw;
+ unsigned char f_lane_cnt;
+ unsigned char sink_stat;
+
+ mdelay(1);
+
+ ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read lane status failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ return ret;
+ }
+
+ debug("DP lane stat : %x\n", lane_stat);
+
+ if (lane_stat & DP_LANE_STAT_CR_DONE) {
+ ret = exynos_dp_read_byte_from_dpcd(DPCD_LN_ALIGN_UPDATED,
+ &sink_stat);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+
+ return ret;
+ }
+
+ interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE);
+
+ for (i = 0; i < edp_info->lane_cnt; i++) {
+ ret = exynos_dp_read_dpcd_adj_req(i,
+ &adj_req_sw, &adj_req_em);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP read adj req 1 failed\n");
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+
+ return ret;
+ }
+
+ lt_ctl_val[i] = 0;
+ lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw;
+
+ if ((adj_req_sw == VOLTAGE_LEVEL_3) ||
+ (adj_req_em == PRE_EMPHASIS_LEVEL_3)) {
+ lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3;
+ lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3;
+ }
+ }
+
+ if (((lane_stat&DP_LANE_STAT_CE_DONE) &&
+ (lane_stat&DP_LANE_STAT_SYM_LOCK))
+ && (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) {
+ debug("DP Equalizer training succeed\n");
+
+ f_bw = exynos_dp_get_link_bandwidth();
+ f_lane_cnt = exynos_dp_get_lane_count();
+
+ debug("DP final BandWidth : %x\n", f_bw);
+ debug("DP final Lane Count : %x\n", f_lane_cnt);
+
+ edp_info->lt_info.lt_status = DP_LT_FINISHED;
+
+ exynos_dp_equalizer_err_link(edp_info);
+
+ } else {
+ edp_info->lt_info.ep_loop++;
+
+ if (edp_info->lt_info.ep_loop > MAX_EQ_LOOP) {
+ if (edp_info->lane_bw == DP_LANE_BW_2_70) {
+ ret = exynos_dp_reduce_link_rate(
+ edp_info);
+ } else {
+ edp_info->lt_info.lt_status =
+ DP_LT_FAIL;
+ exynos_dp_equalizer_err_link(edp_info);
+ }
+ } else {
+ for (i = 0; i < edp_info->lane_cnt; i++)
+ exynos_dp_set_lanex_pre_emphasis(
+ lt_ctl_val[i], i);
+
+ ret = exynos_dp_write_bytes_to_dpcd(
+ DPCD_TRAINING_LANE0_SET,
+ 4, lt_ctl_val);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP set lt pattern failed\n");
+ edp_info->lt_info.lt_status =
+ DP_LT_FAIL;
+ exynos_dp_equalizer_err_link(edp_info);
+ }
+ }
+ }
+ } else if (edp_info->lane_bw == DP_LANE_BW_2_70) {
+ ret = exynos_dp_reduce_link_rate(edp_info);
+ } else {
+ edp_info->lt_info.lt_status = DP_LT_FAIL;
+ exynos_dp_equalizer_err_link(edp_info);
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_sw_link_training(struct edp_device_info *edp_info)
+{
+ unsigned int ret = 0;
+ int training_finished;
+
+ /* Turn off unnecessary lane */
+ if (edp_info->lane_cnt == 1)
+ exynos_dp_set_analog_power_down(CH1_BLOCK, 1);
+
+ training_finished = 0;
+
+ edp_info->lt_info.lt_status = DP_LT_START;
+
+ /* Process here */
+ while (!training_finished) {
+ switch (edp_info->lt_info.lt_status) {
+ case DP_LT_START:
+ ret = exynos_dp_link_start(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP LT:link start failed\n");
+ return ret;
+ }
+ break;
+ case DP_LT_CR:
+ ret = exynos_dp_process_clock_recovery(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP LT:clock recovery failed\n");
+ return ret;
+ }
+ break;
+ case DP_LT_ET:
+ ret = exynos_dp_process_equalizer_training(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP LT:equalizer training failed\n");
+ return ret;
+ }
+ break;
+ case DP_LT_FINISHED:
+ training_finished = 1;
+ break;
+ case DP_LT_FAIL:
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+static unsigned int exynos_dp_set_link_train(struct edp_device_info *edp_info)
+{
+ unsigned int ret;
+
+ exynos_dp_init_training();
+
+ ret = exynos_dp_sw_link_training(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS)
+ printf("DP dp_sw_link_traning() failed\n");
+
+ return ret;
+}
+
+static void exynos_dp_enable_scramble(unsigned int enable)
+{
+ unsigned char data;
+
+ if (enable) {
+ exynos_dp_enable_scrambling(DP_ENABLE);
+
+ exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET,
+ &data);
+ exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET,
+ (u8)(data & ~DPCD_SCRAMBLING_DISABLED));
+ } else {
+ exynos_dp_enable_scrambling(DP_DISABLE);
+ exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET,
+ &data);
+ exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET,
+ (u8)(data | DPCD_SCRAMBLING_DISABLED));
+ }
+}
+
+static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info)
+{
+ unsigned int ret = 0;
+ unsigned int retry_cnt;
+
+ mdelay(1);
+
+ if (edp_info->video_info.master_mode) {
+ printf("DP does not support master mode\n");
+ return -ENODEV;
+ } else {
+ /* debug slave */
+ exynos_dp_config_video_slave_mode(&edp_info->video_info);
+ }
+
+ exynos_dp_set_video_color_format(&edp_info->video_info);
+
+ if (edp_info->video_info.bist_mode) {
+ if (exynos_dp_config_video_bist(edp_info) != 0)
+ return -1;
+ }
+
+ ret = exynos_dp_get_pll_lock_status();
+ if (ret != PLL_LOCKED) {
+ printf("DP PLL is not locked yet\n");
+ return -EIO;
+ }
+
+ if (edp_info->video_info.master_mode == 0) {
+ retry_cnt = 10;
+ while (retry_cnt) {
+ ret = exynos_dp_is_slave_video_stream_clock_on();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP stream_clock_on failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ mdelay(1);
+ } else
+ break;
+ }
+ }
+
+ /* Set to use the register calculated M/N video */
+ exynos_dp_set_video_cr_mn(CALCULATED_M, 0, 0);
+
+ /* For video bist, Video timing must be generated by register */
+ exynos_dp_set_video_timing_mode(VIDEO_TIMING_FROM_CAPTURE);
+
+ /* Enable video bist */
+ if (edp_info->video_info.bist_pattern != COLOR_RAMP &&
+ edp_info->video_info.bist_pattern != BALCK_WHITE_V_LINES &&
+ edp_info->video_info.bist_pattern != COLOR_SQUARE)
+ exynos_dp_enable_video_bist(edp_info->video_info.bist_mode);
+ else
+ exynos_dp_enable_video_bist(DP_DISABLE);
+
+ /* Disable video mute */
+ exynos_dp_enable_video_mute(DP_DISABLE);
+
+ /* Configure video Master or Slave mode */
+ exynos_dp_enable_video_master(edp_info->video_info.master_mode);
+
+ /* Enable video */
+ exynos_dp_start_video();
+
+ if (edp_info->video_info.master_mode == 0) {
+ retry_cnt = 100;
+ while (retry_cnt) {
+ ret = exynos_dp_is_video_stream_on();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP Timeout of video stream\n");
+ return ret;
+ }
+ retry_cnt--;
+ mdelay(5);
+ } else
+ break;
+ }
+ }
+
+ return ret;
+}
+
+unsigned int exynos_init_dp(void)
+{
+ unsigned int ret;
+ struct edp_device_info *edp_info;
+ struct edp_disp_info disp_info;
+
+ edp_info = kzalloc(sizeof(struct edp_device_info), GFP_KERNEL);
+ if (!edp_info) {
+ debug("failed to allocate edp device object.\n");
+ return -EFAULT;
+ }
+
+ edp_info = dp_pd->edp_dev_info;
+ if (edp_info == NULL) {
+ debug("failed to get edp_info data.\n");
+ return -EFAULT;
+ }
+ disp_info = edp_info->disp_info;
+
+ exynos_dp_disp_info(&edp_info->disp_info);
+
+ if (dp_pd->phy_enable)
+ dp_pd->phy_enable(1);
+
+ ret = exynos_dp_init_dp();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP exynos_dp_init_dp() failed\n");
+ return ret;
+ }
+
+ ret = exynos_dp_handle_edid(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("EDP handle_edid fail\n");
+ return ret;
+ }
+
+ ret = exynos_dp_set_link_train(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP link training fail\n");
+ return ret;
+ }
+
+ exynos_dp_enable_scramble(DP_ENABLE);
+ exynos_dp_enable_rx_to_enhanced_mode(DP_ENABLE);
+ exynos_dp_enable_enhanced_mode(DP_ENABLE);
+
+ exynos_dp_set_link_bandwidth(edp_info->lane_bw);
+ exynos_dp_set_lane_count(edp_info->lane_cnt);
+
+ exynos_dp_init_video();
+ ret = exynos_dp_config_video(edp_info);
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("Exynos DP init failed\n");
+ return ret;
+ }
+
+ printf("Exynos DP init done\n");
+
+ return ret;
+}
+
+void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd)
+{
+ if (pd == NULL) {
+ debug("pd is NULL\n");
+ return;
+ }
+
+ dp_pd = pd;
+}
diff --git a/drivers/video/exynos_dp_lowlevel.c b/drivers/video/exynos_dp_lowlevel.c
new file mode 100644
index 0000000000..7b54c8084c
--- /dev/null
+++ b/drivers/video/exynos_dp_lowlevel.c
@@ -0,0 +1,1291 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <config.h>
+#include <common.h>
+#include <linux/err.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/dp_info.h>
+#include <asm/arch/dp.h>
+
+static void exynos_dp_enable_video_input(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl1);
+ reg &= ~VIDEO_EN_MASK;
+
+ /* enable video input*/
+ if (enable)
+ reg |= VIDEO_EN_MASK;
+
+ writel(reg, &dp_regs->video_ctl1);
+
+ return;
+}
+
+void exynos_dp_enable_video_bist(unsigned int enable)
+{
+ /*enable video bist*/
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl4);
+ reg &= ~VIDEO_BIST_MASK;
+
+ /*enable video bist*/
+ if (enable)
+ reg |= VIDEO_BIST_MASK;
+
+ writel(reg, &dp_regs->video_ctl4);
+
+ return;
+}
+
+void exynos_dp_enable_video_mute(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl1);
+ reg &= ~(VIDEO_MUTE_MASK);
+ if (enable)
+ reg |= VIDEO_MUTE_MASK;
+
+ writel(reg, &dp_regs->video_ctl1);
+
+ return;
+}
+
+
+static void exynos_dp_init_analog_param(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /*
+ * Set termination
+ * Normal bandgap, Normal swing, Tx terminal registor 61 ohm
+ * 24M Phy clock, TX digital logic power is 100:1.0625V
+ */
+ reg = SEL_BG_NEW_BANDGAP | TX_TERMINAL_CTRL_61_OHM |
+ SWING_A_30PER_G_NORMAL;
+ writel(reg, &dp_regs->analog_ctl1);
+
+ reg = SEL_24M | TX_DVDD_BIT_1_0625V;
+ writel(reg, &dp_regs->analog_ctl2);
+
+ /*
+ * Set power source for internal clk driver to 1.0625v.
+ * Select current reference of TX driver current to 00:Ipp/2+Ic/2.
+ * Set VCO range of PLL +- 0uA
+ */
+ reg = DRIVE_DVDD_BIT_1_0625V | SEL_CURRENT_DEFAULT | VCO_BIT_000_MICRO;
+ writel(reg, &dp_regs->analog_ctl3);
+
+ /*
+ * Set AUX TX terminal resistor to 102 ohm
+ * Set AUX channel amplitude control
+ */
+ reg = PD_RING_OSC | AUX_TERMINAL_CTRL_52_OHM | TX_CUR1_2X | TX_CUR_4_MA;
+ writel(reg, &dp_regs->pll_filter_ctl1);
+
+ /*
+ * PLL loop filter bandwidth
+ * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz
+ * PLL digital power select: 1.2500V
+ */
+ reg = CH3_AMP_0_MV | CH2_AMP_0_MV | CH1_AMP_0_MV | CH0_AMP_0_MV;
+
+ writel(reg, &dp_regs->amp_tuning_ctl);
+
+ /*
+ * PLL loop filter bandwidth
+ * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz
+ * PLL digital power select: 1.1250V
+ */
+ reg = DP_PLL_LOOP_BIT_DEFAULT | DP_PLL_REF_BIT_1_1250V;
+ writel(reg, &dp_regs->pll_ctl);
+}
+
+static void exynos_dp_init_interrupt(void)
+{
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ /* Set interrupt registers to initial states */
+
+ /*
+ * Disable interrupt
+ * INT pin assertion polarity. It must be configured
+ * correctly according to ICU setting.
+ * 1 = assert high, 0 = assert low
+ */
+ writel(INT_POL, &dp_regs->int_ctl);
+
+ /* Clear pending regisers */
+ writel(0xff, &dp_regs->common_int_sta1);
+ writel(0xff, &dp_regs->common_int_sta2);
+ writel(0xff, &dp_regs->common_int_sta3);
+ writel(0xff, &dp_regs->common_int_sta4);
+ writel(0xff, &dp_regs->int_sta);
+
+ /* 0:mask,1: unmask */
+ writel(0x00, &dp_regs->int_sta_mask1);
+ writel(0x00, &dp_regs->int_sta_mask2);
+ writel(0x00, &dp_regs->int_sta_mask3);
+ writel(0x00, &dp_regs->int_sta_mask4);
+ writel(0x00, &dp_regs->int_sta_mask);
+}
+
+void exynos_dp_reset(void)
+{
+ unsigned int reg_func_1;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /*dp tx sw reset*/
+ writel(RESET_DP_TX, &dp_regs->tx_sw_reset);
+
+ exynos_dp_enable_video_input(DP_DISABLE);
+ exynos_dp_enable_video_bist(DP_DISABLE);
+ exynos_dp_enable_video_mute(DP_DISABLE);
+
+ /* software reset */
+ reg_func_1 = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
+ AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
+ HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+
+ writel(reg_func_1, &dp_regs->func_en1);
+ writel(reg_func_1, &dp_regs->func_en2);
+
+ mdelay(1);
+
+ exynos_dp_init_analog_param();
+ exynos_dp_init_interrupt();
+
+ return;
+}
+
+void exynos_dp_enable_sw_func(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->func_en1);
+ reg &= ~(SW_FUNC_EN_N);
+
+ if (!enable)
+ reg |= SW_FUNC_EN_N;
+
+ writel(reg, &dp_regs->func_en1);
+
+ return;
+}
+
+unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->phy_pd);
+ switch (block) {
+ case AUX_BLOCK:
+ reg &= ~(AUX_PD);
+ if (enable)
+ reg |= AUX_PD;
+ break;
+ case CH0_BLOCK:
+ reg &= ~(CH0_PD);
+ if (enable)
+ reg |= CH0_PD;
+ break;
+ case CH1_BLOCK:
+ reg &= ~(CH1_PD);
+ if (enable)
+ reg |= CH1_PD;
+ break;
+ case CH2_BLOCK:
+ reg &= ~(CH2_PD);
+ if (enable)
+ reg |= CH2_PD;
+ break;
+ case CH3_BLOCK:
+ reg &= ~(CH3_PD);
+ if (enable)
+ reg |= CH3_PD;
+ break;
+ case ANALOG_TOTAL:
+ reg &= ~PHY_PD;
+ if (enable)
+ reg |= PHY_PD;
+ break;
+ case POWER_ALL:
+ reg &= ~(PHY_PD | AUX_PD | CH0_PD | CH1_PD | CH2_PD |
+ CH3_PD);
+ if (enable)
+ reg |= (PHY_PD | AUX_PD | CH0_PD | CH1_PD |
+ CH2_PD | CH3_PD);
+ break;
+ default:
+ printf("DP undefined block number : %d\n", block);
+ return -1;
+ }
+
+ writel(reg, &dp_regs->phy_pd);
+
+ return 0;
+}
+
+unsigned int exynos_dp_get_pll_lock_status(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->debug_ctl);
+
+ if (reg & PLL_LOCK)
+ return PLL_LOCKED;
+ else
+ return PLL_UNLOCKED;
+}
+
+static void exynos_dp_set_pll_power(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->pll_ctl);
+ reg &= ~(DP_PLL_PD);
+
+ if (!enable)
+ reg |= DP_PLL_PD;
+
+ writel(reg, &dp_regs->pll_ctl);
+}
+
+int exynos_dp_init_analog_func(void)
+{
+ int ret = EXYNOS_DP_SUCCESS;
+ unsigned int retry_cnt = 10;
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /*Power On All Analog block */
+ exynos_dp_set_analog_power_down(POWER_ALL, DP_DISABLE);
+
+ reg = PLL_LOCK_CHG;
+ writel(reg, &dp_regs->common_int_sta1);
+
+ reg = readl(&dp_regs->debug_ctl);
+ reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+ writel(reg, &dp_regs->debug_ctl);
+
+ /*Assert DP PLL Reset*/
+ reg = readl(&dp_regs->pll_ctl);
+ reg |= DP_PLL_RESET;
+ writel(reg, &dp_regs->pll_ctl);
+
+ mdelay(1);
+
+ /*Deassert DP PLL Reset*/
+ reg = readl(&dp_regs->pll_ctl);
+ reg &= ~(DP_PLL_RESET);
+ writel(reg, &dp_regs->pll_ctl);
+
+ exynos_dp_set_pll_power(DP_ENABLE);
+
+ while (exynos_dp_get_pll_lock_status() == PLL_UNLOCKED) {
+ mdelay(1);
+ retry_cnt--;
+ if (retry_cnt == 0) {
+ printf("DP dp's pll lock failed : retry : %d\n",
+ retry_cnt);
+ return -EINVAL;
+ }
+ }
+
+ debug("dp's pll lock success(%d)\n", retry_cnt);
+
+ /* Enable Serdes FIFO function and Link symbol clock domain module */
+ reg = readl(&dp_regs->func_en2);
+ reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+ | AUX_FUNC_EN_N);
+ writel(reg, &dp_regs->func_en2);
+
+ return ret;
+}
+
+void exynos_dp_init_hpd(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear interrupts releated to Hot Plug Dectect */
+ reg = HOTPLUG_CHG | HPD_LOST | PLUG;
+ writel(reg, &dp_regs->common_int_sta4);
+
+ reg = INT_HPD;
+ writel(reg, &dp_regs->int_sta);
+
+ reg = readl(&dp_regs->sys_ctl3);
+ reg &= ~(F_HPD | HPD_CTRL);
+ writel(reg, &dp_regs->sys_ctl3);
+
+ return;
+}
+
+static inline void exynos_dp_reset_aux(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Disable AUX channel module */
+ reg = readl(&dp_regs->func_en2);
+ reg |= AUX_FUNC_EN_N;
+ writel(reg, &dp_regs->func_en2);
+
+ return;
+}
+
+void exynos_dp_init_aux(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear inerrupts related to AUX channel */
+ reg = RPLY_RECEIV | AUX_ERR;
+ writel(reg, &dp_regs->int_sta);
+
+ exynos_dp_reset_aux();
+
+ /* Disable AUX transaction H/W retry */
+ reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(3)|
+ AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+ writel(reg, &dp_regs->aux_hw_retry_ctl);
+
+ /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+ reg = DEFER_CTRL_EN | DEFER_COUNT(1);
+ writel(reg, &dp_regs->aux_ch_defer_ctl);
+
+ /* Enable AUX channel module */
+ reg = readl(&dp_regs->func_en2);
+ reg &= ~AUX_FUNC_EN_N;
+ writel(reg, &dp_regs->func_en2);
+
+ return;
+}
+
+void exynos_dp_config_interrupt(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* 0: mask, 1: unmask */
+ reg = COMMON_INT_MASK_1;
+ writel(reg, &dp_regs->common_int_mask1);
+
+ reg = COMMON_INT_MASK_2;
+ writel(reg, &dp_regs->common_int_mask2);
+
+ reg = COMMON_INT_MASK_3;
+ writel(reg, &dp_regs->common_int_mask3);
+
+ reg = COMMON_INT_MASK_4;
+ writel(reg, &dp_regs->common_int_mask4);
+
+ reg = INT_STA_MASK;
+ writel(reg, &dp_regs->int_sta_mask);
+
+ return;
+}
+
+unsigned int exynos_dp_get_plug_in_status(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->sys_ctl3);
+ if (reg & HPD_STATUS)
+ return 0;
+
+ return -1;
+}
+
+unsigned int exynos_dp_detect_hpd(void)
+{
+ int timeout_loop = DP_TIMEOUT_LOOP_COUNT;
+
+ mdelay(2);
+
+ while (exynos_dp_get_plug_in_status() != 0) {
+ if (timeout_loop == 0)
+ return -EINVAL;
+ mdelay(10);
+ timeout_loop--;
+ }
+
+ return EXYNOS_DP_SUCCESS;
+}
+
+unsigned int exynos_dp_start_aux_transaction(void)
+{
+ unsigned int reg;
+ unsigned int ret = 0;
+ unsigned int retry_cnt;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Enable AUX CH operation */
+ reg = readl(&dp_regs->aux_ch_ctl2);
+ reg |= AUX_EN;
+ writel(reg, &dp_regs->aux_ch_ctl2);
+
+ retry_cnt = 10;
+ while (retry_cnt) {
+ reg = readl(&dp_regs->int_sta);
+ if (!(reg & RPLY_RECEIV)) {
+ if (retry_cnt == 0) {
+ printf("DP Reply Timeout!!\n");
+ ret = -EAGAIN;
+ return ret;
+ }
+ mdelay(1);
+ retry_cnt--;
+ } else
+ break;
+ }
+
+ /* Clear interrupt source for AUX CH command reply */
+ writel(reg, &dp_regs->int_sta);
+
+ /* Clear interrupt source for AUX CH access error */
+ reg = readl(&dp_regs->int_sta);
+ if (reg & AUX_ERR) {
+ printf("DP Aux Access Error\n");
+ writel(AUX_ERR, &dp_regs->int_sta);
+ ret = -EAGAIN;
+ return ret;
+ }
+
+ /* Check AUX CH error access status */
+ reg = readl(&dp_regs->aux_ch_sta);
+ if ((reg & AUX_STATUS_MASK) != 0) {
+ debug("DP AUX CH error happens: %x\n", reg & AUX_STATUS_MASK);
+ ret = -EAGAIN;
+ return ret;
+ }
+
+ return EXYNOS_DP_SUCCESS;
+}
+
+unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr,
+ unsigned char data)
+{
+ unsigned int reg, ret;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr);
+ writel(reg, &dp_regs->aux_addr_19_16);
+
+ /* Write data buffer */
+ reg = (unsigned int)data;
+ writel(reg, &dp_regs->buf_data0);
+
+ /*
+ * Set DisplayPort transaction and write 1 byte
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ ret = exynos_dp_start_aux_transaction();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printf("DP Aux transaction failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr,
+ unsigned char *data)
+{
+ unsigned int reg;
+ int retval;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr);
+ writel(reg, &dp_regs->aux_addr_19_16);
+
+ /*
+ * Set DisplayPort transaction and read 1 byte
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (!retval)
+ debug("DP Aux Transaction fail!\n");
+
+ /* Read data buffer */
+ reg = readl(&dp_regs->buf_data0);
+ *data = (unsigned char)(reg & 0xff);
+
+ return retval;
+}
+
+unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[])
+{
+ unsigned int reg;
+ unsigned int start_offset;
+ unsigned int cur_data_count;
+ unsigned int cur_data_idx;
+ unsigned int retry_cnt;
+ unsigned int ret = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ start_offset = 0;
+ while (start_offset < count) {
+ /* Buffer size of AUX CH is 16 * 4bytes */
+ if ((count - start_offset) > 16)
+ cur_data_count = 16;
+ else
+ cur_data_count = count - start_offset;
+
+ retry_cnt = 5;
+ while (retry_cnt) {
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_19_16);
+
+ for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+ cur_data_idx++) {
+ reg = data[start_offset + cur_data_idx];
+ writel(reg, (unsigned int)&dp_regs->buf_data0 +
+ (4 * cur_data_idx));
+ }
+ /*
+ * Set DisplayPort transaction and write
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(cur_data_count) |
+ AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ ret = exynos_dp_start_aux_transaction();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP Aux Transaction failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ } else
+ break;
+ }
+ start_offset += cur_data_count;
+ }
+
+ return ret;
+}
+
+unsigned int exynos_dp_read_bytes_from_dpcd(unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[])
+{
+ unsigned int reg;
+ unsigned int start_offset;
+ unsigned int cur_data_count;
+ unsigned int cur_data_idx;
+ unsigned int retry_cnt;
+ unsigned int ret = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ start_offset = 0;
+ while (start_offset < count) {
+ /* Buffer size of AUX CH is 16 * 4bytes */
+ if ((count - start_offset) > 16)
+ cur_data_count = 16;
+ else
+ cur_data_count = count - start_offset;
+
+ retry_cnt = 5;
+ while (retry_cnt) {
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_7_0);
+ reg = AUX_ADDR_15_8(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_15_8);
+ reg = AUX_ADDR_19_16(reg_addr + start_offset);
+ writel(reg, &dp_regs->aux_addr_19_16);
+ /*
+ * Set DisplayPort transaction and read
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(cur_data_count) |
+ AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ ret = exynos_dp_start_aux_transaction();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ if (retry_cnt == 0) {
+ printf("DP Aux Transaction failed\n");
+ return ret;
+ }
+ retry_cnt--;
+ } else
+ break;
+ }
+
+ for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+ cur_data_idx++) {
+ reg = readl((unsigned int)&dp_regs->buf_data0 +
+ 4 * cur_data_idx);
+ data[start_offset + cur_data_idx] = (unsigned char)reg;
+ }
+
+ start_offset += cur_data_count;
+ }
+
+ return ret;
+}
+
+int exynos_dp_select_i2c_device(unsigned int device_addr,
+ unsigned int reg_addr)
+{
+ unsigned int reg;
+ int retval;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Set EDID device address */
+ reg = device_addr;
+ writel(reg, &dp_regs->aux_addr_7_0);
+ writel(0x0, &dp_regs->aux_addr_15_8);
+ writel(0x0, &dp_regs->aux_addr_19_16);
+
+ /* Set offset from base address of EDID device */
+ writel(reg_addr, &dp_regs->buf_data0);
+
+ /*
+ * Set I2C transaction and write address
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+ AUX_TX_COMM_WRITE;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (retval != 0)
+ printf("%s: DP Aux Transaction fail!\n", __func__);
+
+ return retval;
+}
+
+int exynos_dp_read_byte_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr,
+ unsigned int *data)
+{
+ unsigned int reg;
+ int i;
+ int retval;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ for (i = 0; i < 10; i++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Select EDID device */
+ retval = exynos_dp_select_i2c_device(device_addr, reg_addr);
+ if (retval != 0) {
+ printf("DP Select EDID device fail. retry !\n");
+ continue;
+ }
+
+ /*
+ * Set I2C transaction and read data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (retval != EXYNOS_DP_SUCCESS)
+ printf("%s: DP Aux Transaction fail!\n", __func__);
+ }
+
+ /* Read data */
+ if (retval == 0)
+ *data = readl(&dp_regs->buf_data0);
+
+ return retval;
+}
+
+int exynos_dp_read_bytes_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr, unsigned int count, unsigned char edid[])
+{
+ unsigned int reg;
+ unsigned int i, j;
+ unsigned int cur_data_idx;
+ unsigned int defer = 0;
+ int retval = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ for (i = 0; i < count; i += 16) { /* use 16 burst */
+ for (j = 0; j < 100; j++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, &dp_regs->buffer_data_ctl);
+
+ /* Set normal AUX CH command */
+ reg = readl(&dp_regs->aux_ch_ctl2);
+ reg &= ~ADDR_ONLY;
+ writel(reg, &dp_regs->aux_ch_ctl2);
+
+ /*
+ * If Rx sends defer, Tx sends only reads
+ * request without sending addres
+ */
+ if (!defer)
+ retval =
+ exynos_dp_select_i2c_device(device_addr,
+ reg_addr + i);
+ else
+ defer = 0;
+
+ if (retval == EXYNOS_DP_SUCCESS) {
+ /*
+ * Set I2C transaction and write data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(16) |
+ AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(reg, &dp_regs->aux_ch_ctl1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction();
+ if (retval == 0)
+ break;
+ else
+ printf("DP Aux Transaction fail!\n");
+ }
+ /* Check if Rx sends defer */
+ reg = readl(&dp_regs->aux_rx_comm);
+ if (reg == AUX_RX_COMM_AUX_DEFER ||
+ reg == AUX_RX_COMM_I2C_DEFER) {
+ printf("DP Defer: %d\n\n", reg);
+ defer = 1;
+ }
+ }
+
+ for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+ reg = readl((unsigned int)&dp_regs->buf_data0
+ + 4 * cur_data_idx);
+ edid[i + cur_data_idx] = (unsigned char)reg;
+ }
+ }
+
+ return retval;
+}
+
+void exynos_dp_reset_macro(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->phy_test);
+ reg |= MACRO_RST;
+ writel(reg, &dp_regs->phy_test);
+
+ /* 10 us is the minimum Macro reset time. */
+ mdelay(1);
+
+ reg &= ~MACRO_RST;
+ writel(reg, &dp_regs->phy_test);
+}
+
+void exynos_dp_set_link_bandwidth(unsigned char bwtype)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = (unsigned int)bwtype;
+
+ /* Set bandwidth to 2.7G or 1.62G */
+ if ((bwtype == DP_LANE_BW_1_62) || (bwtype == DP_LANE_BW_2_70))
+ writel(reg, &dp_regs->link_bw_set);
+}
+
+unsigned char exynos_dp_get_link_bandwidth(void)
+{
+ unsigned char ret;
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->link_bw_set);
+ ret = (unsigned char)reg;
+
+ return ret;
+}
+
+void exynos_dp_set_lane_count(unsigned char count)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = (unsigned int)count;
+
+ if ((count == DP_LANE_CNT_1) || (count == DP_LANE_CNT_2) ||
+ (count == DP_LANE_CNT_4))
+ writel(reg, &dp_regs->lane_count_set);
+}
+
+unsigned int exynos_dp_get_lane_count(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->lane_count_set);
+
+ return reg;
+}
+
+unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt)
+{
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ unsigned int reg_list[DP_LANE_CNT_4] = {
+ (unsigned int)&dp_regs->ln0_link_training_ctl,
+ (unsigned int)&dp_regs->ln1_link_training_ctl,
+ (unsigned int)&dp_regs->ln2_link_training_ctl,
+ (unsigned int)&dp_regs->ln3_link_training_ctl,
+ };
+
+ return readl(reg_list[lanecnt]);
+}
+
+void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val,
+ unsigned char lanecnt)
+{
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ unsigned int reg_list[DP_LANE_CNT_4] = {
+ (unsigned int)&dp_regs->ln0_link_training_ctl,
+ (unsigned int)&dp_regs->ln1_link_training_ctl,
+ (unsigned int)&dp_regs->ln2_link_training_ctl,
+ (unsigned int)&dp_regs->ln3_link_training_ctl,
+ };
+
+ writel(request_val, reg_list[lanecnt]);
+}
+
+void exynos_dp_set_lane_pre_emphasis(unsigned int level, unsigned char lanecnt)
+{
+ unsigned char i;
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+ unsigned int reg_list[DP_LANE_CNT_4] = {
+ (unsigned int)&dp_regs->ln0_link_training_ctl,
+ (unsigned int)&dp_regs->ln1_link_training_ctl,
+ (unsigned int)&dp_regs->ln2_link_training_ctl,
+ (unsigned int)&dp_regs->ln3_link_training_ctl,
+ };
+ unsigned int reg_shift[DP_LANE_CNT_4] = {
+ PRE_EMPHASIS_SET_0_SHIFT,
+ PRE_EMPHASIS_SET_1_SHIFT,
+ PRE_EMPHASIS_SET_2_SHIFT,
+ PRE_EMPHASIS_SET_3_SHIFT
+ };
+
+ for (i = 0; i < lanecnt; i++) {
+ reg = level << reg_shift[i];
+ writel(reg, reg_list[i]);
+ }
+}
+
+void exynos_dp_set_training_pattern(unsigned int pattern)
+{
+ unsigned int reg = 0;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ switch (pattern) {
+ case PRBS7:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+ break;
+ case D10_2:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+ break;
+ case TRAINING_PTN1:
+ reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+ break;
+ case TRAINING_PTN2:
+ reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+ break;
+ case DP_NONE:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_DISABLE |
+ SW_TRAINING_PATTERN_SET_NORMAL;
+ break;
+ default:
+ break;
+ }
+
+ writel(reg, &dp_regs->training_ptn_set);
+}
+
+void exynos_dp_enable_enhanced_mode(unsigned char enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->sys_ctl4);
+ reg &= ~ENHANCED;
+
+ if (enable)
+ reg |= ENHANCED;
+
+ writel(reg, &dp_regs->sys_ctl4);
+}
+
+void exynos_dp_enable_scrambling(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->training_ptn_set);
+ reg &= ~(SCRAMBLING_DISABLE);
+
+ if (!enable)
+ reg |= SCRAMBLING_DISABLE;
+
+ writel(reg, &dp_regs->training_ptn_set);
+}
+
+int exynos_dp_init_video(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Clear VID_CLK_CHG[1] and VID_FORMAT_CHG[3] and VSYNC_DET[7] */
+ reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+ writel(reg, &dp_regs->common_int_sta1);
+
+ /* I_STRM__CLK detect : DE_CTL : Auto detect */
+ reg &= ~DET_CTRL;
+ writel(reg, &dp_regs->sys_ctl1);
+
+ return 0;
+}
+
+void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Video Slave mode setting */
+ reg = readl(&dp_regs->func_en1);
+ reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
+ reg |= MASTER_VID_FUNC_EN_N;
+ writel(reg, &dp_regs->func_en1);
+
+ /* Configure Interlaced for slave mode video */
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~INTERACE_SCAN_CFG;
+ reg |= (video_info->interlaced << INTERACE_SCAN_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+
+ /* Configure V sync polarity for slave mode video */
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~VSYNC_POLARITY_CFG;
+ reg |= (video_info->v_sync_polarity << V_S_POLARITY_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+
+ /* Configure H sync polarity for slave mode video */
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~HSYNC_POLARITY_CFG;
+ reg |= (video_info->h_sync_polarity << H_S_POLARITY_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+
+ /*Set video mode to slave mode */
+ reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
+ writel(reg, &dp_regs->soc_general_ctl);
+}
+
+void exynos_dp_set_video_color_format(struct edp_video_info *video_info)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Configure the input color depth, color space, dynamic range */
+ reg = (video_info->dynamic_range << IN_D_RANGE_SHIFT) |
+ (video_info->color_depth << IN_BPC_SHIFT) |
+ (video_info->color_space << IN_COLOR_F_SHIFT);
+ writel(reg, &dp_regs->video_ctl2);
+
+ /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+ reg = readl(&dp_regs->video_ctl3);
+ reg &= ~IN_YC_COEFFI_MASK;
+ if (video_info->ycbcr_coeff)
+ reg |= IN_YC_COEFFI_ITU709;
+ else
+ reg |= IN_YC_COEFFI_ITU601;
+ writel(reg, &dp_regs->video_ctl3);
+}
+
+int exynos_dp_config_video_bist(struct edp_device_info *edp_info)
+{
+ unsigned int reg;
+ unsigned int bist_type = 0;
+ struct edp_video_info video_info = edp_info->video_info;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* For master mode, you don't need to set the video format */
+ if (video_info.master_mode == 0) {
+ writel(TOTAL_LINE_CFG_L(edp_info->disp_info.v_total),
+ &dp_regs->total_ln_cfg_l);
+ writel(TOTAL_LINE_CFG_H(edp_info->disp_info.v_total),
+ &dp_regs->total_ln_cfg_h);
+ writel(ACTIVE_LINE_CFG_L(edp_info->disp_info.v_res),
+ &dp_regs->active_ln_cfg_l);
+ writel(ACTIVE_LINE_CFG_H(edp_info->disp_info.v_res),
+ &dp_regs->active_ln_cfg_h);
+ writel(edp_info->disp_info.v_sync_width,
+ &dp_regs->vsw_cfg);
+ writel(edp_info->disp_info.v_back_porch,
+ &dp_regs->vbp_cfg);
+ writel(edp_info->disp_info.v_front_porch,
+ &dp_regs->vfp_cfg);
+
+ writel(TOTAL_PIXEL_CFG_L(edp_info->disp_info.h_total),
+ &dp_regs->total_pix_cfg_l);
+ writel(TOTAL_PIXEL_CFG_H(edp_info->disp_info.h_total),
+ &dp_regs->total_pix_cfg_h);
+ writel(ACTIVE_PIXEL_CFG_L(edp_info->disp_info.h_res),
+ &dp_regs->active_pix_cfg_l);
+ writel(ACTIVE_PIXEL_CFG_H(edp_info->disp_info.h_res),
+ &dp_regs->active_pix_cfg_h);
+ writel(H_F_PORCH_CFG_L(edp_info->disp_info.h_front_porch),
+ &dp_regs->hfp_cfg_l);
+ writel(H_F_PORCH_CFG_H(edp_info->disp_info.h_front_porch),
+ &dp_regs->hfp_cfg_h);
+ writel(H_SYNC_PORCH_CFG_L(edp_info->disp_info.h_sync_width),
+ &dp_regs->hsw_cfg_l);
+ writel(H_SYNC_PORCH_CFG_H(edp_info->disp_info.h_sync_width),
+ &dp_regs->hsw_cfg_h);
+ writel(H_B_PORCH_CFG_L(edp_info->disp_info.h_back_porch),
+ &dp_regs->hbp_cfg_l);
+ writel(H_B_PORCH_CFG_H(edp_info->disp_info.h_back_porch),
+ &dp_regs->hbp_cfg_h);
+
+ /*
+ * Set SLAVE_I_SCAN_CFG[2], VSYNC_P_CFG[1],
+ * HSYNC_P_CFG[0] properly
+ */
+ reg = (video_info.interlaced << INTERACE_SCAN_CFG_SHIFT |
+ video_info.v_sync_polarity << V_S_POLARITY_CFG_SHIFT |
+ video_info.h_sync_polarity << H_S_POLARITY_CFG_SHIFT);
+ writel(reg, &dp_regs->video_ctl10);
+ }
+
+ /* BIST color bar width set--set to each bar is 32 pixel width */
+ switch (video_info.bist_pattern) {
+ case COLORBAR_32:
+ bist_type = BIST_WIDTH_BAR_32_PIXEL |
+ BIST_TYPE_COLOR_BAR;
+ break;
+ case COLORBAR_64:
+ bist_type = BIST_WIDTH_BAR_64_PIXEL |
+ BIST_TYPE_COLOR_BAR;
+ break;
+ case WHITE_GRAY_BALCKBAR_32:
+ bist_type = BIST_WIDTH_BAR_32_PIXEL |
+ BIST_TYPE_WHITE_GRAY_BLACK_BAR;
+ break;
+ case WHITE_GRAY_BALCKBAR_64:
+ bist_type = BIST_WIDTH_BAR_64_PIXEL |
+ BIST_TYPE_WHITE_GRAY_BLACK_BAR;
+ break;
+ case MOBILE_WHITEBAR_32:
+ bist_type = BIST_WIDTH_BAR_32_PIXEL |
+ BIST_TYPE_MOBILE_WHITE_BAR;
+ break;
+ case MOBILE_WHITEBAR_64:
+ bist_type = BIST_WIDTH_BAR_64_PIXEL |
+ BIST_TYPE_MOBILE_WHITE_BAR;
+ break;
+ default:
+ return -1;
+ }
+
+ reg = bist_type;
+ writel(reg, &dp_regs->video_ctl4);
+
+ return 0;
+}
+
+unsigned int exynos_dp_is_slave_video_stream_clock_on(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Update Video stream clk detect status */
+ reg = readl(&dp_regs->sys_ctl1);
+ writel(reg, &dp_regs->sys_ctl1);
+
+ reg = readl(&dp_regs->sys_ctl1);
+
+ if (!(reg & DET_STA)) {
+ debug("DP Input stream clock not detected.\n");
+ return -EIO;
+ }
+
+ return EXYNOS_DP_SUCCESS;
+}
+
+void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value,
+ unsigned int n_value)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ if (type == REGISTER_M) {
+ reg = readl(&dp_regs->sys_ctl4);
+ reg |= FIX_M_VID;
+ writel(reg, &dp_regs->sys_ctl4);
+ reg = M_VID0_CFG(m_value);
+ writel(reg, &dp_regs->m_vid0);
+ reg = M_VID1_CFG(m_value);
+ writel(reg, &dp_regs->m_vid1);
+ reg = M_VID2_CFG(m_value);
+ writel(reg, &dp_regs->m_vid2);
+
+ reg = N_VID0_CFG(n_value);
+ writel(reg, &dp_regs->n_vid0);
+ reg = N_VID1_CFG(n_value);
+ writel(reg, &dp_regs->n_vid1);
+ reg = N_VID2_CFG(n_value);
+ writel(reg, &dp_regs->n_vid2);
+ } else {
+ reg = readl(&dp_regs->sys_ctl4);
+ reg &= ~FIX_M_VID;
+ writel(reg, &dp_regs->sys_ctl4);
+ }
+}
+
+void exynos_dp_set_video_timing_mode(unsigned int type)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->video_ctl10);
+ reg &= ~FORMAT_SEL;
+
+ if (type != VIDEO_TIMING_FROM_CAPTURE)
+ reg |= FORMAT_SEL;
+
+ writel(reg, &dp_regs->video_ctl10);
+}
+
+void exynos_dp_enable_video_master(unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ reg = readl(&dp_regs->soc_general_ctl);
+ if (enable) {
+ reg &= ~VIDEO_MODE_MASK;
+ reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
+ } else {
+ reg &= ~VIDEO_MODE_MASK;
+ reg |= VIDEO_MODE_SLAVE_MODE;
+ }
+
+ writel(reg, &dp_regs->soc_general_ctl);
+}
+
+void exynos_dp_start_video(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Enable Video input and disable Mute */
+ reg = readl(&dp_regs->video_ctl1);
+ reg |= VIDEO_EN;
+ writel(reg, &dp_regs->video_ctl1);
+}
+
+unsigned int exynos_dp_is_video_stream_on(void)
+{
+ unsigned int reg;
+ struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp();
+
+ /* Update STRM_VALID */
+ reg = readl(&dp_regs->sys_ctl3);
+ writel(reg, &dp_regs->sys_ctl3);
+
+ reg = readl(&dp_regs->sys_ctl3);
+ if (!(reg & STRM_VALID))
+ return -EIO;
+
+ return EXYNOS_DP_SUCCESS;
+}
diff --git a/drivers/video/exynos_dp_lowlevel.h b/drivers/video/exynos_dp_lowlevel.h
new file mode 100644
index 0000000000..a041a7ab50
--- /dev/null
+++ b/drivers/video/exynos_dp_lowlevel.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 _EXYNOS_EDP_LOWLEVEL_H
+#define _EXYNOS_EDP_LOWLEVEL_H
+
+void exynos_dp_enable_video_bist(unsigned int enable);
+void exynos_dp_enable_video_mute(unsigned int enable);
+void exynos_dp_reset(void);
+void exynos_dp_enable_sw_func(unsigned int enable);
+unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable);
+unsigned int exynos_dp_get_pll_lock_status(void);
+int exynos_dp_init_analog_func(void);
+void exynos_dp_init_hpd(void);
+void exynos_dp_init_aux(void);
+void exynos_dp_config_interrupt(void);
+unsigned int exynos_dp_get_plug_in_status(void);
+unsigned int exynos_dp_detect_hpd(void);
+unsigned int exynos_dp_start_aux_transaction(void);
+unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr,
+ unsigned char data);
+unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr,
+ unsigned char *data);
+unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[]);
+unsigned int exynos_dp_read_bytes_from_dpcd( unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[]);
+int exynos_dp_select_i2c_device( unsigned int device_addr,
+ unsigned int reg_addr);
+int exynos_dp_read_byte_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr, unsigned int *data);
+int exynos_dp_read_bytes_from_i2c(unsigned int device_addr,
+ unsigned int reg_addr, unsigned int count,
+ unsigned char edid[]);
+void exynos_dp_reset_macro(void);
+void exynos_dp_set_link_bandwidth(unsigned char bwtype);
+unsigned char exynos_dp_get_link_bandwidth(void);
+void exynos_dp_set_lane_count(unsigned char count);
+unsigned int exynos_dp_get_lane_count(void);
+unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt);
+void exynos_dp_set_lane_pre_emphasis(unsigned int level,
+ unsigned char lanecnt);
+void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val,
+ unsigned char lanecnt);
+void exynos_dp_set_training_pattern(unsigned int pattern);
+void exynos_dp_enable_enhanced_mode(unsigned char enable);
+void exynos_dp_enable_scrambling(unsigned int enable);
+int exynos_dp_init_video(void);
+void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info);
+void exynos_dp_set_video_color_format(struct edp_video_info *video_info);
+int exynos_dp_config_video_bist(struct edp_device_info *edp_info);
+unsigned int exynos_dp_is_slave_video_stream_clock_on(void);
+void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value,
+ unsigned int n_value);
+void exynos_dp_set_video_timing_mode(unsigned int type);
+void exynos_dp_enable_video_master(unsigned int enable);
+void exynos_dp_start_video(void);
+unsigned int exynos_dp_is_video_stream_on(void);
+
+#endif /* _EXYNOS_DP_LOWLEVEL_H */
diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c
index 49fdfec763..e31a0fd500 100644
--- a/drivers/video/exynos_fb.c
+++ b/drivers/video/exynos_fb.c
@@ -28,6 +28,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/clk.h>
#include <asm/arch/mipi_dsim.h>
+#include <asm/arch/dp_info.h>
#include <asm/arch/system.h>
#include "exynos_fb.h"
@@ -91,6 +92,9 @@ static void lcd_panel_on(vidinfo_t *vid)
udelay(vid->power_on_delay);
+ if (vid->dp_enabled)
+ exynos_init_dp();
+
if (vid->reset_lcd) {
vid->reset_lcd();
udelay(vid->reset_delay);
@@ -130,7 +134,6 @@ void lcd_enable(void)
if (panel_info.logo_on) {
memset(lcd_base, 0, panel_width * panel_height *
(NBITS(panel_info.vl_bpix) >> 3));
-
draw_logo();
}
diff --git a/drivers/video/exynos_fimd.c b/drivers/video/exynos_fimd.c
index f07568acca..06eae2ed78 100644
--- a/drivers/video/exynos_fimd.c
+++ b/drivers/video/exynos_fimd.c
@@ -41,8 +41,8 @@ void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size,
static void exynos_fimd_set_dualrgb(unsigned int enabled)
{
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
unsigned int cfg = 0;
if (enabled) {
@@ -57,11 +57,24 @@ static void exynos_fimd_set_dualrgb(unsigned int enabled)
writel(cfg, &fimd_ctrl->dualrgb);
}
+static void exynos_fimd_set_dp_clkcon(unsigned int enabled)
+{
+
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
+ unsigned int cfg = 0;
+
+ if (enabled)
+ cfg = EXYNOS_DP_CLK_ENABLE;
+
+ writel(cfg, &fimd_ctrl->dp_mie_clkcon);
+}
+
static void exynos_fimd_set_par(unsigned int win_id)
{
unsigned int cfg = 0;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
/* set window control */
cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
@@ -93,7 +106,10 @@ static void exynos_fimd_set_par(unsigned int win_id)
EXYNOS_VIDOSD(win_id));
cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) |
- EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1);
+ EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) |
+ EXYNOS_VIDOSD_RIGHT_X_E(1) |
+ EXYNOS_VIDOSD_BOTTOM_Y_E(0);
+
writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b +
EXYNOS_VIDOSD(win_id));
@@ -106,8 +122,8 @@ static void exynos_fimd_set_par(unsigned int win_id)
static void exynos_fimd_set_buffer_address(unsigned int win_id)
{
unsigned long start_addr, end_addr;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
start_addr = (unsigned long)lcd_base_addr;
end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) *
@@ -124,8 +140,8 @@ static void exynos_fimd_set_clock(vidinfo_t *pvid)
unsigned int cfg = 0, div = 0, remainder, remainder_div;
unsigned long pixel_clock;
unsigned long long src_clock;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
if (pvid->dual_lcd_enabled) {
pixel_clock = pvid->vl_freq *
@@ -153,9 +169,6 @@ static void exynos_fimd_set_clock(vidinfo_t *pvid)
cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
- if (pixel_clock > MAX_CLOCK)
- pixel_clock = MAX_CLOCK;
-
src_clock = (unsigned long long) get_lcd_clk();
/* get quotient and remainder. */
@@ -180,8 +193,8 @@ static void exynos_fimd_set_clock(vidinfo_t *pvid)
void exynos_set_trigger(void)
{
unsigned int cfg = 0;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
cfg = readl(&fimd_ctrl->trigcon);
@@ -194,8 +207,8 @@ int exynos_is_i80_frame_done(void)
{
unsigned int cfg = 0;
int status;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
cfg = readl(&fimd_ctrl->trigcon);
@@ -209,8 +222,8 @@ int exynos_is_i80_frame_done(void)
static void exynos_fimd_lcd_on(void)
{
unsigned int cfg = 0;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
/* display on */
cfg = readl(&fimd_ctrl->vidcon0);
@@ -221,8 +234,8 @@ static void exynos_fimd_lcd_on(void)
static void exynos_fimd_window_on(unsigned int win_id)
{
unsigned int cfg = 0;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
/* enable window */
cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
@@ -239,8 +252,8 @@ static void exynos_fimd_window_on(unsigned int win_id)
void exynos_fimd_lcd_off(void)
{
unsigned int cfg = 0;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
cfg = readl(&fimd_ctrl->vidcon0);
cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
@@ -250,8 +263,8 @@ void exynos_fimd_lcd_off(void)
void exynos_fimd_window_off(unsigned int win_id)
{
unsigned int cfg = 0;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
EXYNOS_WINCON(win_id));
@@ -264,11 +277,15 @@ void exynos_fimd_window_off(unsigned int win_id)
writel(cfg, &fimd_ctrl->winshmap);
}
+
void exynos_fimd_lcd_init(vidinfo_t *vid)
{
unsigned int cfg = 0, rgb_mode;
- struct exynos4_fb *fimd_ctrl =
- (struct exynos4_fb *)samsung_get_base_fimd();
+ unsigned int offset;
+ struct exynos_fb *fimd_ctrl =
+ (struct exynos_fb *)samsung_get_base_fimd();
+
+ offset = exynos_fimd_get_base_offset();
/* store panel info to global variable */
pvid = vid;
@@ -297,25 +314,27 @@ void exynos_fimd_lcd_init(vidinfo_t *vid)
if (!pvid->vl_dp)
cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
- writel(cfg, &fimd_ctrl->vidcon1);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset);
/* set timing */
cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1);
cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1);
cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1);
- writel(cfg, &fimd_ctrl->vidtcon0);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset);
cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1);
cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1);
cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1);
- writel(cfg, &fimd_ctrl->vidtcon1);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset);
/* set lcd size */
- cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1);
- cfg |= EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1);
+ cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) |
+ EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) |
+ EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) |
+ EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1);
- writel(cfg, &fimd_ctrl->vidtcon2);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset);
}
/* set display mode */
@@ -331,7 +350,11 @@ void exynos_fimd_lcd_init(vidinfo_t *vid)
exynos_fimd_set_buffer_address(pvid->win_id);
/* set buffer size */
- cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8);
+ cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) |
+ EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) |
+ EXYNOS_VIDADDR_OFFSIZE(0) |
+ EXYNOS_VIDADDR_OFFSIZE_E(0);
+
writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 +
EXYNOS_BUFFER_SIZE(pvid->win_id));
@@ -346,6 +369,8 @@ void exynos_fimd_lcd_init(vidinfo_t *vid)
/* window on */
exynos_fimd_window_on(pvid->win_id);
+
+ exynos_fimd_set_dp_clkcon(pvid->dp_enabled);
}
unsigned long exynos_fimd_calc_fbsize(void)
diff --git a/drivers/video/exynos_pwm_bl.c b/drivers/video/exynos_pwm_bl.c
new file mode 100644
index 0000000000..27fb0b5e7e
--- /dev/null
+++ b/drivers/video/exynos_pwm_bl.c
@@ -0,0 +1,57 @@
+/*
+ * PWM BACKLIGHT driver for Board based on EXYNOS.
+ *
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * Derived from linux/drivers/video/backlight/pwm_backlight.c
+ *
+ * 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 <pwm.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/pwm.h>
+#include <asm/arch/pwm_backlight.h>
+
+static struct pwm_backlight_data *pwm;
+
+static int exynos_pwm_backlight_update_status(void)
+{
+ int brightness = pwm->brightness;
+ int max = pwm->max_brightness;
+
+ if (brightness == 0) {
+ pwm_config(pwm->pwm_id, 0, pwm->period);
+ pwm_disable(pwm->pwm_id);
+ } else {
+ pwm_config(pwm->pwm_id,
+ brightness * pwm->period / max, pwm->period);
+ pwm_enable(pwm->pwm_id);
+ }
+ return 0;
+}
+
+int exynos_pwm_backlight_init(struct pwm_backlight_data *pd)
+{
+ pwm = pd;
+
+ exynos_pwm_backlight_update_status();
+
+ return 0;
+}
diff --git a/drivers/video/omap3_dss.c b/drivers/video/omap3_dss.c
index 6686718b0a..b1424bfd03 100644
--- a/drivers/video/omap3_dss.c
+++ b/drivers/video/omap3_dss.c
@@ -112,7 +112,7 @@ void omap3_dss_panel_config(const struct panel_config *panel_cfg)
writel(panel_cfg->pol_freq, &dispc->pol_freq);
writel(panel_cfg->divisor, &dispc->divisor);
writel(panel_cfg->lcd_size, &dispc->size_lcd);
- writel(panel_cfg->load_mode << FRAME_MODE_SHIFT, &dispc->config);
+ writel(panel_cfg->load_mode << LOADMODE_SHIFT, &dispc->config);
writel(panel_cfg->panel_type << TFTSTN_SHIFT |
panel_cfg->data_lines << DATALINES_SHIFT, &dispc->control);
writel(panel_cfg->panel_color, &dispc->default_color0);
@@ -121,7 +121,6 @@ void omap3_dss_panel_config(const struct panel_config *panel_cfg)
if (!panel_cfg->frame_buffer)
return;
- writel(panel_cfg->load_mode << LOADMODE_SHIFT, &dispc->config);
writel(8 << GFX_FORMAT_SHIFT | GFX_ENABLE, &dispc->gfx_attributes);
writel(1, &dispc->gfx_row_inc);
writel(1, &dispc->gfx_pixel_inc);