]> git.sur5r.net Git - u-boot/commitdiff
Merge branch 'master' of git://git.denx.de/u-boot-arm
authorTom Rini <trini@konsulko.com>
Fri, 10 Apr 2015 13:38:38 +0000 (09:38 -0400)
committerTom Rini <trini@konsulko.com>
Fri, 10 Apr 2015 15:54:07 +0000 (11:54 -0400)
57 files changed:
Makefile
README
arch/arm/Kconfig
arch/arm/Makefile
arch/arm/cpu/arm1136/config.mk [deleted file]
arch/arm/cpu/arm1176/config.mk [deleted file]
arch/arm/cpu/arm720t/config.mk [deleted file]
arch/arm/cpu/arm920t/config.mk [deleted file]
arch/arm/cpu/arm926ejs/config.mk [deleted file]
arch/arm/cpu/arm926ejs/lpc32xx/Makefile
arch/arm/cpu/arm926ejs/lpc32xx/clk.c
arch/arm/cpu/arm926ejs/lpc32xx/cpu.c
arch/arm/cpu/arm926ejs/lpc32xx/devices.c
arch/arm/cpu/arm926ejs/lpc32xx/dram.c [new file with mode: 0644]
arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S [new file with mode: 0644]
arch/arm/cpu/arm946es/config.mk [deleted file]
arch/arm/cpu/armv7/config.mk
arch/arm/cpu/armv8/config.mk
arch/arm/cpu/pxa/config.mk
arch/arm/cpu/sa1100/config.mk [deleted file]
arch/arm/include/asm/arch-lpc32xx/clk.h
arch/arm/include/asm/arch-lpc32xx/config.h
arch/arm/include/asm/arch-lpc32xx/cpu.h
arch/arm/include/asm/arch-lpc32xx/emc.h
arch/arm/include/asm/arch-lpc32xx/gpio.h [new file with mode: 0644]
arch/arm/include/asm/arch-lpc32xx/mux.h [new file with mode: 0644]
arch/arm/include/asm/arch-lpc32xx/sys_proto.h
board/work-microwave/work_92105/Kconfig [new file with mode: 0644]
board/work-microwave/work_92105/MAINTAINERS [new file with mode: 0644]
board/work-microwave/work_92105/Makefile [new file with mode: 0644]
board/work-microwave/work_92105/README [new file with mode: 0644]
board/work-microwave/work_92105/work_92105.c [new file with mode: 0644]
board/work-microwave/work_92105/work_92105_display.c [new file with mode: 0644]
board/work-microwave/work_92105/work_92105_display.h [new file with mode: 0644]
board/work-microwave/work_92105/work_92105_spl.c [new file with mode: 0644]
common/image.c
common/spl/spl.c
configs/work_92105_defconfig [new file with mode: 0644]
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/lpc32xx_gpio.c [new file with mode: 0644]
drivers/hwmon/Makefile
drivers/hwmon/ds620.c [new file with mode: 0644]
drivers/i2c/Makefile
drivers/i2c/lpc32xx_i2c.c [new file with mode: 0644]
drivers/mtd/nand/Makefile
drivers/mtd/nand/lpc32xx_nand_mlc.c [new file with mode: 0644]
drivers/net/Makefile
drivers/net/lpc32xx_eth.c [new file with mode: 0644]
drivers/spi/Makefile
drivers/spi/lpc32xx_ssp.c [new file with mode: 0644]
include/configs/work_92105.h [new file with mode: 0644]
include/dtt.h
include/image.h
include/netdev.h
tools/Makefile
tools/lpc32xximage.c [new file with mode: 0644]

index 53ad450de6af0131b59fe1ae1d70fa41409743de..fa7aa890ccbff7f89c3309e03809c27167d0c285 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -909,6 +909,26 @@ OBJCOPYFLAGS_u-boot-with-spl.bin = -I binary -O binary \
 u-boot-with-spl.bin: spl/u-boot-spl.bin $(SPL_PAYLOAD) FORCE
        $(call if_changed,pad_cat)
 
+MKIMAGEFLAGS_lpc32xx-spl.img = -T lpc32xximage -a $(CONFIG_SPL_TEXT_BASE)
+
+lpc32xx-spl.img: spl/u-boot-spl.bin FORCE
+       $(call if_changed,mkimage)
+
+OBJCOPYFLAGS_lpc32xx-boot-0.bin = -I binary -O binary --pad-to=$(CONFIG_SPL_PAD_TO)
+
+lpc32xx-boot-0.bin: lpc32xx-spl.img
+       $(call if_changed,objcopy)
+
+OBJCOPYFLAGS_lpc32xx-boot-1.bin = -I binary -O binary --pad-to=$(CONFIG_SPL_PAD_TO)
+
+lpc32xx-boot-1.bin: lpc32xx-spl.img
+       $(call if_changed,objcopy)
+
+lpc32xx-full.bin: lpc32xx-boot-0.bin lpc32xx-boot-1.bin u-boot.img
+       $(call if_changed,cat)
+
+CLEAN_FILES += lpc32xx-*
+
 OBJCOPYFLAGS_u-boot-with-tpl.bin = -I binary -O binary \
                                   --pad-to=$(CONFIG_TPL_PAD_TO)
 tpl/u-boot-with-tpl.bin: tpl/u-boot-tpl.bin u-boot.bin FORCE
diff --git a/README b/README
index b7c2a170686536fad534c8fd0010c765bf95dd80..9b748ccc34120b3c10dd8c33d84269bff6853b1b 100644 (file)
--- a/README
+++ b/README
@@ -3607,6 +3607,16 @@ FIT uImage format:
                CONFIG_SPL_STACK
                Adress of the start of the stack SPL will use
 
+               CONFIG_SPL_PANIC_ON_RAW_IMAGE
+               When defined, SPL will panic() if the image it has
+               loaded does not have a signature.
+               Defining this is useful when code which loads images
+               in SPL cannot guarantee that absolutely all read errors
+               will be caught.
+               An example is the LPC32XX MLC NAND driver, which will
+               consider that a completely unreadable NAND block is bad,
+               and thus should be skipped silently.
+
                CONFIG_SPL_RELOC_STACK
                Adress of the start of the stack SPL will use after
                relocation.  If unspecified, this is equal to
index 80b0d34190fc3bafb834ca8dc039ef8466b20255..086ca8514f84e721e9886000a56d216b108c5171 100644 (file)
@@ -132,6 +132,11 @@ config TARGET_DEVKIT3250
        bool "Support devkit3250"
        select CPU_ARM926EJS
 
+config TARGET_WORK_92105
+       bool "Support work_92105"
+       select CPU_ARM926EJS
+       select SUPPORT_SPL
+
 config TARGET_MX25PDK
        bool "Support mx25pdk"
        select CPU_ARM926EJS
@@ -872,6 +877,7 @@ source "board/vpac270/Kconfig"
 source "board/wandboard/Kconfig"
 source "board/warp/Kconfig"
 source "board/woodburn/Kconfig"
+source "board/work-microwave/work_92105/Kconfig"
 source "board/xaeniax/Kconfig"
 source "board/xilinx/zynqmp/Kconfig"
 source "board/zipitz2/Kconfig"
index bac3cb27e251bc3a0804a73dc223110649cfb68e..6463c1947c17caa6b05c8bcae6043d208c6d2f85 100644 (file)
@@ -2,6 +2,38 @@
 # SPDX-License-Identifier:     GPL-2.0+
 #
 
+# This selects which instruction set is used.
+arch-$(CONFIG_CPU_ARM720T)     =-march=armv4
+arch-$(CONFIG_CPU_ARM920T)     =-march=armv4
+arch-$(CONFIG_CPU_ARM926EJS)   =-march=armv5te
+arch-$(CONFIG_CPU_ARM946ES)    =-march=armv4
+arch-$(CONFIG_CPU_SA1100)      =-march=armv4
+arch-$(CONFIG_CPU_PXA)         =
+arch-$(CONFIG_CPU_ARM1136)     =-march=armv5
+arch-$(CONFIG_CPU_ARM1176)     =-march=armv5t
+arch-$(CONFIG_CPU_V7)          =$(call cc-option, -march=armv7-a, -march=armv5)
+arch-$(CONFIG_ARM64)           =-march=armv8-a
+
+# Evaluate arch cc-option calls now
+arch-y := $(arch-y)
+
+# This selects how we optimise for the processor.
+tune-$(CONFIG_CPU_ARM720T)     =-mtune=arm7tdmi
+tune-$(CONFIG_CPU_ARM920T)     =
+tune-$(CONFIG_CPU_ARM926EJS)   =
+tune-$(CONFIG_CPU_ARM946ES)    =
+tune-$(CONFIG_CPU_SA1100)      =-mtune=strongarm1100
+tune-$(CONFIG_CPU_PXA)         =-mcpu=xscale
+tune-$(CONFIG_CPU_ARM1136)     =
+tune-$(CONFIG_CPU_ARM1176)     =
+tune-$(CONFIG_CPU_V7)          =
+tune-$(CONFIG_ARM64)           =
+
+# Evaluate tune cc-option calls now
+tune-y := $(tune-y)
+
+PLATFORM_CPPFLAGS += $(arch-y) $(tune-y)
+
 # Machine directory name.  This list is sorted alphanumerically
 # by CONFIG_* macro name.
 machine-$(CONFIG_ARCH_AT91)            += at91
diff --git a/arch/arm/cpu/arm1136/config.mk b/arch/arm/cpu/arm1136/config.mk
deleted file mode 100644 (file)
index a82c6ce..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# (C) Copyright 2002
-# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-# Make ARMv5 to allow more compilers to work, even though its v6.
-PLATFORM_CPPFLAGS += -march=armv5
diff --git a/arch/arm/cpu/arm1176/config.mk b/arch/arm/cpu/arm1176/config.mk
deleted file mode 100644 (file)
index 5dc2ebb..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# (C) Copyright 2002
-# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-# Make ARMv5 to allow more compilers to work, even though its v6.
-PLATFORM_CPPFLAGS += -march=armv5t
diff --git a/arch/arm/cpu/arm720t/config.mk b/arch/arm/cpu/arm720t/config.mk
deleted file mode 100644 (file)
index 772fb41..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# (C) Copyright 2002
-# Sysgo Real-Time Solutions, GmbH <www.elinos.com>
-# Marius Groeger <mgroeger@sysgo.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-PLATFORM_CPPFLAGS += -march=armv4 -mtune=arm7tdmi
diff --git a/arch/arm/cpu/arm920t/config.mk b/arch/arm/cpu/arm920t/config.mk
deleted file mode 100644 (file)
index 799afff..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# (C) Copyright 2002
-# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-PLATFORM_CPPFLAGS += -march=armv4
diff --git a/arch/arm/cpu/arm926ejs/config.mk b/arch/arm/cpu/arm926ejs/config.mk
deleted file mode 100644 (file)
index bdb3da1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# (C) Copyright 2002
-# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-PLATFORM_CPPFLAGS += -march=armv5te
index 314f004ebaff28970d3fbb152b3d1ace931eb7f3..48373776ff0a593cfd718568a258369f755e39f1 100644 (file)
@@ -6,3 +6,5 @@
 #
 
 obj-y   = cpu.o clk.o devices.o timer.o
+
+obj-$(CONFIG_SPL_BUILD) += dram.o lowlevel_init.o
index b7a44d59dac31d8c46c8c5fc67a9965b868fc8ad..1ef8a366696d1fc7b0d332333704a5c9d64d636f 100644 (file)
@@ -98,6 +98,40 @@ unsigned int get_periph_clk_rate(void)
        return get_hclk_pll_rate() / get_periph_clk_div();
 }
 
+unsigned int get_sdram_clk_rate(void)
+{
+       unsigned int src_clk;
+
+       if (!(readl(&clk->pwr_ctrl) & CLK_PWR_NORMAL_RUN))
+               return get_sys_clk_rate();
+
+       src_clk = get_hclk_pll_rate();
+
+       if (readl(&clk->sdramclk_ctrl) & CLK_SDRAM_DDR_SEL) {
+               /* using DDR */
+               switch (readl(&clk->hclkdiv_ctrl) & CLK_HCLK_DDRAM_MASK) {
+               case CLK_HCLK_DDRAM_HALF:
+                       return src_clk/2;
+               case CLK_HCLK_DDRAM_NOMINAL:
+                       return src_clk;
+               default:
+                       return 0;
+               }
+       } else {
+               /* using SDR */
+               switch (readl(&clk->hclkdiv_ctrl) & CLK_HCLK_ARM_PLL_DIV_MASK) {
+               case CLK_HCLK_ARM_PLL_DIV_4:
+                       return src_clk/4;
+               case CLK_HCLK_ARM_PLL_DIV_2:
+                       return src_clk/2;
+               case CLK_HCLK_ARM_PLL_DIV_1:
+                       return src_clk;
+               default:
+                       return 0;
+               }
+       }
+}
+
 int get_serial_clock(void)
 {
        return get_periph_clk_rate();
index 35095a9583808c91a2083671d55785eedd663924..f75747407658354f84ec6e0ee4fa9db4f8832aeb 100644 (file)
@@ -5,9 +5,11 @@
  */
 
 #include <common.h>
+#include <netdev.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/clk.h>
 #include <asm/arch/wdt.h>
+#include <asm/arch/sys_proto.h>
 #include <asm/io.h>
 
 static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
@@ -55,3 +57,11 @@ int print_cpuinfo(void)
        return 0;
 }
 #endif
+
+#ifdef CONFIG_LPC32XX_ETH
+int cpu_eth_init(bd_t *bis)
+{
+       lpc32xx_eth_initialize(bis);
+       return 0;
+}
+#endif
index b5676578fdfdf36ea6a1e2c433502dc144c19bd7..5a453e303d958d826b55fd1c9433e8b05495e856 100644 (file)
@@ -8,10 +8,13 @@
 #include <asm/arch/cpu.h>
 #include <asm/arch/clk.h>
 #include <asm/arch/uart.h>
+#include <asm/arch/mux.h>
 #include <asm/io.h>
+#include <dm.h>
 
 static struct clk_pm_regs    *clk  = (struct clk_pm_regs *)CLK_PM_BASE;
 static struct uart_ctrl_regs *ctrl = (struct uart_ctrl_regs *)UART_CTRL_BASE;
+static struct mux_regs *mux = (struct mux_regs *)MUX_BASE;
 
 void lpc32xx_uart_init(unsigned int uart_id)
 {
@@ -37,3 +40,43 @@ void lpc32xx_uart_init(unsigned int uart_id)
        writel(CLK_UART_X_DIV(1) | CLK_UART_Y_DIV(1),
               &clk->u3clk + (uart_id - 3));
 }
+
+void lpc32xx_mac_init(void)
+{
+       /* Enable MAC interface */
+       writel(CLK_MAC_REG | CLK_MAC_SLAVE | CLK_MAC_MASTER
+               | CLK_MAC_MII, &clk->macclk_ctrl);
+}
+
+void lpc32xx_mlc_nand_init(void)
+{
+       /* Enable NAND interface */
+       writel(CLK_NAND_MLC | CLK_NAND_MLC_INT, &clk->flashclk_ctrl);
+}
+
+void lpc32xx_i2c_init(unsigned int devnum)
+{
+       /* Enable I2C interface */
+       uint32_t ctrl = readl(&clk->i2cclk_ctrl);
+       if (devnum == 1)
+               ctrl |= CLK_I2C1_ENABLE;
+       if (devnum == 2)
+               ctrl |= CLK_I2C2_ENABLE;
+       writel(ctrl, &clk->i2cclk_ctrl);
+}
+
+U_BOOT_DEVICE(lpc32xx_gpios) = {
+       .name = "gpio_lpc32xx"
+};
+
+/* Mux for SCK0, MISO0, MOSI0. We do not use SSEL0. */
+
+#define P_MUX_SET_SSP0 0x1600
+
+void lpc32xx_ssp_init(void)
+{
+       /* Enable SSP0 interface */
+       writel(CLK_SSP0_ENABLE_CLOCK, &clk->ssp_ctrl);
+       /* Mux SSP0 pins */
+       writel(P_MUX_SET_SSP0, &mux->p_mux_set);
+}
diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/dram.c b/arch/arm/cpu/arm926ejs/lpc32xx/dram.c
new file mode 100644 (file)
index 0000000..1eea8e2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * LPC32xx dram init
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * This is called by SPL to gain access to the SDR DRAM.
+ *
+ * This code runs from SRAM.
+ *
+ * Actual CONFIG_LPC32XX_SDRAM_* parameters must be provided
+ * by the board configuration file.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <netdev.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/wdt.h>
+#include <asm/arch/emc.h>
+#include <asm/io.h>
+
+static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
+static struct emc_regs *emc = (struct emc_regs *)EMC_BASE;
+
+void ddr_init(struct emc_dram_settings *dram)
+{
+       uint32_t ck;
+
+       /* Enable EMC interface and choose little endian mode */
+       writel(1, &emc->ctrl);
+       writel(0, &emc->config);
+       /* Select maximum EMC Dynamic Memory Refresh Time */
+       writel(0x7FF, &emc->refresh);
+       /* Determine CLK */
+       ck = get_sdram_clk_rate();
+       /* Configure SDRAM */
+       writel(dram->cmddelay, &clk->sdramclk_ctrl);
+       writel(dram->config0, &emc->config0);
+       writel(dram->rascas0, &emc->rascas0);
+       writel(dram->rdconfig, &emc->read_config);
+       /* Set timings */
+       writel((ck / dram->trp) & 0x0000000F, &emc->t_rp);
+       writel((ck / dram->tras) & 0x0000000F, &emc->t_ras);
+       writel((ck / dram->tsrex) & 0x0000007F, &emc->t_srex);
+       writel((ck / dram->twr) & 0x0000000F, &emc->t_wr);
+       writel((ck / dram->trc) & 0x0000001F, &emc->t_rc);
+       writel((ck / dram->trfc) & 0x0000001F, &emc->t_rfc);
+       writel((ck / dram->txsr) & 0x000000FF, &emc->t_xsr);
+       writel(dram->trrd, &emc->t_rrd);
+       writel(dram->tmrd, &emc->t_mrd);
+       writel(dram->tcdlr, &emc->t_cdlr);
+       /* Dynamic refresh */
+       writel((((ck / dram->refresh) >> 4) & 0x7FF), &emc->refresh);
+       udelay(10);
+       /* Force all clocks, enable inverted ck, issue NOP command */
+       writel(0x00000193, &emc->control);
+       udelay(100);
+       /* Keep all clocks enabled, issue a PRECHARGE ALL command */
+       writel(0x00000113, &emc->control);
+       /* Fast dynamic refresh for at least a few SDRAM ck cycles */
+       writel((((128) >> 4) & 0x7FF), &emc->refresh);
+       udelay(10);
+       /* set correct dynamic refresh timing */
+       writel((((ck / dram->refresh) >> 4) & 0x7FF), &emc->refresh);
+       udelay(10);
+       /* set normal mode to CAS=3 */
+       writel(0x00000093, &emc->control);
+       readl(EMC_DYCS0_BASE | dram->mode);
+       /* set extended mode to all zeroes */
+       writel(0x00000093, &emc->control);
+       readl(EMC_DYCS0_BASE | dram->emode);
+       /* stop forcing clocks, keep inverted clock, issue normal mode */
+       writel(0x00000010, &emc->control);
+}
diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S b/arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S
new file mode 100644 (file)
index 0000000..4b8053e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * WORK Microwave work_92105 board low level init
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * Low level init is called from SPL to set up the clocks.
+ * On entry, the LPC3250 is in Direct Run mode with all clocks
+ * running at 13 MHz; on exit, ARM clock is 208 MHz, HCLK is
+ * 104 MHz and PCLK is 13 MHz.
+ *
+ * This code must run from SRAM so that the clock changes do
+ * not prevent it from executing.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+.globl lowlevel_init
+
+lowlevel_init:
+
+       /* Set ARM, HCLK, PCLK dividers for normal mode */
+       ldr     r0, =0x0000003D
+       ldr     r1, =0x40004040
+       str     r0, [r1]
+
+       /* Start HCLK PLL for 208 MHz */
+       ldr     r0, =0x0001401E
+       ldr     r1, =0x40004058
+       str     r0, [r1]
+
+       /* wait for HCLK PLL to lock */
+1:
+       ldr     r0, [r1]
+       ands    r0, r0, #1
+       beq     1b
+
+       /* switch to normal mode */
+       ldr     r1, =0x40004044
+       ldr     r0, [r1]
+       orr     r0, #0x00000004
+       str     r0, [r1]
+
+       /* Return to U-boot via saved link register */
+       mov     pc, lr
diff --git a/arch/arm/cpu/arm946es/config.mk b/arch/arm/cpu/arm946es/config.mk
deleted file mode 100644 (file)
index 438668d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# (C) Copyright 2002
-# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-PLATFORM_CPPFLAGS +=  -march=armv4
index 6c82c3b537adbbd640ccbb74241bae63f9fd254b..63591d4a29040bcfb60b53977ce8a6954326aa02 100644 (file)
@@ -5,11 +5,6 @@
 # SPDX-License-Identifier:     GPL-2.0+
 #
 
-# If armv7-a is not supported by GCC fall-back to armv5, which is
-# supported by more tool-chains
-PF_CPPFLAGS_ARMV7 := $(call cc-option, -march=armv7-a, -march=armv5)
-PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_ARMV7)
-
 # On supported platforms we set the bit which causes us to trap on unaligned
 # memory access.  This is the opposite of what the compiler expects to be
 # the default so we must pass in -mno-unaligned-access so that it is aware
index f5b95591af55f789ce9c000969e9e2d8be042851..685025881c487ee39dc7d0c1e09da9e8aec29cd3 100644 (file)
@@ -6,7 +6,5 @@
 #
 PLATFORM_RELFLAGS += -fno-common -ffixed-x18
 
-PF_CPPFLAGS_ARMV8 := $(call cc-option, -march=armv8-a)
 PF_NO_UNALIGNED := $(call cc-option, -mstrict-align)
-PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_ARMV8)
 PLATFORM_CPPFLAGS += $(PF_NO_UNALIGNED)
index 525f5d33b7025cdc14ea107d2feb87f77b25a449..7fb5316d8f3b14013ecddaa8bf94c8f119c863e9 100644 (file)
@@ -6,8 +6,6 @@
 # SPDX-License-Identifier:     GPL-2.0+
 #
 
-PLATFORM_CPPFLAGS += -mcpu=xscale
-
 #
 # !WARNING!
 # The PXA's OneNAND SPL uses .text.0 and .text.1 segments to allow booting from
diff --git a/arch/arm/cpu/sa1100/config.mk b/arch/arm/cpu/sa1100/config.mk
deleted file mode 100644 (file)
index 3afa685..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# (C) Copyright 2002
-# Sysgo Real-Time Solutions, GmbH <www.elinos.com>
-# Marius Groeger <mgroeger@sysgo.de>
-#
-# SPDX-License-Identifier:     GPL-2.0+
-#
-
-PLATFORM_CPPFLAGS += -march=armv4 -mtune=strongarm1100
index 92f6c15f20fc8dba1ce1d2a3f620c4625fa7fd66..94498695a0291814478db80cb3ba1198eca927c5 100644 (file)
@@ -71,6 +71,7 @@ struct clk_pm_regs {
 };
 
 /* HCLK Divider Control Register bits */
+#define CLK_HCLK_DDRAM_MASK            (0x3 << 7)
 #define CLK_HCLK_DDRAM_HALF            (0x2 << 7)
 #define CLK_HCLK_DDRAM_NOMINAL         (0x1 << 7)
 #define CLK_HCLK_DDRAM_STOPPED         (0x0 << 7)
@@ -123,6 +124,10 @@ struct clk_pm_regs {
 #define CLK_MAC_SLAVE                  (1 << 1)
 #define CLK_MAC_REG                    (1 << 0)
 
+/* I2C Clock Control Register bits     */
+#define CLK_I2C2_ENABLE                        (1 << 1)
+#define CLK_I2C1_ENABLE                        (1 << 0)
+
 /* Timer Clock Control1 Register bits */
 #define CLK_TIMCLK_MOTOR               (1 << 6)
 #define CLK_TIMCLK_TIMER3              (1 << 5)
@@ -147,11 +152,22 @@ struct clk_pm_regs {
 /* DMA Clock Control Register bits */
 #define CLK_DMA_ENABLE                 (1 << 0)
 
+/* NAND Clock Control Register bits */
+#define CLK_NAND_MLC                   (1 << 1)
+#define CLK_NAND_MLC_INT               (1 << 5)
+
+/* SSP Clock Control Register bits */
+#define CLK_SSP0_ENABLE_CLOCK          (1 << 0)
+
+/* SDRAMCLK register bits */
+#define CLK_SDRAM_DDR_SEL              (1 << 1)
+
 unsigned int get_sys_clk_rate(void);
 unsigned int get_hclk_pll_rate(void);
 unsigned int get_hclk_clk_div(void);
 unsigned int get_hclk_clk_rate(void);
 unsigned int get_periph_clk_div(void);
 unsigned int get_periph_clk_rate(void);
+unsigned int get_sdram_clk_rate(void);
 
 #endif /* _LPC32XX_CLK_H */
index 564441cbfa3b603299fd6fdf29b155711488f7e2..d57bc4877ea939d6d0e6129ced89fb3394e20975 100644 (file)
@@ -52,6 +52,9 @@
 #define CONFIG_SYS_BAUDRATE_TABLE      \
                { 9600, 19200, 38400, 57600, 115200, 230400, 460800 }
 
+/* Ethernet */
+#define LPC32XX_ETH_BASE ETHERNET_BASE
+
 /* NOR Flash */
 #if defined(CONFIG_SYS_FLASH_CFI)
 #define CONFIG_FLASH_CFI_DRIVER
index 199b4a026b9ac380c66c58d5d502b6a6f505f2f6..0b5dca11b8db295dff27d530d026ef3241f9d0c1 100644 (file)
@@ -27,6 +27,7 @@
 #define HS_UART7_BASE  0x4001C000      /* High speed UART 7 registers base */
 #define RTC_BASE       0x40024000      /* RTC registers base               */
 #define GPIO_BASE      0x40028000      /* GPIO registers base              */
+#define MUX_BASE       0x40028100      /* MUX registers base               */
 #define WDT_BASE       0x4003C000      /* Watchdog timer registers base    */
 #define TIMER0_BASE    0x40044000      /* Timer0 registers base            */
 #define TIMER1_BASE    0x4004C000      /* Timer1 registers base            */
@@ -37,6 +38,8 @@
 #define UART4_BASE     0x40088000      /* UART 4 registers base            */
 #define UART5_BASE     0x40090000      /* UART 5 registers base            */
 #define UART6_BASE     0x40098000      /* UART 6 registers base            */
+#define I2C1_BASE      0x400A0000      /* I2C  1 registers base            */
+#define I2C2_BASE      0x400A8000      /* I2C  2 registers base            */
 
 /* External SDRAM Memory Bank base addresses */
 #define EMC_DYCS0_BASE 0x80000000      /* SDRAM DYCS0 base address         */
index 82d9bcce50fc2ffb2d10c3a5c27b6dfe3d9a82de..1a2bab251f0052f4391bf617ff909d5f840fe0f6 100644 (file)
@@ -76,4 +76,25 @@ struct emc_regs {
 #define EMC_STAT_WAITWR(n)             (((n) - 2) & 0x1F)
 #define EMC_STAT_WAITTURN(n)           (((n) - 1) & 0x0F)
 
+/* EMC settings for DRAM */
+struct emc_dram_settings {
+       u32     cmddelay;
+       u32     config0;
+       u32     rascas0;
+       u32     rdconfig;
+       u32     trp;
+       u32     tras;
+       u32     tsrex;
+       u32     twr;
+       u32     trc;
+       u32     trfc;
+       u32     txsr;
+       u32     trrd;
+       u32     tmrd;
+       u32     tcdlr;
+       u32     refresh;
+       u32     mode;
+       u32     emode;
+};
+
 #endif /* _LPC32XX_EMC_H */
diff --git a/arch/arm/include/asm/arch-lpc32xx/gpio.h b/arch/arm/include/asm/arch-lpc32xx/gpio.h
new file mode 100644 (file)
index 0000000..3bd94e3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * LPC32xx GPIO interface
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+/**
+ * GPIO Register map for LPC32xx
+ */
+
+struct gpio_regs {
+       u32 p3_inp_state;
+       u32 p3_outp_set;
+       u32 p3_outp_clr;
+       u32 p3_outp_state;
+       /* Watch out! the following are shared between p2 and p3 */
+       u32 p2_p3_dir_set;
+       u32 p2_p3_dir_clr;
+       u32 p2_p3_dir_state;
+       /* Now back to 'one register for one port' */
+       u32 p2_inp_state;
+       u32 p2_outp_set;
+       u32 p2_outp_clr;
+       u32 reserved1[6];
+       u32 p0_inp_state;
+       u32 p0_outp_set;
+       u32 p0_outp_clr;
+       u32 p0_outp_state;
+       u32 p0_dir_set;
+       u32 p0_dir_clr;
+       u32 p0_dir_state;
+       u32 reserved2;
+       u32 p1_inp_state;
+       u32 p1_outp_set;
+       u32 p1_outp_clr;
+       u32 p1_outp_state;
+       u32 p1_dir_set;
+       u32 p1_dir_clr;
+       u32 p1_dir_state;
+};
diff --git a/arch/arm/include/asm/arch-lpc32xx/mux.h b/arch/arm/include/asm/arch-lpc32xx/mux.h
new file mode 100644 (file)
index 0000000..dc1b5bc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * LPC32xx MUX interface
+ *
+ * (C) Copyright 2015  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+/**
+ * MUX register map for LPC32xx
+ */
+
+struct mux_regs {
+       u32 p_mux_set;
+       u32 p_mux_clr;
+       u32 p_mux_state;
+};
index 28812be3cb01c4198d6e0e8178922ae70ea8e8fc..c3d890dde437f1502e470bcc947da2147948f090 100644 (file)
@@ -7,6 +7,14 @@
 #ifndef _LPC32XX_SYS_PROTO_H
 #define _LPC32XX_SYS_PROTO_H
 
-void lpc32xx_uart_init(unsigned int uart_id);
+#include <asm/arch/emc.h>
 
+void lpc32xx_uart_init(unsigned int uart_id);
+void lpc32xx_mac_init(void);
+void lpc32xx_mlc_nand_init(void);
+void lpc32xx_i2c_init(unsigned int devnum);
+void lpc32xx_ssp_init(void);
+#if defined(CONFIG_SPL_BUILD)
+void ddr_init(const struct emc_dram_settings *dram);
+#endif
 #endif /* _LPC32XX_SYS_PROTO_H */
diff --git a/board/work-microwave/work_92105/Kconfig b/board/work-microwave/work_92105/Kconfig
new file mode 100644 (file)
index 0000000..74f004f
--- /dev/null
@@ -0,0 +1,15 @@
+if TARGET_WORK_92105
+
+config SYS_BOARD
+       default "work_92105"
+
+config SYS_VENDOR
+       default "work-microwave"
+
+config SYS_SOC
+       default "lpc32xx"
+
+config SYS_CONFIG_NAME
+       default "work_92105"
+
+endif
diff --git a/board/work-microwave/work_92105/MAINTAINERS b/board/work-microwave/work_92105/MAINTAINERS
new file mode 100644 (file)
index 0000000..29a92c5
--- /dev/null
@@ -0,0 +1,6 @@
+WORK_92105 BOARD
+M:     Albert ARIBAUD <albert.aribaud@3adev.fr>
+S:     Maintained
+F:     board/work-microwave/work_92105/
+F:     include/configs/work_92105.h
+F:     configs/work_92105_defconfig
diff --git a/board/work-microwave/work_92105/Makefile b/board/work-microwave/work_92105/Makefile
new file mode 100644 (file)
index 0000000..ba31c8e
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# (C) Copyright 2014  DENX Software Engineering GmbH
+# Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y  := work_92105.o work_92105_display.o
+
+obj-$(CONFIG_SPL_BUILD) += work_92105_spl.o
diff --git a/board/work-microwave/work_92105/README b/board/work-microwave/work_92105/README
new file mode 100644 (file)
index 0000000..3c256e0
--- /dev/null
@@ -0,0 +1,91 @@
+Work_92105 from Work Microwave is an LPC3250- based board with the
+following features:
+
+    - 64MB SDR DRAM
+    - 1 GB SLC NAND, managed through MLC controller.
+    - Ethernet
+    - Ethernet + PHY SMSC8710
+    - I2C:
+      - EEPROM (24M01-compatible)
+      - RTC (DS1374-compatible)
+      - Temperature sensor (DS620)
+      - DACs (2 x MAX518)
+    - SPI (through SSP interface)
+      - Port expander MAX6957
+    - LCD display (HD44780-compatible), controlled
+      through the port expander and DACs
+
+Standard SPL and U-Boot binaries
+--------------------------------
+
+The default 'make' (or the 'make all') command will produce the
+following files:
+
+1. spl/u-boot-spl.bin  SPL, intended to run from SRAM at address 0.
+                       This file can be loaded in SRAM through a JTAG
+                       debugger or through the LPC32XX Service Boot
+                       mechanism.
+
+2. u-boot.bin          The raw U-Boot image, which can be loaded in
+                       DDR through a JTAG debugger (for instance by
+                       breaking SPL after DDR init), or by a running
+                       U-Boot through e.g. 'loady' or 'tftp' and then
+                       executed with 'go'.
+
+3. u-boot.img          A U-Boot image with a mkimage header prepended.
+                       SPL assumes (even when loaded through JTAG or
+                       Service Boot) that such an image will be found
+                       at offset 0x00040000 in NAND.
+
+NAND cold-boot binaries
+-----------------------
+
+The board can boot entirely from power-on with only SPL and U-Boot in
+NAND. The LPC32XX-specific 'make lpc32xx-full.bin' command will produce
+(in addition to spl/u-boot-spl.bin and u-boot.img if they were not made
+already) the following files:
+
+4. lpc32xx-spl.img     spl/u-boot-spl.bin, with a LPC32XX boot header
+                       prepended. This header is required for the ROM
+                       code to load SPL into SRAM and branch into it.
+                       The content of this file is expected to reside
+                       in NAND at addresses 0x00000000 and 0x00020000
+                       (two copies).
+
+5. lpc32xx-boot-0.bin  lpc32xx-spl.img, padded with 0xFF bytes to a
+                       size of 0x20000 bytes. This file covers exactly
+                       the reserved area for the first bootloader copy
+                       in NAND.
+
+6. lpc32xx-boot-1.bin  Same as lpc32xx-boot-0.bin. This is intended to
+                       be used as the second bootloader copy.
+
+7. lpc32xx-full.bin    lpc32xx-boot-0.bin, lpc32xx-boot-1.bin and
+                       u-boot.img concatenated. This file represents
+                       the content of whole bootloader as present in
+                       NAND at offset 00x00000000.
+
+Flashing instructions
+---------------------
+
+The following assumes a working U-Boot on the target, with the ability
+to load files into DDR.
+
+To update the whole bootloader:
+
+       nand erase 0x00000000 0x80000
+       (load lpc32xx-full.bin at location $loadaddr)
+       nand write $loadaddr 0x00000000 $filesize
+
+To update SPL only (note the double nand write) :
+
+       nand erase 0x00000000 0x40000
+       (load lpc32xx-spl.img or lpc32xx-boot-N.bin at location $loadaddr)
+       nand write $loadaddr 0x00000000 $filesize
+       nand write $loadaddr 0x00020000 $filesize
+
+To update U-Boot only:
+
+       nand erase 0x00040000 0x40000
+       (load u-boot.img at location $loadaddr)
+       nand write $loadaddr 0x00040000 $filesize
diff --git a/board/work-microwave/work_92105/work_92105.c b/board/work-microwave/work_92105/work_92105.c
new file mode 100644 (file)
index 0000000..f782284
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * WORK Microwave work_92105 board support
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/emc.h>
+#include <asm/arch/wdt.h>
+#include <asm/gpio.h>
+#include <spl.h>
+#include "work_92105_display.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
+static struct wdt_regs  *wdt = (struct wdt_regs *)WDT_BASE;
+
+void reset_periph(void)
+{
+       setbits_le32(&clk->timclk_ctrl, CLK_TIMCLK_WATCHDOG);
+       writel(WDTIM_MCTRL_RESFRC1, &wdt->mctrl);
+       udelay(150);
+       writel(0, &wdt->mctrl);
+       clrbits_le32(&clk->timclk_ctrl, CLK_TIMCLK_WATCHDOG);
+}
+
+int board_early_init_f(void)
+{
+       /* initialize serial port for console */
+       lpc32xx_uart_init(CONFIG_SYS_LPC32XX_UART);
+       /* enable I2C, SSP, MAC, NAND */
+       lpc32xx_i2c_init(1); /* only I2C1 has devices, I2C2 has none */
+       lpc32xx_ssp_init();
+       lpc32xx_mac_init();
+       lpc32xx_mlc_nand_init();
+       /* Display must wait until after relocation and devices init */
+       return 0;
+}
+
+#define GPO_19 115
+
+int board_early_init_r(void)
+{
+       /* Set NAND !WP to 1 through GPO_19 */
+       gpio_request(GPO_19, "NAND_nWP");
+       gpio_direction_output(GPO_19, 1);
+
+       /* initialize display */
+       work_92105_display_init();
+
+       return 0;
+}
+
+int board_init(void)
+{
+       reset_periph();
+       /* adress of boot parameters */
+       gd->bd->bi_boot_params  = CONFIG_SYS_SDRAM_BASE + 0x100;
+
+       return 0;
+}
+
+int dram_init(void)
+{
+       gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE,
+                                   CONFIG_SYS_SDRAM_SIZE);
+
+       return 0;
+}
diff --git a/board/work-microwave/work_92105/work_92105_display.c b/board/work-microwave/work_92105/work_92105_display.c
new file mode 100644 (file)
index 0000000..c8b1013
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * work_92105 display support
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * The work_92105 display is a HD44780-compatible module
+ * controlled through a MAX6957AAX SPI port expander, two
+ * MAX518 I2C DACs and native LPC32xx GPO 15.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/emc.h>
+#include <asm/gpio.h>
+#include <spi.h>
+#include <i2c.h>
+#include <version.h>
+#include <vsprintf.h>
+
+/*
+ * GPO 15 in port 3 is gpio 3*32+15 = 111
+ */
+
+#define GPO_15 111
+
+/**
+ * MAX6957AAX registers that we will be using
+ */
+
+#define MAX6957_CONF           0x04
+
+#define MAX6957_CONF_08_11     0x0A
+#define MAX6957_CONF_12_15     0x0B
+#define MAX6957_CONF_16_19     0x0C
+
+/**
+ * Individual gpio ports (one per gpio) to HD44780
+ */
+
+#define MAX6957AAX_HD44780_RS  0x29
+#define MAX6957AAX_HD44780_R_W 0x2A
+#define MAX6957AAX_HD44780_EN  0x2B
+#define MAX6957AAX_HD44780_DATA        0x4C
+
+/**
+ * Display controller instructions
+ */
+
+/* Function set: eight bits, two lines, 8-dot font */
+#define HD44780_FUNCTION_SET           0x38
+
+/* Display ON / OFF: turn display on */
+#define HD44780_DISPLAY_ON_OFF_CONTROL 0x0C
+
+/* Entry mode: increment */
+#define HD44780_ENTRY_MODE_SET         0x06
+
+/* Clear */
+#define HD44780_CLEAR_DISPLAY          0x01
+
+/* Set DDRAM addr (to be ORed with exact address) */
+#define HD44780_SET_DDRAM_ADDR         0x80
+
+/* Set CGRAM addr (to be ORed with exact address) */
+#define HD44780_SET_CGRAM_ADDR         0x40
+
+/**
+ * Default value for contrats
+ */
+
+#define CONTRAST_DEFAULT  25
+
+/**
+ * Define slave as a module-wide local to save passing it around,
+ * plus we will need it after init for the "hd44780" command.
+ */
+
+static struct spi_slave *slave;
+
+/*
+ * Write a value into a MAX6957AAX register.
+ */
+
+static void max6957aax_write(uint8_t reg, uint8_t value)
+{
+       uint8_t dout[2];
+
+       dout[0] = reg;
+       dout[1] = value;
+       gpio_set_value(GPO_15, 0);
+       /* do SPI read/write (passing din==dout is OK) */
+       spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
+       gpio_set_value(GPO_15, 1);
+}
+
+/*
+ * Read a value from a MAX6957AAX register.
+ *
+ * According to the MAX6957AAX datasheet, we should release the chip
+ * select halfway through the read sequence, when the actual register
+ * value is read; but the WORK_92105 hardware prevents the MAX6957AAX
+ * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
+ * so let's release the CS an hold it again while reading the result.
+ */
+
+static uint8_t max6957aax_read(uint8_t reg)
+{
+       uint8_t dout[2], din[2];
+
+       /* send read command */
+       dout[0] = reg | 0x80; /* set bit 7 to indicate read */
+       dout[1] = 0;
+       gpio_set_value(GPO_15, 0);
+       /* do SPI read/write (passing din==dout is OK) */
+       spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
+       /* latch read command */
+       gpio_set_value(GPO_15, 1);
+       /* read register -- din = noop on xmit, din[1] = reg on recv */
+       din[0] = 0;
+       din[1] = 0;
+       gpio_set_value(GPO_15, 0);
+       /* do SPI read/write (passing din==dout is OK) */
+       spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
+       /* end of read. */
+       gpio_set_value(GPO_15, 1);
+       return din[1];
+}
+
+static void hd44780_instruction(unsigned long instruction)
+{
+       max6957aax_write(MAX6957AAX_HD44780_RS, 0);
+       max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 1);
+       max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+       /* HD44780 takes 37 us for most instructions, 1520 for clear */
+       if (instruction == HD44780_CLEAR_DISPLAY)
+               udelay(2000);
+       else
+               udelay(100);
+}
+
+static void hd44780_write_char(char c)
+{
+       max6957aax_write(MAX6957AAX_HD44780_RS, 1);
+       max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 1);
+       max6957aax_write(MAX6957AAX_HD44780_DATA, c);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+       /* HD44780 takes 37 us to write to DDRAM or CGRAM */
+       udelay(100);
+}
+
+static void hd44780_write_str(char *s)
+{
+       max6957aax_write(MAX6957AAX_HD44780_RS, 1);
+       max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
+       while (*s) {
+               max6957aax_write(MAX6957AAX_HD44780_EN, 1);
+               max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
+               max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+               s++;
+               /* HD44780 takes 37 us to write to DDRAM or CGRAM */
+               udelay(100);
+       }
+}
+
+/*
+ * Existing user code might expect these custom characters to be
+ * recognized and displayed on the LCD
+ */
+
+static u8 char_gen_chars[] = {
+       /* #8, empty rectangle */
+       0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
+       /* #9, filled right arrow */
+       0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
+       /* #10, filled left arrow */
+       0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
+       /* #11, up and down arrow */
+       0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
+       /* #12, plus/minus */
+       0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
+       /* #13, fat exclamation mark */
+       0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
+       /* #14, empty square */
+       0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
+       /* #15, struck out square */
+       0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
+};
+
+static void hd44780_init_char_gen(void)
+{
+       int i;
+
+       hd44780_instruction(HD44780_SET_CGRAM_ADDR);
+
+       for (i = 0; i < sizeof(char_gen_chars); i++)
+               hd44780_write_char(char_gen_chars[i]);
+
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR);
+}
+
+void work_92105_display_init(void)
+{
+       int claim_err;
+       char *display_contrast_str;
+       uint8_t display_contrast = CONTRAST_DEFAULT;
+       uint8_t enable_backlight = 0x96;
+
+       slave = spi_setup_slave(0, 0, 500000, 0);
+
+       if (!slave) {
+               printf("Failed to set up SPI slave\n");
+               return;
+       }
+
+       claim_err = spi_claim_bus(slave);
+
+       if (claim_err)
+               debug("Failed to claim SPI bus: %d\n", claim_err);
+
+       /* enable backlight */
+       i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);
+
+       /* set display contrast */
+       display_contrast_str = getenv("fwopt_dispcontrast");
+       if (display_contrast_str)
+               display_contrast = simple_strtoul(display_contrast_str,
+                       NULL, 10);
+       i2c_write(0x2c, 0x00, 1, &display_contrast, 1);
+
+       /* request GPO_15 as an output initially set to 1 */
+       gpio_request(GPO_15, "MAX6957_nCS");
+       gpio_direction_output(GPO_15, 1);
+
+       /* enable MAX6957 portexpander */
+       max6957aax_write(MAX6957_CONF, 0x01);
+       /* configure pin 8 as input, pins 9..19 as outputs */
+       max6957aax_write(MAX6957_CONF_08_11, 0x56);
+       max6957aax_write(MAX6957_CONF_12_15, 0x55);
+       max6957aax_write(MAX6957_CONF_16_19, 0x55);
+
+       /* initialize HD44780 */
+       max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+       hd44780_instruction(HD44780_FUNCTION_SET);
+       hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL);
+       hd44780_instruction(HD44780_ENTRY_MODE_SET);
+
+       /* write custom character glyphs */
+       hd44780_init_char_gen();
+
+       /* Show U-Boot version, date and time as a sign-of-life */
+       hd44780_instruction(HD44780_CLEAR_DISPLAY);
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0);
+       hd44780_write_str(U_BOOT_VERSION);
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64);
+       hd44780_write_str(U_BOOT_DATE);
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20);
+       hd44780_write_str(U_BOOT_TIME);
+}
+
+#ifdef CONFIG_CMD_MAX6957
+
+static int do_max6957aax(cmd_tbl_t *cmdtp, int flag, int argc,
+                        char *const argv[])
+{
+       int reg, val;
+
+       if (argc != 3)
+               return CMD_RET_USAGE;
+       switch (argv[1][0]) {
+       case 'r':
+       case 'R':
+               reg = simple_strtoul(argv[2], NULL, 0);
+               val = max6957aax_read(reg);
+               printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val);
+               return 0;
+       default:
+               reg = simple_strtoul(argv[1], NULL, 0);
+               val = simple_strtoul(argv[2], NULL, 0);
+               max6957aax_write(reg, val);
+               printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val);
+               return 0;
+       }
+       return 1;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char max6957aax_help_text[] =
+       "max6957aax - write or read display register:\n"
+               "\tmax6957aax R|r reg - read display register;\n"
+               "\tmax6957aax reg val - write display register.";
+#endif
+
+U_BOOT_CMD(
+       max6957aax, 6, 1, do_max6957aax,
+       "SPI MAX6957 display write/read",
+       max6957aax_help_text
+);
+#endif /* CONFIG_CMD_MAX6957 */
+
+#ifdef CONFIG_CMD_HD44760
+
+/*
+ * We need the HUSH parser because we need string arguments, and
+ * only HUSH can understand them.
+ */
+
+#if !defined(CONFIG_SYS_HUSH_PARSER)
+#error CONFIG_CMD_HD44760 requires CONFIG_SYS_HUSH_PARSER
+#endif
+
+static int do_hd44780(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       char *cmd;
+
+       if (argc != 3)
+               return CMD_RET_USAGE;
+
+       cmd = argv[1];
+
+       if (strcasecmp(cmd, "cmd") == 0)
+               hd44780_instruction(simple_strtol(argv[2], NULL, 0));
+       else if (strcasecmp(cmd, "data") == 0)
+               hd44780_write_char(simple_strtol(argv[2], NULL, 0));
+       else if (strcasecmp(cmd, "str") == 0)
+               hd44780_write_str(argv[2]);
+       return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char hd44780_help_text[] =
+       "hd44780 - control LCD driver:\n"
+               "\thd44780 cmd <val> - send command <val> to driver;\n"
+               "\thd44780 data <val> - send data <val> to driver;\n"
+               "\thd44780 str \"<text>\" - send \"<text>\" to driver.";
+#endif
+
+U_BOOT_CMD(
+       hd44780, 6, 1, do_hd44780,
+       "HD44780 LCD driver control",
+       hd44780_help_text
+);
+#endif /* CONFIG_CMD_HD44780 */
diff --git a/board/work-microwave/work_92105/work_92105_display.h b/board/work-microwave/work_92105/work_92105_display.h
new file mode 100644 (file)
index 0000000..dd6e768
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * work_92105 display support interface
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * The work_92105 display is a HD44780-compatible module
+ * controlled through a MAX6957AAX SPI port expander, two
+ * MAX518 I2C DACs and native LPC32xx GPO 15.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+void work_92105_display_init(void);
diff --git a/board/work-microwave/work_92105/work_92105_spl.c b/board/work-microwave/work_92105/work_92105_spl.c
new file mode 100644 (file)
index 0000000..282a6dd
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * WORK Microwave work_92105 board support
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/emc.h>
+#include <asm/gpio.h>
+#include <spl.h>
+#include "work_92105_display.h"
+
+struct emc_dram_settings dram_64mb = {
+       .cmddelay = 0x0001C000,
+       .config0 = 0x00005682,
+       .rascas0 = 0x00000302,
+       .rdconfig = 0x00000011,
+       .trp = 52631578,
+       .tras = 20833333,
+       .tsrex = 12500000,
+       .twr = 66666666,
+       .trc = 13888888,
+       .trfc = 10256410,
+       .txsr = 12500000,
+       .trrd = 1,
+       .tmrd = 1,
+       .tcdlr = 0,
+       .refresh = 128000,
+       .mode = 0x00018000,
+       .emode = 0x02000000
+};
+
+const struct emc_dram_settings dram_128mb = {
+       .cmddelay = 0x0001C000,
+       .config0 = 0x00005882,
+       .rascas0 = 0x00000302,
+       .rdconfig = 0x00000011,
+       .trp = 52631578,
+       .tras = 22222222,
+       .tsrex = 8333333,
+       .twr = 66666666,
+       .trc = 14814814,
+       .trfc = 10256410,
+       .txsr = 8333333,
+       .trrd = 1,
+       .tmrd = 1,
+       .tcdlr = 0,
+       .refresh = 128000,
+       .mode = 0x00030000,
+       .emode = 0x02000000
+};
+
+void spl_board_init(void)
+{
+       /* initialize serial port for console */
+       lpc32xx_uart_init(CONFIG_SYS_LPC32XX_UART);
+       /* initialize console */
+       preloader_console_init();
+       /* init DDR and NAND to chainload U-Boot */
+       ddr_init(&dram_128mb);
+       /*
+        * If this is actually a 64MB module, then the highest column
+        * bit in any address will be ignored, and thus address 0x80000000
+        * should be mirrored at address 0x80000800. Test this.
+        */
+       writel(0x31415926, 0x80000000); /* write Pi at 0x80000000 */
+       writel(0x16180339, 0x80000800); /* write Phi at 0x80000800 */
+       if (readl(0x80000000) == 0x16180339) /* check 0x80000000 */ {
+               /* actually 64MB mirrored: reconfigure controller */
+               ddr_init(&dram_64mb);
+       }
+       /* initialize NAND controller to load U-Boot from NAND */
+       lpc32xx_mlc_nand_init();
+}
+
+u32 spl_boot_device(void)
+{
+       return BOOT_DEVICE_NAND;
+}
index a911aa9b4d128227520fba77555c4879e5e93ff3..162b68269d575196c54608949818aa92a45525b8 100644 (file)
@@ -149,6 +149,7 @@ static const table_entry_t uimage_type[] = {
        {       IH_TYPE_MXSIMAGE,   "mxsimage",   "Freescale MXS Boot Image",},
        {       IH_TYPE_ATMELIMAGE, "atmelimage", "ATMEL ROM-Boot Image",},
        {       IH_TYPE_X86_SETUP,  "x86_setup",  "x86 setup.bin",    },
+       {       IH_TYPE_LPC32XXIMAGE, "lpc32xximage",  "LPC32XX Boot Image", },
        {       -1,                 "",           "",                   },
 };
 
index cd75bbc4ced967b96d09aad4f5311a65ee207ec7..8e1fb40c47f12990c58bb36ab13e7e1596a80fb9 100644 (file)
@@ -101,10 +101,22 @@ void spl_parse_image_header(const struct image_header *header)
                        (int)sizeof(spl_image.name), spl_image.name,
                        spl_image.load_addr, spl_image.size);
        } else {
+#ifdef CONFIG_SPL_PANIC_ON_RAW_IMAGE
+               /*
+                * CONFIG_SPL_PANIC_ON_RAW_IMAGE is defined when the
+                * code which loads images in SPL cannot guarantee that
+                * absolutely all read errors will be reported.
+                * An example is the LPC32XX MLC NAND driver, which
+                * will consider that a completely unreadable NAND block
+                * is bad, and thus should be skipped silently.
+                */
+               panic("** no mkimage signature but raw image not supported");
+#else
                /* Signature not found - assume u-boot.bin */
                debug("mkimage signature not found - ih_magic = %x\n",
                        header->ih_magic);
                spl_set_header_raw_uboot();
+#endif
        }
 }
 
diff --git a/configs/work_92105_defconfig b/configs/work_92105_defconfig
new file mode 100644 (file)
index 0000000..142a505
--- /dev/null
@@ -0,0 +1,6 @@
+CONFIG_ARM=y
+CONFIG_TARGET_WORK_92105=y
+CONFIG_DM=y
+CONFIG_DM_GPIO=y
+CONFIG_SPL=y
+CONFIG_SYS_EXTRA_OPTIONS=""
index b609e73bbaf5fe4993b6a560ef9101c4363ec5e2..7b5178a23a4f623b8fddc3ea2520bf9c4fca2d5f 100644 (file)
@@ -7,3 +7,10 @@ config DM_GPIO
          the GPIO uclass. Drivers provide methods to query the
          particular GPIOs that they provide. The uclass interface
          is defined in include/asm-generic/gpio.h.
+
+config LPC32XX_GPIO
+       bool "LPC32XX GPIO driver"
+       depends on DM
+       default n
+       help
+         Support for the LPC32XX GPIO driver.
index fe9a3b23964556e18c92a985a06f1e7fcfad433d..85f71c5d4a773172ece61eecc3f9af6640231925 100644 (file)
@@ -41,3 +41,4 @@ obj-$(CONFIG_ADI_GPIO2)       += adi_gpio2.o
 obj-$(CONFIG_TCA642X)          += tca642x.o
 oby-$(CONFIG_SX151X)           += sx151x.o
 obj-$(CONFIG_SUNXI_GPIO)       += sunxi_gpio.o
+obj-$(CONFIG_LPC32XX_GPIO)     += lpc32xx_gpio.o
diff --git a/drivers/gpio/lpc32xx_gpio.c b/drivers/gpio/lpc32xx_gpio.c
new file mode 100644 (file)
index 0000000..96b3125
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * LPC32xxGPIO driver
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <asm/io.h>
+#include <asm/arch-lpc32xx/cpu.h>
+#include <asm/arch-lpc32xx/gpio.h>
+#include <asm-generic/gpio.h>
+#include <dm.h>
+
+/**
+ * LPC32xx GPIOs work in banks but are non-homogeneous:
+ * - each bank holds a different number of GPIOs
+ * - some GPIOs are input/ouput, some input only, some output only;
+ * - some GPIOs have different meanings as an input and as an output;
+ * - some GPIOs are controlled on a given port and bit index, but
+ *   read on another one.
+*
+ * In order to keep this code simple, GPIOS are considered here as
+ * homogeneous and linear, from 0 to 127.
+ *
+ *     ** WARNING #1 **
+ *
+ * Client code is responsible for properly using valid GPIO numbers,
+ * including cases where a single physical GPIO has differing numbers
+ * for setting its direction, reading it and/or writing to it.
+ *
+ *     ** WARNING #2 **
+ *
+ * Please read NOTE in description of lpc32xx_gpio_get_function().
+ */
+
+#define LPC32XX_GPIOS 128
+
+struct lpc32xx_gpio_platdata {
+       struct gpio_regs *regs;
+       /* GPIO FUNCTION: SEE WARNING #2 */
+       signed char function[LPC32XX_GPIOS];
+};
+
+/**
+ * We have 4 GPIO ports of 32 bits each
+ */
+
+#define MAX_GPIO 128
+
+#define GPIO_TO_PORT(gpio) ((gpio / 32) & 3)
+#define GPIO_TO_RANK(gpio) (gpio % 32)
+#define GPIO_TO_MASK(gpio) (1 << (gpio % 32))
+
+/**
+ * Configure a GPIO number 'offset' as input
+ */
+
+static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+       int port, mask;
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       struct gpio_regs *regs = gpio_platdata->regs;
+
+       port = GPIO_TO_PORT(offset);
+       mask = GPIO_TO_MASK(offset);
+
+       switch (port) {
+       case 0:
+               writel(mask, &regs->p0_dir_clr);
+               break;
+       case 1:
+               writel(mask, &regs->p1_dir_clr);
+               break;
+       case 2:
+               /* ports 2 and 3 share a common direction */
+       case 3:
+               writel(mask, &regs->p2_p3_dir_clr);
+               break;
+       default:
+               return -1;
+       }
+
+       /* GPIO FUNCTION: SEE WARNING #2 */
+       gpio_platdata->function[offset] = GPIOF_INPUT;
+
+       return 0;
+}
+
+/**
+ * Get the value of a GPIO
+ */
+
+static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+       int port, rank, mask, value;
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       struct gpio_regs *regs = gpio_platdata->regs;
+
+       port = GPIO_TO_PORT(offset);
+
+       switch (port) {
+       case 0:
+               value = readl(&regs->p0_inp_state);
+               break;
+       case 1:
+               value = readl(&regs->p1_inp_state);
+               break;
+       case 2:
+               value = readl(&regs->p2_inp_state);
+               break;
+       case 3:
+               value = readl(&regs->p3_inp_state);
+               break;
+       default:
+               return -1;
+       }
+
+       rank = GPIO_TO_RANK(offset);
+       mask = GPIO_TO_MASK(offset);
+
+       return (value & mask) >> rank;
+}
+
+/**
+ * Set a GPIO
+ */
+
+static int gpio_set(struct udevice *dev, unsigned gpio)
+{
+       int port, mask;
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       struct gpio_regs *regs = gpio_platdata->regs;
+
+       port = GPIO_TO_PORT(gpio);
+       mask = GPIO_TO_MASK(gpio);
+
+       switch (port) {
+       case 0:
+               writel(mask, &regs->p0_outp_set);
+               break;
+       case 1:
+               writel(mask, &regs->p1_outp_set);
+               break;
+       case 2:
+               writel(mask, &regs->p2_outp_set);
+               break;
+       case 3:
+               writel(mask, &regs->p3_outp_set);
+               break;
+       default:
+               return -1;
+       }
+       return 0;
+}
+
+/**
+ * Clear a GPIO
+ */
+
+static int gpio_clr(struct udevice *dev, unsigned gpio)
+{
+       int port, mask;
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       struct gpio_regs *regs = gpio_platdata->regs;
+
+       port = GPIO_TO_PORT(gpio);
+       mask = GPIO_TO_MASK(gpio);
+
+       switch (port) {
+       case 0:
+               writel(mask, &regs->p0_outp_clr);
+               break;
+       case 1:
+               writel(mask, &regs->p1_outp_clr);
+               break;
+       case 2:
+               writel(mask, &regs->p2_outp_clr);
+               break;
+       case 3:
+               writel(mask, &regs->p3_outp_clr);
+               break;
+       default:
+               return -1;
+       }
+       return 0;
+}
+
+/**
+ * Set the value of a GPIO
+ */
+
+static int lpc32xx_gpio_set_value(struct udevice *dev, unsigned offset,
+                                int value)
+{
+       if (value)
+               return gpio_set(dev, offset);
+       else
+               return gpio_clr(dev, offset);
+}
+
+/**
+ * Configure a GPIO number 'offset' as output with given initial value.
+ */
+
+static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset,
+                                      int value)
+{
+       int port, mask;
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       struct gpio_regs *regs = gpio_platdata->regs;
+
+       port = GPIO_TO_PORT(offset);
+       mask = GPIO_TO_MASK(offset);
+
+       switch (port) {
+       case 0:
+               writel(mask, &regs->p0_dir_set);
+               break;
+       case 1:
+               writel(mask, &regs->p1_dir_set);
+               break;
+       case 2:
+               /* ports 2 and 3 share a common direction */
+       case 3:
+               writel(mask, &regs->p2_p3_dir_set);
+               break;
+       default:
+               return -1;
+       }
+
+       /* GPIO FUNCTION: SEE WARNING #2 */
+       gpio_platdata->function[offset] = GPIOF_OUTPUT;
+
+       return lpc32xx_gpio_set_value(dev, offset, value);
+}
+
+/**
+ * GPIO functions are supposed to be computed from their current
+ * configuration, but that's way too complicated in LPC32XX. A simpler
+ * approach is used, where the GPIO functions are cached in an array.
+ * When the GPIO is in use, its function is either "input" or "output"
+ * depending on its direction, otherwise its function is "unknown".
+ *
+ *     ** NOTE **
+ *
+ * THIS APPROACH WAS CHOSEN DU TO THE COMPLEX NATURE OF THE LPC32XX
+ * GPIOS; DO NOT TAKE THIS AS AN EXAMPLE FOR NEW CODE.
+ */
+
+static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       return gpio_platdata->function[offset];
+}
+
+static const struct dm_gpio_ops gpio_lpc32xx_ops = {
+       .direction_input        = lpc32xx_gpio_direction_input,
+       .direction_output       = lpc32xx_gpio_direction_output,
+       .get_value              = lpc32xx_gpio_get_value,
+       .set_value              = lpc32xx_gpio_set_value,
+       .get_function           = lpc32xx_gpio_get_function,
+};
+
+static int lpc32xx_gpio_probe(struct udevice *dev)
+{
+       struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
+       struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+
+       if (dev->of_offset == -1) {
+               /* Tell the uclass how many GPIOs we have */
+               uc_priv->gpio_count = LPC32XX_GPIOS;
+       }
+
+       /* set base address for GPIO registers */
+       gpio_platdata->regs = (struct gpio_regs *)GPIO_BASE;
+
+       /* all GPIO functions are unknown until requested */
+       /* GPIO FUNCTION: SEE WARNING #2 */
+       memset(gpio_platdata->function, GPIOF_UNKNOWN,
+              sizeof(gpio_platdata->function));
+
+       return 0;
+}
+
+U_BOOT_DRIVER(gpio_lpc32xx) = {
+       .name   = "gpio_lpc32xx",
+       .id     = UCLASS_GPIO,
+       .ops    = &gpio_lpc32xx_ops,
+       .probe  = lpc32xx_gpio_probe,
+       .priv_auto_alloc_size = sizeof(struct lpc32xx_gpio_platdata),
+};
index 25b8e8a2d79f4490e246aa2b687a522523e11818..b4fb057c16a0dfed94c64390cab05e3b0e16e2fa 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_DTT_ADT7460) += adt7460.o
 obj-$(CONFIG_DTT_DS1621) += ds1621.o
 obj-$(CONFIG_DTT_DS1722) += ds1722.o
 obj-$(CONFIG_DTT_DS1775) += ds1775.o
+obj-$(CONFIG_DTT_DS620) += ds620.o
 obj-$(CONFIG_DTT_LM63) += lm63.o
 obj-$(CONFIG_DTT_LM73) += lm73.o
 obj-$(CONFIG_DTT_LM75) += lm75.o
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
new file mode 100644 (file)
index 0000000..1ecc3da
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * DS620 DTT support
+ *
+ * (C) Copyright 2014 3ADEV <http://www.3adev.com>
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+/*
+ * Dallas Semiconductor's DS1621/1631 Digital Thermometer and Thermostat.
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <dtt.h>
+
+/*
+ * Device code
+ */
+#define DTT_I2C_DEV_CODE       0x48
+#define DTT_START_CONVERT      0x51
+#define DTT_TEMP               0xAA
+#define DTT_CONFIG             0xAC
+
+/*
+ * Config register MSB bits
+ */
+#define DTT_CONFIG_1SHOT       0x01
+#define DTT_CONFIG_AUTOC       0x02
+#define DTT_CONFIG_R0          0x04 /* always 1 */
+#define DTT_CONFIG_R1          0x08 /* always 1 */
+#define DTT_CONFIG_TLF 0x10
+#define DTT_CONFIG_THF 0x20
+#define DTT_CONFIG_NVB 0x40
+#define DTT_CONFIG_DONE        0x80
+
+#define CHIP(sensor) (DTT_I2C_DEV_CODE + (sensor & 0x07))
+
+int dtt_init_one(int sensor)
+{
+       uint8_t config = DTT_CONFIG_1SHOT
+                       | DTT_CONFIG_R0
+                       | DTT_CONFIG_R1;
+       return i2c_write(CHIP(sensor), DTT_CONFIG, 1, &config, 1);
+}
+
+int dtt_get_temp(int sensor)
+{
+       uint8_t status;
+       uint8_t temp[2];
+
+       /* Start a conversion, may take up to 1 second. */
+       i2c_write(CHIP(sensor), DTT_START_CONVERT, 1, NULL, 0);
+       do {
+               if (i2c_read(CHIP(sensor), DTT_CONFIG, 1, &status, 1))
+                       /* bail out if I2C error */
+                       status |= DTT_CONFIG_DONE;
+       } while (!(status & DTT_CONFIG_DONE));
+       if (i2c_read(CHIP(sensor), DTT_TEMP, 1, temp, 2))
+               /* bail out if I2C error */
+               return -274; /* below absolute zero == error */
+
+       return ((int16_t)(temp[1] | (temp[0] << 8))) >> 7;
+}
index 774bc94a4a7a864acfbdb6a8a5214f34637f7fe7..26ea854ec8dfac477ee0951cc58cb6dc150a31e2 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
 obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o
 obj-$(CONFIG_SYS_I2C_IHS) += ihs_i2c.o
 obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o
+obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
 obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
 obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
 obj-$(CONFIG_SYS_I2C_MXS) += mxs_i2c.o
diff --git a/drivers/i2c/lpc32xx_i2c.c b/drivers/i2c/lpc32xx_i2c.c
new file mode 100644 (file)
index 0000000..78d26e4
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * LPC32xx I2C interface driver
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <i2c.h>
+#include <asm/errno.h>
+#include <asm/arch/clk.h>
+
+/*
+ * Provide default speed and slave if target did not
+ */
+
+#if !defined(CONFIG_SYS_I2C_LPC32XX_SPEED)
+#define CONFIG_SYS_I2C_LPC32XX_SPEED 350000
+#endif
+
+#if !defined(CONFIG_SYS_I2C_LPC32XX_SLAVE)
+#define CONFIG_SYS_I2C_LPC32XX_SLAVE 0
+#endif
+
+/* i2c register set */
+struct lpc32xx_i2c_registers {
+       union {
+               u32 rx;
+               u32 tx;
+       };
+       u32 stat;
+       u32 ctrl;
+       u32 clk_hi;
+       u32 clk_lo;
+       u32 adr;
+       u32 rxfl;
+       u32 txfl;
+       u32 rxb;
+       u32 txb;
+       u32 stx;
+       u32 stxfl;
+};
+
+/* TX register fields */
+#define LPC32XX_I2C_TX_START           0x00000100
+#define LPC32XX_I2C_TX_STOP            0x00000200
+
+/* Control register values */
+#define LPC32XX_I2C_SOFT_RESET         0x00000100
+
+/* Status register values */
+#define LPC32XX_I2C_STAT_TFF           0x00000400
+#define LPC32XX_I2C_STAT_RFE           0x00000200
+#define LPC32XX_I2C_STAT_DRMI          0x00000008
+#define LPC32XX_I2C_STAT_NAI           0x00000004
+#define LPC32XX_I2C_STAT_TDI           0x00000001
+
+static struct lpc32xx_i2c_registers *lpc32xx_i2c[] = {
+       (struct lpc32xx_i2c_registers *)I2C1_BASE,
+       (struct lpc32xx_i2c_registers *)I2C2_BASE
+};
+
+/* Set I2C bus speed */
+static unsigned int lpc32xx_i2c_set_bus_speed(struct i2c_adapter *adap,
+                       unsigned int speed)
+{
+       int half_period;
+
+       if (speed == 0)
+               return -EINVAL;
+
+       half_period = (105000000 / speed) / 2;
+
+       if ((half_period > 255) || (half_period < 0))
+               return -EINVAL;
+
+       writel(half_period, &lpc32xx_i2c[adap->hwadapnr]->clk_hi);
+       writel(half_period, &lpc32xx_i2c[adap->hwadapnr]->clk_lo);
+       return 0;
+}
+
+/* I2C init called by cmd_i2c when doing 'i2c reset'. */
+static void _i2c_init(struct i2c_adapter *adap,
+       int requested_speed, int slaveadd)
+{
+       struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
+
+       /* soft reset (auto-clears) */
+       writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
+       /* set HI and LO periods for about 350 kHz */
+       lpc32xx_i2c_set_bus_speed(adap, requested_speed);
+}
+
+/* I2C probe called by cmd_i2c when doing 'i2c probe'. */
+static int lpc32xx_i2c_probe(struct i2c_adapter *adap, u8 dev)
+{
+       struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
+       int stat;
+
+       /* Soft-reset the controller */
+       writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
+       while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
+               ;
+       /* Addre slave for write with start before and stop after */
+       writel((dev<<1) | LPC32XX_I2C_TX_START | LPC32XX_I2C_TX_STOP,
+              &i2c->tx);
+       /* wait for end of transation */
+       while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
+               ;
+       /* was there no acknowledge? */
+       return (stat & LPC32XX_I2C_STAT_NAI) ? -1 : 0;
+}
+
+/*
+ * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
+ * Begin write, send address byte(s), begin read, receive data bytes, end.
+ */
+static int lpc32xx_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr,
+                        int alen, u8 *data, int length)
+{
+       struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
+       int stat, wlen;
+
+       /* Soft-reset the controller */
+       writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
+       while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
+               ;
+       /* do we need to write an address at all? */
+       if (alen) {
+               /* Address slave in write mode */
+               writel((dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
+               /* write address bytes */
+               while (alen--) {
+                       /* compute address byte + stop for the last one */
+                       int a = (addr >> (8 * alen)) & 0xff;
+                       if (!alen)
+                               a |= LPC32XX_I2C_TX_STOP;
+                       /* Send address byte */
+                       writel(a, &i2c->tx);
+               }
+               /* wait for end of transation */
+               while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
+                       ;
+               /* clear end-of-transaction flag */
+               writel(1, &i2c->stat);
+       }
+       /* do we have to read data at all? */
+       if (length) {
+               /* Address slave in read mode */
+               writel(1 | (dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
+               wlen = length;
+               /* get data */
+               while (length | wlen) {
+                       /* read status for TFF and RFE */
+                       stat = readl(&i2c->stat);
+                       /* must we, can we write a trigger byte? */
+                       if ((wlen > 0)
+                          & (!(stat & LPC32XX_I2C_STAT_TFF))) {
+                               wlen--;
+                               /* write trigger byte + stop if last */
+                               writel(wlen ? 0 :
+                               LPC32XX_I2C_TX_STOP, &i2c->tx);
+                       }
+                       /* must we, can we read a data byte? */
+                       if ((length > 0)
+                          & (!(stat & LPC32XX_I2C_STAT_RFE))) {
+                               length--;
+                               /* read byte */
+                               *(data++) = readl(&i2c->rx);
+                       }
+               }
+       }
+       /* wait for end of transation */
+       while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
+               ;
+       /* clear end-of-transaction flag */
+       writel(1, &i2c->stat);
+       /* success */
+       return 0;
+}
+
+/*
+ * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
+ * Begin write, send address byte(s), send data bytes, end.
+ */
+static int lpc32xx_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr,
+                         int alen, u8 *data, int length)
+{
+       struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
+       int stat;
+
+       /* Soft-reset the controller */
+       writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
+       while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
+               ;
+       /* do we need to write anything at all? */
+       if (alen | length)
+               /* Address slave in write mode */
+               writel((dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
+       /* write address bytes */
+       while (alen) {
+               /* wait for transmit fifo not full */
+               stat = readl(&i2c->stat);
+               if (!(stat & LPC32XX_I2C_STAT_TFF)) {
+                       alen--;
+                       int a = (addr >> (8 * alen)) & 0xff;
+                       if (!(alen | length))
+                               a |= LPC32XX_I2C_TX_STOP;
+                       /* Send address byte */
+                       writel(a, &i2c->tx);
+               }
+       }
+       while (length) {
+               /* wait for transmit fifo not full */
+               stat = readl(&i2c->stat);
+               if (!(stat & LPC32XX_I2C_STAT_TFF)) {
+                       /* compute data byte, add stop if length==0 */
+                       length--;
+                       int d = *(data++);
+                       if (!length)
+                               d |= LPC32XX_I2C_TX_STOP;
+                       /* Send data byte */
+                       writel(d, &i2c->tx);
+               }
+       }
+       /* wait for end of transation */
+       while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
+               ;
+       /* clear end-of-transaction flag */
+       writel(1, &i2c->stat);
+       return 0;
+}
+
+U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_0, _i2c_init, lpc32xx_i2c_probe,
+                        lpc32xx_i2c_read, lpc32xx_i2c_write,
+                        lpc32xx_i2c_set_bus_speed,
+                        CONFIG_SYS_I2C_LPC32XX_SPEED,
+                        CONFIG_SYS_I2C_LPC32XX_SLAVE,
+                        0)
+
+U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_1, _i2c_init, lpc32xx_i2c_probe,
+                        lpc32xx_i2c_read, lpc32xx_i2c_write,
+                        lpc32xx_i2c_set_bus_speed,
+                        CONFIG_SYS_I2C_LPC32XX_SPEED,
+                        CONFIG_SYS_I2C_LPC32XX_SLAVE,
+                        1)
index 1f02bfc35f6a6b020affadd210ec23d652593164..347ea62e0b3d3fa5a33eb8097d01ce40d642be49 100644 (file)
@@ -52,6 +52,7 @@ obj-$(CONFIG_NAND_JZ4740) += jz4740_nand.o
 obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o
 obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
 obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
+obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o
 obj-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o
 obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o
 obj-$(CONFIG_NAND_MXC) += mxc_nand.o
diff --git a/drivers/mtd/nand/lpc32xx_nand_mlc.c b/drivers/mtd/nand/lpc32xx_nand_mlc.c
new file mode 100644 (file)
index 0000000..8156fe9
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ * LPC32xx MLC NAND flash controller driver
+ *
+ * (C) Copyright 2014 3ADEV <http://3adev.com>
+ * Written by Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ *
+ * NOTE:
+ *
+ * The MLC NAND flash controller provides hardware Reed-Solomon ECC
+ * covering in- and out-of-band data together. Therefore, in- and out-
+ * of-band data must be written together in order to have a valid ECC.
+ *
+ * Consequently, pages with meaningful in-band data are written with
+ * blank (all-ones) out-of-band data and a valid ECC, and any later
+ * out-of-band data write will void the ECC.
+ *
+ * Therefore, code which reads such late-written out-of-band data
+ * should not rely on the ECC validity.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <nand.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/sys_proto.h>
+
+/*
+ * MLC NAND controller registers.
+ */
+struct lpc32xx_nand_mlc_registers {
+       u8 buff[32768]; /* controller's serial data buffer */
+       u8 data[32768]; /* NAND's raw data buffer */
+       u32 cmd;
+       u32 addr;
+       u32 ecc_enc_reg;
+       u32 ecc_dec_reg;
+       u32 ecc_auto_enc_reg;
+       u32 ecc_auto_dec_reg;
+       u32 rpr;
+       u32 wpr;
+       u32 rubp;
+       u32 robp;
+       u32 sw_wp_add_low;
+       u32 sw_wp_add_hig;
+       u32 icr;
+       u32 time_reg;
+       u32 irq_mr;
+       u32 irq_sr;
+       u32 lock_pr;
+       u32 isr;
+       u32 ceh;
+};
+
+/* LOCK_PR register defines */
+#define LOCK_PR_UNLOCK_KEY 0x0000A25E  /* Magic unlock value */
+
+/* ICR defines */
+#define ICR_LARGE_BLOCKS 0x00000004    /* configure for 2KB blocks */
+#define ICR_ADDR4        0x00000002    /* configure for 4-word addrs */
+
+/* CEH defines */
+#define CEH_NORMAL_CE  0x00000001      /* do not force CE ON */
+
+/* ISR register defines */
+#define ISR_NAND_READY        0x00000001
+#define ISR_CONTROLLER_READY  0x00000002
+#define ISR_ECC_READY         0x00000004
+#define ISR_DECODER_ERRORS(s) ((((s) >> 4) & 3)+1)
+#define ISR_DECODER_FAILURE   0x00000040
+#define ISR_DECODER_ERROR     0x00000008
+
+/* time-out for NAND chip / controller loops, in us */
+#define LPC32X_NAND_TIMEOUT 5000
+
+/*
+ * There is a single instance of the NAND MLC controller
+ */
+
+static struct lpc32xx_nand_mlc_registers __iomem *lpc32xx_nand_mlc_registers
+       = (struct lpc32xx_nand_mlc_registers __iomem *)MLC_NAND_BASE;
+
+#define clkdiv(v, w, o) (((1+(clk/v)) & w) << o)
+
+/**
+ * OOB data in each small page are 6 'free' then 10 ECC bytes.
+ * To make things easier, when reading large pages, the four pages'
+ * 'free' OOB bytes are grouped in the first 24 bytes of the OOB buffer,
+ * while the the four ECC bytes are groupe in its last 40 bytes.
+ *
+ * The struct below represents how free vs ecc oob bytes are stored
+ * in the buffer.
+ *
+ * Note: the OOB bytes contain the bad block marker at offsets 0 and 1.
+ */
+
+struct lpc32xx_oob {
+       struct {
+               uint8_t free_oob_bytes[6];
+       } free[4];
+       struct {
+               uint8_t ecc_oob_bytes[10];
+       } ecc[4];
+};
+
+/*
+ * Initialize the controller
+ */
+
+static void lpc32xx_nand_init(void)
+{
+       unsigned int clk;
+
+       /* Configure controller for no software write protection, x8 bus
+          width, large block device, and 4 address words */
+
+       /* unlock controller registers with magic key */
+       writel(LOCK_PR_UNLOCK_KEY,
+              &lpc32xx_nand_mlc_registers->lock_pr);
+
+       /* enable large blocks and large NANDs */
+       writel(ICR_LARGE_BLOCKS | ICR_ADDR4,
+              &lpc32xx_nand_mlc_registers->icr);
+
+       /* Make sure MLC interrupts are disabled */
+       writel(0, &lpc32xx_nand_mlc_registers->irq_mr);
+
+       /* Normal chip enable operation */
+       writel(CEH_NORMAL_CE,
+              &lpc32xx_nand_mlc_registers->ceh);
+
+       /* Setup NAND timing */
+       clk = get_hclk_clk_rate();
+
+       writel(
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY, 0x03, 24) |
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY, 0x1F, 19) |
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_NAND_TA,    0x07, 16) |
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_HIGH,    0x0F, 12) |
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_LOW,     0x0F, 8) |
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_HIGH,    0x0F, 4) |
+               clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_LOW,     0x0F, 0),
+               &lpc32xx_nand_mlc_registers->time_reg);
+}
+
+#if !defined(CONFIG_SPL_BUILD)
+
+/**
+ * lpc32xx_cmd_ctrl - write command to either cmd or data register
+ */
+
+static void lpc32xx_cmd_ctrl(struct mtd_info *mtd, int cmd,
+                                  unsigned int ctrl)
+{
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       if (ctrl & NAND_CLE)
+               writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->cmd);
+       else if (ctrl & NAND_ALE)
+               writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->addr);
+}
+
+/**
+ * lpc32xx_read_byte - read a byte from the NAND
+ * @mtd:       MTD device structure
+ */
+
+static uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
+{
+       return readb(&lpc32xx_nand_mlc_registers->data);
+}
+
+/**
+ * lpc32xx_dev_ready - test if NAND device (actually controller) is ready
+ * @mtd:       MTD device structure
+ * @mode:      mode to set the ECC HW to.
+ */
+
+static int lpc32xx_dev_ready(struct mtd_info *mtd)
+{
+       /* means *controller* ready for us */
+       int status = readl(&lpc32xx_nand_mlc_registers->isr);
+       return status & ISR_CONTROLLER_READY;
+}
+
+/**
+ * ECC layout -- this is needed whatever ECC mode we are using.
+ * In a 2KB (4*512B) page, R/S codes occupy 40 (4*10) bytes.
+ * To make U-Boot's life easier, we pack 'useable' OOB at the
+ * front and R/S ECC at the back.
+ */
+
+static struct nand_ecclayout lpc32xx_largepage_ecclayout = {
+       .eccbytes = 40,
+       .eccpos = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+                  34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+                  44, 45, 46, 47, 48, 48, 50, 51, 52, 53,
+                  54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+                  },
+       .oobfree = {
+               /* bytes 0 and 1 are used for the bad block marker */
+               {
+                       .offset = 2,
+                       .length = 22
+               },
+       }
+};
+
+/**
+ * lpc32xx_read_page_hwecc - read in- and out-of-band data with ECC
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Use large block Auto Decode Read Mode(1) as described in User Manual
+ * section 8.6.2.1.
+ *
+ * The initial Read Mode and Read Start commands are sent by the caller.
+ *
+ * ECC will be false if out-of-band data has been updated since in-band
+ * data was initially written.
+ */
+
+static int lpc32xx_read_page_hwecc(struct mtd_info *mtd,
+       struct nand_chip *chip, uint8_t *buf, int oob_required,
+       int page)
+{
+       unsigned int i, status, timeout, err, max_bitflips = 0;
+       struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+       /* go through all four small pages */
+       for (i = 0; i < 4; i++) {
+               /* start auto decode (reads 528 NAND bytes) */
+               writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
+               /* wait for controller to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_CONTROLLER_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if decoder failed, return failure */
+               if (status & ISR_DECODER_FAILURE)
+                       return -1;
+               /* keep count of maximum bitflips performed */
+               if (status & ISR_DECODER_ERROR) {
+                       err = ISR_DECODER_ERRORS(status);
+                       if (err > max_bitflips)
+                               max_bitflips = err;
+               }
+               /* copy first 512 bytes into buffer */
+               memcpy(buf+512*i, lpc32xx_nand_mlc_registers->buff, 512);
+               /* copy next 6 bytes at front of OOB buffer */
+               memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
+               /* copy last 10 bytes (R/S ECC) at back of OOB buffer */
+               memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
+       }
+       return max_bitflips;
+}
+
+/**
+ * lpc32xx_read_page_raw - read raw (in-band, out-of-band and ECC) data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Read NAND directly; can read pages with invalid ECC.
+ */
+
+static int lpc32xx_read_page_raw(struct mtd_info *mtd,
+       struct nand_chip *chip, uint8_t *buf, int oob_required,
+       int page)
+{
+       unsigned int i, status, timeout;
+       struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+       /* when we get here we've already had the Read Mode(1) */
+
+       /* go through all four small pages */
+       for (i = 0; i < 4; i++) {
+               /* wait for NAND to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_NAND_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if NAND stalled, return failure */
+               if (!(status & ISR_NAND_READY))
+                       return -1;
+               /* copy first 512 bytes into buffer */
+               memcpy(buf+512*i, lpc32xx_nand_mlc_registers->data, 512);
+               /* copy next 6 bytes at front of OOB buffer */
+               memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->data, 6);
+               /* copy last 10 bytes (R/S ECC) at back of OOB buffer */
+               memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->data, 10);
+       }
+       return 0;
+}
+
+/**
+ * lpc32xx_read_oob - read out-of-band data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ *
+ * Read out-of-band data. User Manual section 8.6.4 suggests using Read
+ * Mode(3) which the controller will turn into a Read Mode(1) internally
+ * but nand_base.c will turn Mode(3) into Mode(0), so let's use Mode(0)
+ * directly.
+ *
+ * ECC covers in- and out-of-band data and was written when out-of-band
+ * data was blank. Therefore, if the out-of-band being read here is not
+ * blank, then the ECC will be false and the read will return bitflips,
+ * even in case of ECC failure where we will return 5 bitflips. The
+ * caller should be prepared to handle this.
+ */
+
+static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+       int page)
+{
+       unsigned int i, status, timeout, err, max_bitflips = 0;
+       struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+       /* No command was sent before calling read_oob() so send one */
+
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+       /* go through all four small pages */
+       for (i = 0; i < 4; i++) {
+               /* start auto decode (reads 528 NAND bytes) */
+               writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
+               /* wait for controller to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_CONTROLLER_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if decoder failure, count 'one too many' bitflips */
+               if (status & ISR_DECODER_FAILURE)
+                       max_bitflips = 5;
+               /* keep count of maximum bitflips performed */
+               if (status & ISR_DECODER_ERROR) {
+                       err = ISR_DECODER_ERRORS(status);
+                       if (err > max_bitflips)
+                               max_bitflips = err;
+               }
+               /* set read pointer to OOB area */
+               writel(0, &lpc32xx_nand_mlc_registers->robp);
+               /* copy next 6 bytes at front of OOB buffer */
+               memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
+               /* copy next 10 bytes (R/S ECC) at back of OOB buffer */
+               memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
+       }
+       return max_bitflips;
+}
+
+/**
+ * lpc32xx_write_page_hwecc - write in- and out-of-band data with ECC
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ *
+ * Use large block Auto Encode as per User Manual section 8.6.4.
+ *
+ * The initial Write Serial Input and final Auto Program commands are
+ * sent by the caller.
+ */
+
+static int lpc32xx_write_page_hwecc(struct mtd_info *mtd,
+       struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+       unsigned int i, status, timeout;
+       struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+       /* when we get here we've already had the SEQIN */
+       for (i = 0; i < 4; i++) {
+               /* start encode (expects 518 writes to buff) */
+               writel(0, &lpc32xx_nand_mlc_registers->ecc_enc_reg);
+               /* copy first 512 bytes from buffer */
+               memcpy(&lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
+               /* copy next 6 bytes from OOB buffer -- excluding ECC */
+               memcpy(&lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
+               /* wait for ECC to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_ECC_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if ECC stalled, return failure */
+               if (!(status & ISR_ECC_READY))
+                       return -1;
+               /* Trigger auto encode (writes 528 bytes to NAND) */
+               writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_enc_reg);
+               /* wait for controller to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_CONTROLLER_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if controller stalled, return error */
+               if (!(status & ISR_CONTROLLER_READY))
+                       return -1;
+       }
+       return 0;
+}
+
+/**
+ * lpc32xx_write_page_raw - write raw (in-band, out-of-band and ECC) data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Use large block write but without encode.
+ *
+ * The initial Write Serial Input and final Auto Program commands are
+ * sent by the caller.
+ *
+ * This function will write the full out-of-band data, including the
+ * ECC area. Therefore, it can write pages with valid *or* invalid ECC.
+ */
+
+static int lpc32xx_write_page_raw(struct mtd_info *mtd,
+       struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+       unsigned int i;
+       struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+       /* when we get here we've already had the Read Mode(1) */
+       for (i = 0; i < 4; i++) {
+               /* copy first 512 bytes from buffer */
+               memcpy(lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
+               /* copy next 6 bytes into OOB buffer -- excluding ECC */
+               memcpy(lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
+               /* copy next 10 bytes into OOB buffer -- that is 'ECC' */
+               memcpy(lpc32xx_nand_mlc_registers->buff, &oob->ecc[i], 10);
+       }
+       return 0;
+}
+
+/**
+ * lpc32xx_write_oob - write out-of-band data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ *
+ * Since ECC covers in- and out-of-band data, writing out-of-band data
+ * with ECC will render the page ECC wrong -- or, if the page was blank,
+ * then it will produce a good ECC but a later in-band data write will
+ * render it wrong.
+ *
+ * Therefore, do not compute or write any ECC, and always return success.
+ *
+ * This implies that we do four writes, since non-ECC out-of-band data
+ * are not contiguous in a large page.
+ */
+
+static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+       int page)
+{
+       /* update oob on all 4 subpages in sequence */
+       unsigned int i, status, timeout;
+       struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+       for (i = 0; i < 4; i++) {
+               /* start data input */
+               chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x200+0x210*i, page);
+               /* copy 6 non-ECC out-of-band bytes directly into NAND */
+               memcpy(lpc32xx_nand_mlc_registers->data, &oob->free[i], 6);
+               /* program page */
+               chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+               /* wait for NAND to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_NAND_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if NAND stalled, return error */
+               if (!(status & ISR_NAND_READY))
+                       return -1;
+       }
+       return 0;
+}
+
+/**
+ * lpc32xx_waitfunc - wait until a command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ *
+ * Wait for controller and FLASH to both be ready.
+ */
+
+static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+       int status;
+       unsigned int timeout;
+       /* wait until both controller and NAND are ready */
+       for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+               status = readl(&lpc32xx_nand_mlc_registers->isr);
+               if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
+                   == (ISR_CONTROLLER_READY || ISR_NAND_READY))
+                       break;
+               udelay(1);
+       }
+       /* if controller or NAND stalled, return error */
+       if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
+           != (ISR_CONTROLLER_READY || ISR_NAND_READY))
+               return -1;
+       /* write NAND status command */
+       writel(NAND_CMD_STATUS, &lpc32xx_nand_mlc_registers->cmd);
+       /* read back status and return it */
+       return readb(&lpc32xx_nand_mlc_registers->data);
+}
+
+/*
+ * We are self-initializing, so we need our own chip struct
+ */
+
+static struct nand_chip lpc32xx_chip;
+
+/*
+ * Initialize the controller
+ */
+
+void board_nand_init(void)
+{
+       /* we have only one device anyway */
+       struct mtd_info *mtd = &nand_info[0];
+       /* chip is struct nand_chip, and is now provided by the driver. */
+       mtd->priv = &lpc32xx_chip;
+       /* to store return status in case we need to print it */
+       int ret;
+
+       /* Set all BOARDSPECIFIC (actually core-specific) fields  */
+
+       lpc32xx_chip.IO_ADDR_R = &lpc32xx_nand_mlc_registers->buff;
+       lpc32xx_chip.IO_ADDR_W = &lpc32xx_nand_mlc_registers->buff;
+       lpc32xx_chip.cmd_ctrl = lpc32xx_cmd_ctrl;
+       /* do not set init_size: nand_base.c will read sizes from chip */
+       lpc32xx_chip.dev_ready = lpc32xx_dev_ready;
+       /* do not set setup_read_retry: this is NAND-chip-specific */
+       /* do not set chip_delay: we have dev_ready defined. */
+       lpc32xx_chip.options |= NAND_NO_SUBPAGE_WRITE;
+
+       /* Set needed ECC fields */
+
+       lpc32xx_chip.ecc.mode = NAND_ECC_HW;
+       lpc32xx_chip.ecc.layout = &lpc32xx_largepage_ecclayout;
+       lpc32xx_chip.ecc.size = 512;
+       lpc32xx_chip.ecc.bytes = 10;
+       lpc32xx_chip.ecc.strength = 4;
+       lpc32xx_chip.ecc.read_page = lpc32xx_read_page_hwecc;
+       lpc32xx_chip.ecc.read_page_raw = lpc32xx_read_page_raw;
+       lpc32xx_chip.ecc.write_page = lpc32xx_write_page_hwecc;
+       lpc32xx_chip.ecc.write_page_raw = lpc32xx_write_page_raw;
+       lpc32xx_chip.ecc.read_oob = lpc32xx_read_oob;
+       lpc32xx_chip.ecc.write_oob = lpc32xx_write_oob;
+       lpc32xx_chip.waitfunc = lpc32xx_waitfunc;
+
+       lpc32xx_chip.read_byte = lpc32xx_read_byte; /* FIXME: NEEDED? */
+
+       /* BBT options: read from last two pages */
+       lpc32xx_chip.bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK
+               | NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE
+               | NAND_BBT_WRITE;
+
+       /* Initialize NAND interface */
+       lpc32xx_nand_init();
+
+       /* identify chip */
+       ret = nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL);
+       if (ret) {
+               error("nand_scan_ident returned %i", ret);
+               return;
+       }
+
+       /* finish scanning the chip */
+       ret = nand_scan_tail(mtd);
+       if (ret) {
+               error("nand_scan_tail returned %i", ret);
+               return;
+       }
+
+       /* chip is good, register it */
+       ret = nand_register(0);
+       if (ret)
+               error("nand_register returned %i", ret);
+}
+
+#else /* defined(CONFIG_SPL_BUILD) */
+
+void nand_init(void)
+{
+       /* enable NAND controller */
+       lpc32xx_mlc_nand_init();
+       /* initialize NAND controller */
+       lpc32xx_nand_init();
+}
+
+void nand_deselect(void)
+{
+       /* nothing to do, but SPL requires this function */
+}
+
+static int read_single_page(uint8_t *dest, int page,
+       struct lpc32xx_oob *oob)
+{
+       int status, i, timeout, err, max_bitflips = 0;
+
+       /* enter read mode */
+       writel(NAND_CMD_READ0, &lpc32xx_nand_mlc_registers->cmd);
+       /* send column (lsb then MSB) and page (lsb to MSB) */
+       writel(0, &lpc32xx_nand_mlc_registers->addr);
+       writel(0, &lpc32xx_nand_mlc_registers->addr);
+       writel(page & 0xff, &lpc32xx_nand_mlc_registers->addr);
+       writel((page>>8) & 0xff, &lpc32xx_nand_mlc_registers->addr);
+       writel((page>>16) & 0xff, &lpc32xx_nand_mlc_registers->addr);
+       /* start reading */
+       writel(NAND_CMD_READSTART, &lpc32xx_nand_mlc_registers->cmd);
+
+       /* large page auto decode read */
+       for (i = 0; i < 4; i++) {
+               /* start auto decode (reads 528 NAND bytes) */
+               writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
+               /* wait for controller to return to ready state */
+               for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+                       status = readl(&lpc32xx_nand_mlc_registers->isr);
+                       if (status & ISR_CONTROLLER_READY)
+                               break;
+                       udelay(1);
+               }
+               /* if controller stalled, return error */
+               if (!(status & ISR_CONTROLLER_READY))
+                       return -1;
+               /* if decoder failure, return error */
+               if (status & ISR_DECODER_FAILURE)
+                       return -1;
+               /* keep count of maximum bitflips performed */
+               if (status & ISR_DECODER_ERROR) {
+                       err = ISR_DECODER_ERRORS(status);
+                       if (err > max_bitflips)
+                               max_bitflips = err;
+               }
+               /* copy first 512 bytes into buffer */
+               memcpy(dest+i*512, lpc32xx_nand_mlc_registers->buff, 512);
+               /* copy next 6 bytes bytes into OOB buffer */
+               memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
+       }
+       return max_bitflips;
+}
+
+/*
+ * Load U-Boot signed image.
+ * This loads an image from NAND, skipping bad blocks.
+ * A block is declared bad if at least one of its readable pages has
+ * a bad block marker in its OOB at position 0.
+ * If all pages ion a block are unreadable, the block is considered
+ * bad (i.e., assumed not to be part of the image) and skipped.
+ *
+ * IMPORTANT NOTE:
+ *
+ * If the first block of the image is fully unreadable, it will be
+ * ignored and skipped as if it had been marked bad. If it was not
+ * actually marked bad at the time of writing the image, the resulting
+ * image loaded will lack a header and magic number. It could thus be
+ * considered as a raw, headerless, image and SPL might erroneously
+ * jump into it.
+ *
+ * In order to avoid this risk, LPC32XX-based boards which use this
+ * driver MUST define CONFIG_SPL_PANIC_ON_RAW_IMAGE.
+ */
+
+#define BYTES_PER_PAGE 2048
+#define PAGES_PER_BLOCK 64
+#define BYTES_PER_BLOCK (BYTES_PER_PAGE * PAGES_PER_BLOCK)
+#define PAGES_PER_CHIP_MAX 524288
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+       int bytes_left = size;
+       int pages_left = DIV_ROUND_UP(size, BYTES_PER_PAGE);
+       int blocks_left = DIV_ROUND_UP(size, BYTES_PER_BLOCK);
+       int block = 0;
+       int page = offs / BYTES_PER_PAGE;
+       /* perform reads block by block */
+       while (blocks_left) {
+               /* compute first page number to read */
+               void *block_page_dst = dst;
+               /* read at most one block, possibly less */
+               int block_bytes_left = bytes_left;
+               if (block_bytes_left > BYTES_PER_BLOCK)
+                       block_bytes_left = BYTES_PER_BLOCK;
+               /* keep track of good, failed, and "bad" pages */
+               int block_pages_good = 0;
+               int block_pages_bad = 0;
+               int block_pages_err = 0;
+               /* we shall read a full block of pages, maybe less */
+               int block_pages_left = pages_left;
+               if (block_pages_left > PAGES_PER_BLOCK)
+                       block_pages_left = PAGES_PER_BLOCK;
+               int block_pages = block_pages_left;
+               int block_page = page;
+               /* while pages are left and the block is not known as bad */
+               while ((block_pages > 0) && (block_pages_bad == 0)) {
+                       /* we will read OOB, too, for bad block markers */
+                       struct lpc32xx_oob oob;
+                       /* read page */
+                       int res = read_single_page(block_page_dst, block_page,
+                                                  &oob);
+                       /* count readable pages */
+                       if (res >= 0) {
+                               /* this page is good */
+                               block_pages_good++;
+                               /* this page is bad */
+                               if ((oob.free[0].free_oob_bytes[0] != 0xff)
+                                   | (oob.free[0].free_oob_bytes[1] != 0xff))
+                                       block_pages_bad++;
+                       } else
+                               /* count errors */
+                               block_pages_err++;
+                       /* we're done with this page */
+                       block_page++;
+                       block_page_dst += BYTES_PER_PAGE;
+                       if (block_pages)
+                               block_pages--;
+               }
+               /* a fully unreadable block is considered bad */
+               if (block_pages_good == 0)
+                       block_pages_bad = block_pages_err;
+               /* errors are fatal only in good blocks */
+               if ((block_pages_err > 0) && (block_pages_bad == 0))
+                       return -1;
+               /* we keep reads only of good blocks */
+               if (block_pages_bad == 0) {
+                       dst += block_bytes_left;
+                       bytes_left -= block_bytes_left;
+                       pages_left -= block_pages_left;
+                       blocks_left--;
+               }
+               /* good or bad, we're done with this block */
+               block++;
+               page += PAGES_PER_BLOCK;
+       }
+
+       /* report success */
+       return 0;
+}
+
+#endif /* CONFIG_SPL_BUILD */
index 3ff86b703f80e7055a961daeb674f6552a85fe9a..5a5269aa06d06cc64085654d6dafbb21e0ddaa6f 100644 (file)
@@ -35,6 +35,7 @@ obj-$(CONFIG_GRETH) += greth.o
 obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o
 obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
 obj-$(CONFIG_LAN91C96) += lan91c96.o
+obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o
 obj-$(CONFIG_MACB) += macb.o
 obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
 obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o
diff --git a/drivers/net/lpc32xx_eth.c b/drivers/net/lpc32xx_eth.c
new file mode 100644 (file)
index 0000000..fcadf0c
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * LPC32xx Ethernet MAC interface driver
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <net.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/types.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/config.h>
+
+/*
+ * Notes:
+ *
+ * 1. Unless specified otherwise, all references to tables or paragraphs
+ *    are to UM10326, "LPC32x0 and LPC32x0/01 User manual".
+ *
+ * 2. Only bitfield masks/values which are actually used by the driver
+ *    are defined.
+ */
+
+/* a single RX descriptor. The controller has an array of these */
+struct lpc32xx_eth_rxdesc {
+       u32 packet;             /* Receive packet pointer */
+       u32 control;            /* Descriptor command status */
+};
+
+#define LPC32XX_ETH_RX_DESC_SIZE (sizeof(struct lpc32xx_eth_rxdesc))
+
+/* RX control bitfields/masks (see Table 330) */
+#define LPC32XX_ETH_RX_CTRL_SIZE_MASK 0x000007FF
+#define LPC32XX_ETH_RX_CTRL_UNUSED    0x7FFFF800
+#define LPC32XX_ETH_RX_CTRL_INTERRUPT 0x80000000
+
+/* a single RX status. The controller has an array of these */
+struct lpc32xx_eth_rxstat {
+       u32 statusinfo;         /* Transmit Descriptor status */
+       u32 statushashcrc;      /* Transmit Descriptor CRCs */
+};
+
+#define LPC32XX_ETH_RX_STAT_SIZE (sizeof(struct lpc32xx_eth_rxstat))
+
+/* RX statusinfo bitfields/masks (see Table 333) */
+#define RX_STAT_RXSIZE 0x000007FF
+/* Helper: OR of all errors except RANGE */
+#define RX_STAT_ERRORS 0x1B800000
+
+/* a single TX descriptor. The controller has an array of these */
+struct lpc32xx_eth_txdesc {
+       u32 packet;             /* Transmit packet pointer */
+       u32 control;            /* Descriptor control */
+};
+
+#define LPC32XX_ETH_TX_DESC_SIZE (sizeof(struct lpc32xx_eth_txdesc))
+
+/* TX control bitfields/masks (see Table 335) */
+#define TX_CTRL_TXSIZE    0x000007FF
+#define TX_CTRL_LAST      0x40000000
+
+/* a single TX status. The controller has an array of these */
+struct lpc32xx_eth_txstat {
+       u32 statusinfo;         /* Transmit Descriptor status */
+};
+
+#define LPC32XX_ETH_TX_STAT_SIZE (sizeof(struct lpc32xx_eth_txstat))
+
+/* Ethernet MAC interface registers (see Table 283) */
+struct lpc32xx_eth_registers {
+       /* MAC registers - 0x3106_0000 to 0x3106_01FC */
+       u32 mac1;               /* MAC configuration register 1 */
+       u32 mac2;               /* MAC configuration register 2 */
+       u32 ipgt;               /* Back-to-back Inter-Packet Gap reg. */
+       u32 ipgr;               /* Non-back-to-back IPG register */
+       u32 clrt;               /* Collision Window / Retry register */
+       u32 maxf;               /* Maximum Frame register */
+       u32 supp;               /* Phy Support register */
+       u32 test;
+       u32 mcfg;               /* MII management configuration reg. */
+       u32 mcmd;               /* MII management command register */
+       u32 madr;               /* MII management address register */
+       u32 mwtd;               /* MII management wite data register */
+       u32 mrdd;               /* MII management read data register */
+       u32 mind;               /* MII management indicators register */
+       u32 reserved1[2];
+       u32 sa0;                /* Station address register 0 */
+       u32 sa1;                /* Station address register 1 */
+       u32 sa2;                /* Station address register 2 */
+       u32 reserved2[45];
+       /* Control registers */
+       u32 command;
+       u32 status;
+       u32 rxdescriptor;
+       u32 rxstatus;
+       u32 rxdescriptornumber; /* actually, number MINUS ONE */
+       u32 rxproduceindex;     /* head of rx desc fifo */
+       u32 rxconsumeindex;     /* tail of rx desc fifo */
+       u32 txdescriptor;
+       u32 txstatus;
+       u32 txdescriptornumber; /* actually, number MINUS ONE */
+       u32 txproduceindex;     /* head of rx desc fifo */
+       u32 txconsumeindex;     /* tail of rx desc fifo */
+       u32 reserved3[10];
+       u32 tsv0;               /* Transmit status vector register 0 */
+       u32 tsv1;               /* Transmit status vector register 1 */
+       u32 rsv;                /* Receive status vector register */
+       u32 reserved4[3];
+       u32 flowcontrolcounter;
+       u32 flowcontrolstatus;
+       u32 reserved5[34];
+       /* RX filter registers - 0x3106_0200 to 0x3106_0FDC */
+       u32 rxfilterctrl;
+       u32 rxfilterwolstatus;
+       u32 rxfilterwolclear;
+       u32 reserved6;
+       u32 hashfilterl;
+       u32 hashfilterh;
+       u32 reserved7[882];
+       /* Module control registers - 0x3106_0FE0 to 0x3106_0FF8 */
+       u32 intstatus;          /* Interrupt status register */
+       u32 intenable;
+       u32 intclear;
+       u32 intset;
+       u32 reserved8;
+       u32 powerdown;
+       u32 reserved9;
+};
+
+/* MAC1 register bitfields/masks and offsets (see Table 283) */
+#define MAC1_RECV_ENABLE        0x00000001
+#define MAC1_PASS_ALL_RX_FRAMES 0x00000002
+#define MAC1_SOFT_RESET         0x00008000
+/* Helper: general reset */
+#define MAC1_RESETS             0x0000CF00
+
+/* MAC2 register bitfields/masks and offsets (see Table 284) */
+#define MAC2_FULL_DUPLEX    0x00000001
+#define MAC2_CRC_ENABLE     0x00000010
+#define MAC2_PAD_CRC_ENABLE 0x00000020
+
+/* SUPP register bitfields/masks and offsets (see Table 290) */
+#define SUPP_SPEED 0x00000100
+
+/* MCFG register bitfields/masks and offsets (see Table 292) */
+#define MCFG_CLOCK_SELECT_MASK  0x0000001C
+/* divide clock by 28 (see Table 293) */
+#define MCFG_CLOCK_SELECT_DIV28 0x0000001C
+
+/* MADR register bitfields/masks and offsets (see Table 295) */
+#define MADR_REG_MASK   0x0000001F
+#define MADR_PHY_MASK   0x00001F00
+#define MADR_REG_OFFSET 0
+#define MADR_PHY_OFFSET 8
+
+/* MIND register bitfields/masks (see Table 298) */
+#define MIND_BUSY      0x00000001
+
+/* COMMAND register bitfields/masks and offsets (see Table 283) */
+#define COMMAND_RXENABLE      0x00000001
+#define COMMAND_TXENABLE      0x00000002
+#define COMMAND_PASSRUNTFRAME 0x00000040
+#define COMMAND_FULL_DUPLEX   0x00000400
+/* Helper: general reset */
+#define COMMAND_RESETS        0x0000001C
+
+/* STATUS register bitfields/masks and offsets (see Table 283) */
+#define STATUS_RXSTATUS 0x00000001
+#define STATUS_TXSTATUS 0x00000002
+
+/* RXFILTERCTRL register bitfields/masks (see Table 319) */
+#define RXFILTERCTRL_ACCEPTBROADCAST 0x00000002
+#define RXFILTERCTRL_ACCEPTPERFECT   0x00000020
+
+/* Buffers and descriptors */
+
+#define ATTRS(n) __aligned(n)
+
+#define TX_BUF_COUNT 4
+#define RX_BUF_COUNT 4
+
+struct lpc32xx_eth_buffers {
+       ATTRS(4) struct lpc32xx_eth_txdesc tx_desc[TX_BUF_COUNT];
+       ATTRS(4) struct lpc32xx_eth_txstat tx_stat[TX_BUF_COUNT];
+       ATTRS(PKTALIGN) u8 tx_buf[TX_BUF_COUNT*PKTSIZE_ALIGN];
+       ATTRS(4) struct lpc32xx_eth_rxdesc rx_desc[RX_BUF_COUNT];
+       ATTRS(8) struct lpc32xx_eth_rxstat rx_stat[RX_BUF_COUNT];
+       ATTRS(PKTALIGN) u8 rx_buf[RX_BUF_COUNT*PKTSIZE_ALIGN];
+};
+
+/* port device data struct */
+struct lpc32xx_eth_device {
+       struct eth_device dev;
+       struct lpc32xx_eth_registers *regs;
+       struct lpc32xx_eth_buffers *bufs;
+};
+
+#define LPC32XX_ETH_DEVICE_SIZE (sizeof(struct lpc32xx_eth_device))
+
+/* generic macros */
+#define to_lpc32xx_eth(_d) container_of(_d, struct lpc32xx_eth_device, dev)
+
+/* timeout for MII polling */
+#define MII_TIMEOUT 10000000
+
+/* limits for PHY and register addresses */
+#define MII_MAX_REG (MADR_REG_MASK >> MADR_REG_OFFSET)
+
+#define MII_MAX_PHY (MADR_PHY_MASK >> MADR_PHY_OFFSET)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if defined(CONFIG_PHYLIB) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+/*
+ * mii_reg_read - miiphy_read callback function.
+ *
+ * Returns 16bit phy register value, or 0xffff on error
+ */
+static int mii_reg_read(const char *devname, u8 phy_adr, u8 reg_ofs, u16 *data)
+{
+       struct eth_device *dev = eth_get_dev_by_name(devname);
+       struct lpc32xx_eth_device *dlpc32xx_eth = to_lpc32xx_eth(dev);
+       struct lpc32xx_eth_registers *regs = dlpc32xx_eth->regs;
+       u32 mind_reg;
+       u32 timeout;
+
+       /* check parameters */
+       if (phy_adr > MII_MAX_PHY) {
+               printf("%s:%u: Invalid PHY address %d\n",
+                      __func__, __LINE__, phy_adr);
+               return -EFAULT;
+       }
+       if (reg_ofs > MII_MAX_REG) {
+               printf("%s:%u: Invalid register offset %d\n",
+                      __func__, __LINE__, reg_ofs);
+               return -EFAULT;
+       }
+
+       /* write the phy and reg addressse into the MII address reg */
+       writel((phy_adr << MADR_PHY_OFFSET) | (reg_ofs << MADR_REG_OFFSET),
+              &regs->madr);
+
+       /* write 1 to the MII command register to cause a read */
+       writel(1, &regs->mcmd);
+
+       /* wait till the MII is not busy */
+       timeout = MII_TIMEOUT;
+       do {
+               /* read MII indicators register */
+               mind_reg = readl(&regs->mind);
+               if (--timeout == 0)
+                       break;
+       } while (mind_reg & MIND_BUSY);
+
+       /* write 0 to the MII command register to finish the read */
+       writel(0, &regs->mcmd);
+
+       if (timeout == 0) {
+               printf("%s:%u: MII busy timeout\n", __func__, __LINE__);
+               return -EFAULT;
+       }
+
+       *data = (u16) readl(&regs->mrdd);
+
+       debug("%s:(adr %d, off %d) => %04x\n", __func__, phy_adr,
+             reg_ofs, *data);
+
+       return 0;
+}
+
+/*
+ * mii_reg_write - imiiphy_write callback function.
+ *
+ * Returns 0 if write succeed, -EINVAL on bad parameters
+ * -ETIME on timeout
+ */
+static int mii_reg_write(const char *devname, u8 phy_adr, u8 reg_ofs, u16 data)
+{
+       struct eth_device *dev = eth_get_dev_by_name(devname);
+       struct lpc32xx_eth_device *dlpc32xx_eth = to_lpc32xx_eth(dev);
+       struct lpc32xx_eth_registers *regs = dlpc32xx_eth->regs;
+       u32 mind_reg;
+       u32 timeout;
+
+       /* check parameters */
+       if (phy_adr > MII_MAX_PHY) {
+               printf("%s:%u: Invalid PHY address %d\n",
+                      __func__, __LINE__, phy_adr);
+               return -EFAULT;
+       }
+       if (reg_ofs > MII_MAX_REG) {
+               printf("%s:%u: Invalid register offset %d\n",
+                      __func__, __LINE__, reg_ofs);
+               return -EFAULT;
+       }
+
+       /* wait till the MII is not busy */
+       timeout = MII_TIMEOUT;
+       do {
+               /* read MII indicators register */
+               mind_reg = readl(&regs->mind);
+               if (--timeout == 0)
+                       break;
+       } while (mind_reg & MIND_BUSY);
+
+       if (timeout == 0) {
+               printf("%s:%u: MII busy timeout\n", __func__,
+                      __LINE__);
+               return -EFAULT;
+       }
+
+       /* write the phy and reg addressse into the MII address reg */
+       writel((phy_adr << MADR_PHY_OFFSET) | (reg_ofs << MADR_REG_OFFSET),
+              &regs->madr);
+
+       /* write data to the MII write register */
+       writel(data, &regs->mwtd);
+
+       /*debug("%s:(adr %d, off %d) <= %04x\n", __func__, phy_adr,
+               reg_ofs, data);*/
+
+       return 0;
+}
+#endif
+
+#if defined(CONFIG_PHYLIB)
+int lpc32xx_eth_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr,
+       int reg_addr)
+{
+       u16 data;
+       int ret;
+       ret = mii_reg_read(bus->name, phy_addr, reg_addr, &data);
+       if (ret)
+               return ret;
+       return data;
+}
+
+int lpc32xx_eth_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr,
+       int reg_addr, u16 data)
+{
+       return mii_reg_write(bus->name, phy_addr, reg_addr, data);
+}
+#endif
+
+/*
+ * Locate buffers in SRAM at 0x00001000 to avoid cache issues and
+ * maximize throughput.
+ */
+
+#define LPC32XX_ETH_BUFS 0x00001000
+
+static struct lpc32xx_eth_device lpc32xx_eth = {
+       .regs = (struct lpc32xx_eth_registers *)LPC32XX_ETH_BASE,
+       .bufs = (struct lpc32xx_eth_buffers *)LPC32XX_ETH_BUFS
+};
+
+#define TX_TIMEOUT 10000
+
+static int lpc32xx_eth_send(struct eth_device *dev, void *dataptr, int datasize)
+{
+       struct lpc32xx_eth_device *lpc32xx_eth_device =
+               container_of(dev, struct lpc32xx_eth_device, dev);
+       struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs;
+       struct lpc32xx_eth_buffers *bufs = lpc32xx_eth_device->bufs;
+       int timeout, tx_index;
+
+       /* time out if transmit descriptor array remains full too long */
+       timeout = TX_TIMEOUT;
+       while ((readl(&regs->status) & STATUS_TXSTATUS) &&
+              (readl(&regs->txconsumeindex)
+              == readl(&regs->txproduceindex))) {
+               if (timeout-- == 0)
+                       return -1;
+       }
+
+       /* determine next transmit packet index to use */
+       tx_index = readl(&regs->txproduceindex);
+
+       /* set up transmit packet */
+       writel((u32)dataptr, &bufs->tx_desc[tx_index].packet);
+       writel(TX_CTRL_LAST | ((datasize - 1) & TX_CTRL_TXSIZE),
+              &bufs->tx_desc[tx_index].control);
+       writel(0, &bufs->tx_stat[tx_index].statusinfo);
+
+       /* pass transmit packet to DMA engine */
+       tx_index = (tx_index + 1) % TX_BUF_COUNT;
+       writel(tx_index, &regs->txproduceindex);
+
+       /* transmission succeeded */
+       return 0;
+}
+
+#define RX_TIMEOUT 1000000
+
+static int lpc32xx_eth_recv(struct eth_device *dev)
+{
+       struct lpc32xx_eth_device *lpc32xx_eth_device =
+               container_of(dev, struct lpc32xx_eth_device, dev);
+       struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs;
+       struct lpc32xx_eth_buffers *bufs = lpc32xx_eth_device->bufs;
+       int timeout, rx_index;
+
+       /* time out if receive descriptor array remains empty too long */
+       timeout = RX_TIMEOUT;
+       while (readl(&regs->rxproduceindex) == readl(&regs->rxconsumeindex)) {
+               if (timeout-- == 0)
+                       return -1;
+       }
+
+       /* determine next receive packet index to use */
+       rx_index = readl(&regs->rxconsumeindex);
+
+       /* if data was valid, pass it on */
+       if (!(bufs->rx_stat[rx_index].statusinfo & RX_STAT_ERRORS))
+               NetReceive(&(bufs->rx_buf[rx_index*PKTSIZE_ALIGN]),
+                          (bufs->rx_stat[rx_index].statusinfo
+                           & RX_STAT_RXSIZE) + 1);
+
+       /* pass receive slot back to DMA engine */
+       rx_index = (rx_index + 1) % RX_BUF_COUNT;
+       writel(rx_index, &regs->rxconsumeindex);
+
+       /* reception successful */
+       return 0;
+}
+
+static int lpc32xx_eth_write_hwaddr(struct eth_device *dev)
+{
+       struct lpc32xx_eth_device *lpc32xx_eth_device =
+               container_of(dev, struct lpc32xx_eth_device, dev);
+       struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs;
+
+       /* Save station address */
+       writel((unsigned long) (dev->enetaddr[0] |
+               (dev->enetaddr[1] << 8)), &regs->sa2);
+       writel((unsigned long) (dev->enetaddr[2] |
+               (dev->enetaddr[3] << 8)), &regs->sa1);
+       writel((unsigned long) (dev->enetaddr[4] |
+               (dev->enetaddr[5] << 8)), &regs->sa0);
+
+       return 0;
+}
+
+static int lpc32xx_eth_init(struct eth_device *dev)
+{
+       struct lpc32xx_eth_device *lpc32xx_eth_device =
+               container_of(dev, struct lpc32xx_eth_device, dev);
+       struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs;
+       struct lpc32xx_eth_buffers *bufs = lpc32xx_eth_device->bufs;
+       int index;
+
+       /* Release SOFT reset to let MII talk to PHY */
+       clrbits_le32(&regs->mac1, MAC1_SOFT_RESET);
+
+       /* Configure Full/Half Duplex mode */
+       if (miiphy_duplex(dev->name, CONFIG_PHY_ADDR) == FULL) {
+               setbits_le32(&regs->mac2, MAC2_FULL_DUPLEX);
+               setbits_le32(&regs->command, COMMAND_FULL_DUPLEX);
+               writel(0x15, &regs->ipgt);
+       } else {
+               writel(0x12, &regs->ipgt);
+       }
+
+       /* Configure 100MBit/10MBit mode */
+       if (miiphy_speed(dev->name, CONFIG_PHY_ADDR) == _100BASET)
+               writel(SUPP_SPEED, &regs->supp);
+       else
+               writel(0, &regs->supp);
+
+       /* Initial MAC initialization */
+       writel(MAC1_PASS_ALL_RX_FRAMES, &regs->mac1);
+       writel(MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE, &regs->mac2);
+       writel(PKTSIZE_ALIGN, &regs->maxf);
+
+       /* Retries: 15 (0xF). Collision window: 57 (0x37). */
+       writel(0x370F, &regs->clrt);
+
+       /* Set IP gap pt 2 to default 0x12 but pt 1 to non-default 0 */
+       writel(0x0012, &regs->ipgr);
+
+       /* pass runt (smaller than 64 bytes) frames */
+       writel(COMMAND_PASSRUNTFRAME, &regs->command);
+
+       /* Save station address */
+       writel((unsigned long) (dev->enetaddr[0] |
+               (dev->enetaddr[1] << 8)), &regs->sa2);
+       writel((unsigned long) (dev->enetaddr[2] |
+               (dev->enetaddr[3] << 8)), &regs->sa1);
+       writel((unsigned long) (dev->enetaddr[4] |
+               (dev->enetaddr[5] << 8)), &regs->sa0);
+
+       /* set up transmit buffers */
+       for (index = 0; index < TX_BUF_COUNT; index++) {
+               bufs->tx_desc[index].control = 0;
+               bufs->tx_stat[index].statusinfo = 0;
+       }
+       writel((u32)(&bufs->tx_desc), (u32 *)&regs->txdescriptor);
+       writel((u32)(&bufs->tx_stat), &regs->txstatus);
+       writel(TX_BUF_COUNT-1, &regs->txdescriptornumber);
+
+       /* set up receive buffers */
+       for (index = 0; index < RX_BUF_COUNT; index++) {
+               bufs->rx_desc[index].packet =
+                       (u32) (bufs->rx_buf+index*PKTSIZE_ALIGN);
+               bufs->rx_desc[index].control = PKTSIZE_ALIGN - 1;
+               bufs->rx_stat[index].statusinfo = 0;
+               bufs->rx_stat[index].statushashcrc = 0;
+       }
+       writel((u32)(&bufs->rx_desc), &regs->rxdescriptor);
+       writel((u32)(&bufs->rx_stat), &regs->rxstatus);
+       writel(RX_BUF_COUNT-1, &regs->rxdescriptornumber);
+
+       /* Enable broadcast and matching address packets */
+       writel(RXFILTERCTRL_ACCEPTBROADCAST |
+               RXFILTERCTRL_ACCEPTPERFECT, &regs->rxfilterctrl);
+
+       /* Clear and disable interrupts */
+       writel(0xFFFF, &regs->intclear);
+       writel(0, &regs->intenable);
+
+       /* Enable receive and transmit mode of MAC ethernet core */
+       setbits_le32(&regs->command, COMMAND_RXENABLE | COMMAND_TXENABLE);
+       setbits_le32(&regs->mac1, MAC1_RECV_ENABLE);
+
+       /*
+        * Perform a 'dummy' first send to work around Ethernet.1
+        * erratum (see ES_LPC3250 rev. 9 dated 1 June 2011).
+        * Use zeroed "index" variable as the dummy.
+        */
+
+       index = 0;
+       lpc32xx_eth_send(dev, &index, 4);
+
+       return 0;
+}
+
+static int lpc32xx_eth_halt(struct eth_device *dev)
+{
+       struct lpc32xx_eth_device *lpc32xx_eth_device =
+               container_of(dev, struct lpc32xx_eth_device, dev);
+       struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs;
+
+       /* Reset all MAC logic */
+       writel(MAC1_RESETS, &regs->mac1);
+       writel(COMMAND_RESETS, &regs->command);
+       /* Let reset condition settle */
+       udelay(2000);
+
+       return 0;
+}
+
+#if defined(CONFIG_PHYLIB)
+int lpc32xx_eth_phylib_init(struct eth_device *dev, int phyid)
+{
+       struct mii_dev *bus;
+       struct phy_device *phydev;
+       int ret;
+
+       bus = mdio_alloc();
+       if (!bus) {
+               printf("mdio_alloc failed\n");
+               return -ENOMEM;
+       }
+       bus->read = lpc32xx_eth_phy_read;
+       bus->write = lpc32xx_eth_phy_write;
+       sprintf(bus->name, dev->name);
+
+       ret = mdio_register(bus);
+       if (ret) {
+               printf("mdio_register failed\n");
+               free(bus);
+               return -ENOMEM;
+       }
+
+       phydev = phy_connect(bus, phyid, dev, PHY_INTERFACE_MODE_MII);
+       if (!phydev) {
+               printf("phy_connect failed\n");
+               return -ENODEV;
+       }
+
+       phy_config(phydev);
+       phy_startup(phydev);
+
+       return 0;
+}
+#endif
+
+int lpc32xx_eth_initialize(bd_t *bis)
+{
+       struct eth_device *dev = &lpc32xx_eth.dev;
+       struct lpc32xx_eth_registers *regs = lpc32xx_eth.regs;
+
+       /*
+        * Set RMII management clock rate. With HCLK at 104 MHz and
+        * a divider of 28, this will be 3.72 MHz.
+        */
+
+       writel(MCFG_CLOCK_SELECT_DIV28, &regs->mcfg);
+
+       /* Reset all MAC logic */
+       writel(MAC1_RESETS, &regs->mac1);
+       writel(COMMAND_RESETS, &regs->command);
+
+       /* wait 10 ms for the whole I/F to reset */
+       udelay(10000);
+
+       /* must be less than sizeof(dev->name) */
+       strcpy(dev->name, "eth0");
+
+       dev->init = (void *)lpc32xx_eth_init;
+       dev->halt = (void *)lpc32xx_eth_halt;
+       dev->send = (void *)lpc32xx_eth_send;
+       dev->recv = (void *)lpc32xx_eth_recv;
+       dev->write_hwaddr = (void *)lpc32xx_eth_write_hwaddr;
+
+       /* Release SOFT reset to let MII talk to PHY */
+       clrbits_le32(&regs->mac1, MAC1_SOFT_RESET);
+
+       /* register driver before talking to phy */
+       eth_register(dev);
+
+#if defined(CONFIG_PHYLIB)
+       lpc32xx_eth_phylib_init(dev, 0);
+#elif defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+       miiphy_register(dev->name, mii_reg_read, mii_reg_write);
+#endif
+
+       return 0;
+}
index edbd5201411cd12c109fe190f033b6d66f0c3b28..ce6f1cc74e069f8535d5c9c2afbb76310ffe9df7 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
 obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o
 obj-$(CONFIG_ICH_SPI) +=  ich.o
 obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
+obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
 obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
 obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
 obj-$(CONFIG_MXC_SPI) += mxc_spi.o
diff --git a/drivers/spi/lpc32xx_ssp.c b/drivers/spi/lpc32xx_ssp.c
new file mode 100644 (file)
index 0000000..c5b766c
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * LPC32xx SSP interface (SPI mode)
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/compat.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/arch/clk.h>
+
+/* SSP chip registers */
+struct ssp_regs {
+       u32 cr0;
+       u32 cr1;
+       u32 data;
+       u32 sr;
+       u32 cpsr;
+       u32 imsc;
+       u32 ris;
+       u32 mis;
+       u32 icr;
+       u32 dmacr;
+};
+
+/* CR1 register defines  */
+#define SSP_CR1_SSP_ENABLE 0x0002
+
+/* SR register defines  */
+#define SSP_SR_TNF 0x0002
+/* SSP status RX FIFO not empty bit */
+#define SSP_SR_RNE 0x0004
+
+/* lpc32xx spi slave */
+struct lpc32xx_spi_slave {
+       struct spi_slave slave;
+       struct ssp_regs *regs;
+};
+
+static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(
+       struct spi_slave *slave)
+{
+       return container_of(slave, struct lpc32xx_spi_slave, slave);
+}
+
+/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
+void spi_init(void)
+{
+       /*
+        *  nothing to do: clocking was enabled in lpc32xx_ssp_enable()
+        * and configuration will be done in spi_setup_slave()
+       */
+}
+
+/* the following is called in sequence by do_spi_xfer() */
+
+struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
+{
+       struct lpc32xx_spi_slave *lslave;
+
+       /* we only set up SSP0 for now, so ignore bus */
+
+       if (mode & SPI_3WIRE) {
+               error("3-wire mode not supported");
+               return NULL;
+       }
+
+       if (mode & SPI_SLAVE) {
+               error("slave mode not supported\n");
+               return NULL;
+       }
+
+       if (mode & SPI_PREAMBLE) {
+               error("preamble byte skipping not supported\n");
+               return NULL;
+       }
+
+       lslave = spi_alloc_slave(struct lpc32xx_spi_slave, bus, cs);
+       if (!lslave) {
+               printf("SPI_error: Fail to allocate lpc32xx_spi_slave\n");
+               return NULL;
+       }
+
+       lslave->regs = (struct ssp_regs *)SSP0_BASE;
+
+       /*
+        * 8 bit frame, SPI fmt, 500kbps -> clock divider is 26.
+        * Set SCR to 0 and CPSDVSR to 26.
+        */
+
+       writel(0x7, &lslave->regs->cr0); /* 8-bit chunks, SPI, 1 clk/bit */
+       writel(26, &lslave->regs->cpsr); /* SSP clock = HCLK/26 = 500kbps */
+       writel(0, &lslave->regs->imsc); /* do not raise any interrupts */
+       writel(0, &lslave->regs->icr); /* clear any pending interrupt */
+       writel(0, &lslave->regs->dmacr); /* do not do DMAs */
+       writel(SSP_CR1_SSP_ENABLE, &lslave->regs->cr1); /* enable SSP0 */
+       return &lslave->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+       struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
+
+       debug("(lpc32xx) spi_free_slave: 0x%08x\n", (u32)lslave);
+       free(lslave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+       /* only one bus and slave so far, always available */
+       return 0;
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+       const void *dout, void *din, unsigned long flags)
+{
+       struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
+       int bytelen = bitlen >> 3;
+       int idx_out = 0;
+       int idx_in = 0;
+       int start_time;
+
+       start_time = get_timer(0);
+       while ((idx_out < bytelen) || (idx_in < bytelen)) {
+               int status = readl(&lslave->regs->sr);
+               if ((idx_out < bytelen) && (status & SSP_SR_TNF))
+                       writel(((u8 *)dout)[idx_out++], &lslave->regs->data);
+               if ((idx_in < bytelen) && (status & status & SSP_SR_RNE))
+                       ((u8 *)din)[idx_in++] = readl(&lslave->regs->data);
+               if (get_timer(start_time) >= CONFIG_LPC32XX_SSP_TIMEOUT)
+                       return -1;
+       }
+       return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+       /* do nothing */
+}
diff --git a/include/configs/work_92105.h b/include/configs/work_92105.h
new file mode 100644 (file)
index 0000000..dc8e99f
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * WORK Microwave work_92105 board configuration file
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __CONFIG_WORK_92105_H__
+#define __CONFIG_WORK_92105_H__
+
+/* SoC and board defines */
+#include <linux/sizes.h>
+#include <asm/arch/cpu.h>
+
+/*
+ * Define work_92105 machine type by hand -- done only for compatibility
+ * with original board code
+ */
+#define MACH_TYPE_WORK_92105           736
+#define CONFIG_MACH_TYPE               MACH_TYPE_WORK_92105
+
+#define CONFIG_SYS_ICACHE_OFF
+#define CONFIG_SYS_DCACHE_OFF
+#if !defined(CONFIG_SPL_BUILD)
+#define CONFIG_SKIP_LOWLEVEL_INIT
+#endif
+#define CONFIG_BOARD_EARLY_INIT_F
+#define CONFIG_BOARD_EARLY_INIT_R
+
+/* generate LPC32XX-specific SPL image */
+#define CONFIG_LPC32XX_SPL
+
+/*
+ * Memory configurations
+ */
+#define CONFIG_NR_DRAM_BANKS           1
+#define CONFIG_SYS_MALLOC_LEN          SZ_1M
+#define CONFIG_SYS_SDRAM_BASE          EMC_DYCS0_BASE
+#define CONFIG_SYS_SDRAM_SIZE          SZ_128M
+#define CONFIG_SYS_TEXT_BASE           0x80100000
+#define CONFIG_SYS_MEMTEST_START       (CONFIG_SYS_SDRAM_BASE + SZ_32K)
+#define CONFIG_SYS_MEMTEST_END         (CONFIG_SYS_TEXT_BASE - SZ_1M)
+
+#define CONFIG_SYS_LOAD_ADDR           (CONFIG_SYS_SDRAM_BASE + SZ_32K)
+
+#define CONFIG_SYS_INIT_SP_ADDR                (CONFIG_SYS_SDRAM_BASE + SZ_512K \
+                                        - GENERATED_GBL_DATA_SIZE)
+
+/*
+ * Serial Driver
+ */
+#define CONFIG_SYS_LPC32XX_UART                5   /* UART5 - NS16550 */
+#define CONFIG_BAUDRATE                        115200
+
+/*
+ * Ethernet Driver
+ */
+
+#define CONFIG_PHY_SMSC
+#define CONFIG_LPC32XX_ETH
+#define CONFIG_PHYLIB
+#define CONFIG_PHY_ADDR 0
+#define CONFIG_SYS_FAULT_ECHO_LINK_DOWN
+#define CONFIG_CMD_MII
+#define CONFIG_CMD_PING
+#define CONFIG_CMD_DHCP
+/* FIXME: remove "Waiting for PHY auto negotiation to complete..." message */
+
+/*
+ * I2C driver
+ */
+
+#define CONFIG_SYS_I2C_LPC32XX
+#define CONFIG_SYS_I2C
+#define CONFIG_CMD_I2C
+#define CONFIG_SYS_I2C_SPEED 350000
+
+/*
+ * I2C EEPROM
+ */
+
+#define CONFIG_CMD_EEPROM
+#define CONFIG_SYS_I2C_EEPROM_ADDR 0x56
+#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2
+
+/*
+ * I2C RTC
+ */
+
+#define CONFIG_CMD_DATE
+#define CONFIG_RTC_DS1374
+
+/*
+ * I2C Temperature Sensor (DTT)
+ */
+
+#define CONFIG_CMD_DTT
+#define CONFIG_DTT_SENSORS { 0, 1 }
+#define CONFIG_DTT_DS620
+
+/*
+ * U-Boot General Configurations
+ */
+#define CONFIG_SYS_GENERIC_BOARD
+#define CONFIG_SYS_LONGHELP
+#define CONFIG_SYS_CBSIZE              1024
+#define CONFIG_SYS_PBSIZE              \
+       (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16)
+#define CONFIG_SYS_MAXARGS             16
+#define CONFIG_SYS_BARGSIZE            CONFIG_SYS_CBSIZE
+
+#define CONFIG_SYS_HUSH_PARSER
+
+#define CONFIG_AUTO_COMPLETE
+#define CONFIG_CMDLINE_EDITING
+#define CONFIG_VERSION_VARIABLE
+#define CONFIG_DISPLAY_CPUINFO
+#define CONFIG_DOS_PARTITION
+
+/*
+ * No NOR
+ */
+
+#define CONFIG_SYS_NO_FLASH
+
+/*
+ * NAND chip timings for FIXME: which one?
+ */
+
+#define CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY  333333333
+#define CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY   10000000
+#define CONFIG_LPC32XX_NAND_MLC_NAND_TA      18181818
+#define CONFIG_LPC32XX_NAND_MLC_RD_HIGH      31250000
+#define CONFIG_LPC32XX_NAND_MLC_RD_LOW       45454545
+#define CONFIG_LPC32XX_NAND_MLC_WR_HIGH      40000000
+#define CONFIG_LPC32XX_NAND_MLC_WR_LOW       83333333
+
+/*
+ * NAND
+ */
+
+/* driver configuration */
+#define CONFIG_SYS_NAND_SELF_INIT
+#define CONFIG_SYS_MAX_NAND_DEVICE 1
+#define CONFIG_SYS_MAX_NAND_CHIPS 1
+#define CONFIG_SYS_NAND_BASE MLC_NAND_BASE
+#define CONFIG_NAND_LPC32XX_MLC
+
+#define CONFIG_CMD_NAND
+
+/*
+ * GPIO
+ */
+
+#define CONFIG_CMD_GPIO
+#define CONFIG_LPC32XX_GPIO
+
+/*
+ * SSP/SPI/DISPLAY
+ */
+
+#define CONFIG_CMD_SPI
+#define CONFIG_LPC32XX_SSP
+#define CONFIG_LPC32XX_SSP_TIMEOUT 100000
+#define CONFIG_CMD_MAX6957
+#define CONFIG_CMD_HD44760
+/*
+ * Environment
+ */
+
+#define CONFIG_ENV_IS_IN_NAND          1
+#define CONFIG_ENV_SIZE                        0x00020000
+#define CONFIG_ENV_OFFSET              0x00100000
+#define CONFIG_ENV_OFFSET_REDUND       0x00120000
+#define CONFIG_ENV_ADDR                        0x80000100
+
+/*
+ * Provide default ethernet address
+ *
+ * THIS IS NORMALLY NOT DONE. HERE WE KEEP WHAT WAS IN THE PORTED
+ * BOARD CONFIG IN CASE SOME PROVISIONING PROCESS OUT THERE EXPECTS
+ * THIS MAC ADDRESS WHEN THE DEVICE HAS STILL ITS DEFAULT CONFIG.
+ */
+
+#define CONFIG_ETHADDR                 00:12:B4:00:AF:FE
+#define        CONFIG_OVERWRITE_ETHADDR_ONCE
+
+/*
+ * U-Boot Commands
+ */
+#include <config_cmd_default.h>
+
+/*
+ * Boot Linux
+ */
+#define CONFIG_CMDLINE_TAG
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_INITRD_TAG
+
+#define CONFIG_ZERO_BOOTDELAY_CHECK
+#define CONFIG_BOOTDELAY               3
+
+#define CONFIG_BOOTFILE                        "uImage"
+#define CONFIG_BOOTARGS                        "console=ttyS2,115200n8"
+#define CONFIG_LOADADDR                        0x80008000
+
+/*
+ * SPL
+ */
+
+/* SPL will be executed at offset 0 */
+#define CONFIG_SPL_TEXT_BASE 0x00000000
+/* SPL will use SRAM as stack */
+#define CONFIG_SPL_STACK     0x0000FFF8
+#define CONFIG_SPL_BOARD_INIT
+/* Use the framework and generic lib */
+#define CONFIG_SPL_FRAMEWORK
+#define CONFIG_SPL_LIBGENERIC_SUPPORT
+#define CONFIG_SPL_LIBCOMMON_SUPPORT
+/* SPL will use serial */
+#define CONFIG_SPL_SERIAL_SUPPORT
+/* SPL will load U-Boot from NAND offset 0x40000 */
+#define CONFIG_SPL_NAND_SUPPORT
+#define CONFIG_SPL_NAND_DRIVERS
+#define CONFIG_SPL_NAND_BASE
+#define CONFIG_SPL_NAND_BOOT
+#define CONFIG_SYS_NAND_U_BOOT_OFFS  0x00040000
+#define CONFIG_SPL_PAD_TO 0x20000
+/* U-Boot will be 0x40000 bytes, loaded and run at CONFIG_SYS_TEXT_BASE */
+#define CONFIG_SYS_MONITOR_LEN 0x40000 /* actually, MAX size */
+#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE
+#define CONFIG_SYS_NAND_U_BOOT_DST   CONFIG_SYS_TEXT_BASE
+
+/*
+ * Include SoC specific configuration
+ */
+#include <asm/arch/config.h>
+
+#endif  /* __CONFIG_WORK_92105_H__*/
index 058bca4d5d6c0193e7e83bb1ab3714359cdd2a8c..173159dc6287552c56e492ae09bc3d243b922095 100644 (file)
 #define _DTT_H_
 
 #if defined(CONFIG_DTT_ADM1021)        || \
-    defined(CONFIG_DTT_ADT7460)        || \
-    defined(CONFIG_DTT_DS1621) || \
-    defined(CONFIG_DTT_DS1775) || \
-    defined(CONFIG_DTT_LM63)   || \
-    defined(CONFIG_DTT_LM73)   || \
-    defined(CONFIG_DTT_LM75)   || \
-    defined(CONFIG_DTT_LM81)
+       defined(CONFIG_DTT_ADT7460)     || \
+       defined(CONFIG_DTT_DS1621)      || \
+       defined(CONFIG_DTT_DS1775)      || \
+       defined(CONFIG_DTT_DS620)       || \
+       defined(CONFIG_DTT_LM63)        || \
+       defined(CONFIG_DTT_LM73)        || \
+       defined(CONFIG_DTT_LM75)        || \
+       defined(CONFIG_DTT_LM81)
 
 #define CONFIG_DTT                             /* We have a DTT */
 
index 0e6af00c16bbab07a7c1284bca9963b0a4f2155c..3844be67d8e014d9c80b6a0ce12cd2fe70faac1d 100644 (file)
@@ -242,6 +242,7 @@ struct lmb;
 #define IH_TYPE_ATMELIMAGE     18      /* ATMEL ROM bootable Image     */
 #define IH_TYPE_SOCFPGAIMAGE   19      /* Altera SOCFPGA Preloader     */
 #define IH_TYPE_X86_SETUP      20      /* x86 setup.bin Image          */
+#define IH_TYPE_LPC32XXIMAGE   21      /* x86 setup.bin Image          */
 
 /*
  * Compression Types
index c69533e9aaead6f253facceaeac7c05812772773..d96e1dabf9874dcf60efa9385cbcf65194e4f51b 100644 (file)
@@ -57,6 +57,7 @@ int greth_initialize(bd_t *bis);
 void gt6426x_eth_initialize(bd_t *bis);
 int ks8851_mll_initialize(u8 dev_num, int base_addr);
 int lan91c96_initialize(u8 dev_num, int base_addr);
+int lpc32xx_eth_initialize(bd_t *bis);
 int macb_eth_initialize(int id, void *regs, unsigned int phy_addr);
 int mcdmafec_initialize(bd_t *bis);
 int mcffec_initialize(bd_t *bis);
index 88770b0611f6bd8b94b358d6256abb8b8a0b788b..4bbb15336edc929640a87518c094f0eff8df220b 100644 (file)
@@ -83,6 +83,7 @@ dumpimage-mkimage-objs := aisimage.o \
                        imximage.o \
                        kwbimage.o \
                        lib/md5.o \
+                       lpc32xximage.o \
                        mxsimage.o \
                        omapimage.o \
                        os_support.o \
diff --git a/tools/lpc32xximage.c b/tools/lpc32xximage.c
new file mode 100644 (file)
index 0000000..6b3865f
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Image manipulator for LPC32XX SoCs
+ *
+ * (C) Copyright 2015  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * Derived from omapimage.c:
+ *
+ * (C) Copyright 2010
+ * Linaro LTD, www.linaro.org
+ * Author: John Rigby <john.rigby@linaro.org>
+ * Based on TI's signGP.c
+ *
+ * (C) Copyright 2009
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * (C) Copyright 2008
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Prafulla Wadaskar <prafulla@marvell.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include "imagetool.h"
+#include <compiler.h>
+#include <image.h>
+
+/*
+ * NAND page 0 boot header
+ */
+
+struct nand_page_0_boot_header {
+       uint32_t data[129];
+       uint32_t pad[383];
+};
+
+/*
+ * Default ICC (interface configuration data [sic]) if none specified
+ * in board config
+ */
+
+#ifndef LPC32XX_BOOT_ICR
+#define LPC32XX_BOOT_ICR 0x00000096
+#endif
+
+/*
+ * Default boot NAND page size if none specified in board config
+ */
+
+#ifndef LPC32XX_BOOT_NAND_PAGESIZE
+#define LPC32XX_BOOT_NAND_PAGESIZE 2048
+#endif
+
+/*
+ * Default boot NAND pages per sector if none specified in board config
+ */
+
+#ifndef LPC32XX_BOOT_NAND_PAGES_PER_SECTOR
+#define LPC32XX_BOOT_NAND_PAGES_PER_SECTOR 64
+#endif
+
+/*
+ * Maximum size for boot code is 56K unless defined in board config
+ */
+
+#ifndef LPC32XX_BOOT_CODESIZE
+#define LPC32XX_BOOT_CODESIZE (56*1024)
+#endif
+
+/* signature byte for a readable block */
+
+#define LPC32XX_BOOT_BLOCK_OK 0xaa
+
+static struct nand_page_0_boot_header lpc32xximage_header;
+
+static int lpc32xximage_check_image_types(uint8_t type)
+{
+       if (type == IH_TYPE_LPC32XXIMAGE)
+               return EXIT_SUCCESS;
+       return EXIT_FAILURE;
+}
+
+static int lpc32xximage_verify_header(unsigned char *ptr, int image_size,
+                       struct image_tool_params *params)
+{
+       struct nand_page_0_boot_header *hdr =
+               (struct nand_page_0_boot_header *)ptr;
+
+       /* turn image size from bytes to NAND pages, page 0 included */
+       int image_size_in_pages = ((image_size - 1)
+                                 / LPC32XX_BOOT_NAND_PAGESIZE);
+
+       if (hdr->data[0] != (0xff & LPC32XX_BOOT_ICR))
+               return -1;
+       if (hdr->data[1] != (0xff & ~LPC32XX_BOOT_ICR))
+               return -1;
+       if (hdr->data[2] != (0xff & LPC32XX_BOOT_ICR))
+               return -1;
+       if (hdr->data[3] != (0xff & ~LPC32XX_BOOT_ICR))
+               return -1;
+       if (hdr->data[4] != (0xff & image_size_in_pages))
+               return -1;
+       if (hdr->data[5] != (0xff & ~image_size_in_pages))
+               return -1;
+       if (hdr->data[6] != (0xff & image_size_in_pages))
+               return -1;
+       if (hdr->data[7] != (0xff & ~image_size_in_pages))
+               return -1;
+       if (hdr->data[8] != (0xff & image_size_in_pages))
+               return -1;
+       if (hdr->data[9] != (0xff & ~image_size_in_pages))
+               return -1;
+       if (hdr->data[10] != (0xff & image_size_in_pages))
+               return -1;
+       if (hdr->data[11] != (0xff & ~image_size_in_pages))
+               return -1;
+       if (hdr->data[12] != LPC32XX_BOOT_BLOCK_OK)
+               return -1;
+       if (hdr->data[128] != LPC32XX_BOOT_BLOCK_OK)
+               return -1;
+       return 0;
+}
+
+static void print_hdr_byte(struct nand_page_0_boot_header *hdr, int ofs)
+{
+       printf("header[%d] = %02x\n", ofs, hdr->data[ofs]);
+}
+
+static void lpc32xximage_print_header(const void *ptr)
+{
+       struct nand_page_0_boot_header *hdr =
+               (struct nand_page_0_boot_header *)ptr;
+       int ofs;
+
+       for (ofs = 0; ofs <= 12; ofs++)
+               print_hdr_byte(hdr, ofs);
+       print_hdr_byte(hdr, 128);
+}
+
+static void lpc32xximage_set_header(void *ptr, struct stat *sbuf, int ifd,
+                               struct image_tool_params *params)
+{
+       struct nand_page_0_boot_header *hdr =
+               (struct nand_page_0_boot_header *)ptr;
+
+       /* turn image size from bytes to NAND pages, page 0 included */
+       int image_size_in_pages = ((sbuf->st_size
+                                 + LPC32XX_BOOT_NAND_PAGESIZE - 1)
+                                 / LPC32XX_BOOT_NAND_PAGESIZE);
+
+       /* fill header -- default byte value is 0x00, not 0xFF */
+       memset((void *)hdr, 0, sizeof(*hdr));
+       hdr->data[0] = (hdr->data[2] = 0xff & LPC32XX_BOOT_ICR);
+       hdr->data[1] = (hdr->data[3] = 0xff & ~LPC32XX_BOOT_ICR);
+       hdr->data[4] = (hdr->data[6] = (hdr->data[8]
+                      = (hdr->data[10] = 0xff & image_size_in_pages)));
+       hdr->data[5] = (hdr->data[7] = (hdr->data[9]
+                      = (hdr->data[11] = 0xff & ~image_size_in_pages)));
+       hdr->data[12] = (hdr->data[128] = LPC32XX_BOOT_BLOCK_OK);
+}
+
+/*
+ * lpc32xximage parameters
+ */
+U_BOOT_IMAGE_TYPE(
+       lpc32xximage,
+       "LPC32XX Boot Image",
+       sizeof(lpc32xximage_header),
+       (void *)&lpc32xximage_header,
+       NULL,
+       lpc32xximage_verify_header,
+       lpc32xximage_print_header,
+       lpc32xximage_set_header,
+       NULL,
+       lpc32xximage_check_image_types,
+       NULL,
+       NULL
+);