From 79d75d752717fb4106ec49abaddbd7744c775a35 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 27 Feb 2015 02:40:33 +0900 Subject: ARM: move -march=* and -mtune= options to arch/arm/Makefile My main motivations for this commit are: [1] Follow the arch/arm/Makefile style of Linux Kernel [2] Maintain compiler options systematically Currently, we give -march=* and -mtune=* options inconsistently: Only some of the CPUs pass -march=* and -mtune=* options. By collecting such options into the single place arch/arm/Makefile we can tell which options are missing at a glance. [3] Prepare for deprecating arch/*/cpu/*/config.mk Note: This commit just moves the compiler options so as not to change the behavior at all. It does not care about the correctness of the given options. Fox example, "-march=armv5te" might be better than "-march=armv4" for ARM946EJS, but it is beyond the scope this commit. Also, filling the missing -march=* and -tune=* is left to follow-up patches. Signed-off-by: Masahiro Yamada Acked-by: Marek Vasut Acked-by: Stefan Roese --- arch/arm/Makefile | 32 ++++++++++++++++++++++++++++++++ arch/arm/cpu/arm1136/config.mk | 9 --------- arch/arm/cpu/arm1176/config.mk | 9 --------- arch/arm/cpu/arm720t/config.mk | 9 --------- arch/arm/cpu/arm920t/config.mk | 8 -------- arch/arm/cpu/arm926ejs/config.mk | 8 -------- arch/arm/cpu/arm946es/config.mk | 8 -------- arch/arm/cpu/armv7/config.mk | 5 ----- arch/arm/cpu/armv8/config.mk | 2 -- arch/arm/cpu/pxa/config.mk | 2 -- arch/arm/cpu/sa1100/config.mk | 9 --------- 11 files changed, 32 insertions(+), 69 deletions(-) delete mode 100644 arch/arm/cpu/arm1136/config.mk delete mode 100644 arch/arm/cpu/arm1176/config.mk delete mode 100644 arch/arm/cpu/arm720t/config.mk delete mode 100644 arch/arm/cpu/arm920t/config.mk delete mode 100644 arch/arm/cpu/arm926ejs/config.mk delete mode 100644 arch/arm/cpu/arm946es/config.mk delete mode 100644 arch/arm/cpu/sa1100/config.mk diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 08946de..54be925 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -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 index a82c6ce..0000000 --- a/arch/arm/cpu/arm1136/config.mk +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2002 -# Gary Jennejohn, DENX Software Engineering, -# -# 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 index 5dc2ebb..0000000 --- a/arch/arm/cpu/arm1176/config.mk +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2002 -# Gary Jennejohn, DENX Software Engineering, -# -# 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 index 772fb41..0000000 --- a/arch/arm/cpu/arm720t/config.mk +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2002 -# Sysgo Real-Time Solutions, GmbH -# Marius Groeger -# -# 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 index 799afff..0000000 --- a/arch/arm/cpu/arm920t/config.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2002 -# Gary Jennejohn, DENX Software Engineering, -# -# 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 index bdb3da1..0000000 --- a/arch/arm/cpu/arm926ejs/config.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2002 -# Gary Jennejohn, DENX Software Engineering, -# -# SPDX-License-Identifier: GPL-2.0+ -# - -PLATFORM_CPPFLAGS += -march=armv5te diff --git a/arch/arm/cpu/arm946es/config.mk b/arch/arm/cpu/arm946es/config.mk deleted file mode 100644 index 438668d..0000000 --- a/arch/arm/cpu/arm946es/config.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2002 -# Gary Jennejohn, DENX Software Engineering, -# -# SPDX-License-Identifier: GPL-2.0+ -# - -PLATFORM_CPPFLAGS += -march=armv4 diff --git a/arch/arm/cpu/armv7/config.mk b/arch/arm/cpu/armv7/config.mk index 6c82c3b..63591d4 100644 --- a/arch/arm/cpu/armv7/config.mk +++ b/arch/arm/cpu/armv7/config.mk @@ -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 diff --git a/arch/arm/cpu/armv8/config.mk b/arch/arm/cpu/armv8/config.mk index f5b9559..6850258 100644 --- a/arch/arm/cpu/armv8/config.mk +++ b/arch/arm/cpu/armv8/config.mk @@ -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) diff --git a/arch/arm/cpu/pxa/config.mk b/arch/arm/cpu/pxa/config.mk index 525f5d3..7fb5316 100644 --- a/arch/arm/cpu/pxa/config.mk +++ b/arch/arm/cpu/pxa/config.mk @@ -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 index 3afa685..0000000 --- a/arch/arm/cpu/sa1100/config.mk +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2002 -# Sysgo Real-Time Solutions, GmbH -# Marius Groeger -# -# SPDX-License-Identifier: GPL-2.0+ -# - -PLATFORM_CPPFLAGS += -march=armv4 -mtune=strongarm1100 -- cgit v1.1 From ac2916a2245d999006585b6bb1e312639fa4929b Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:43 +0200 Subject: lpc32xx: add Ethernet support Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/cpu/arm926ejs/lpc32xx/cpu.c | 9 + arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 7 + arch/arm/include/asm/arch-lpc32xx/config.h | 3 + arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 1 + drivers/net/Makefile | 1 + drivers/net/lpc32xx_eth.c | 637 ++++++++++++++++++++++++++ include/netdev.h | 1 + 7 files changed, 659 insertions(+) create mode 100644 drivers/net/lpc32xx_eth.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c b/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c index 35095a9..eec4d9e 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -55,3 +56,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 diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index b567657..062db8d 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -37,3 +37,10 @@ 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); +} diff --git a/arch/arm/include/asm/arch-lpc32xx/config.h b/arch/arm/include/asm/arch-lpc32xx/config.h index 564441c..d57bc48 100644 --- a/arch/arm/include/asm/arch-lpc32xx/config.h +++ b/arch/arm/include/asm/arch-lpc32xx/config.h @@ -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 diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index 28812be3..a6b8826 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -8,5 +8,6 @@ #define _LPC32XX_SYS_PROTO_H void lpc32xx_uart_init(unsigned int uart_id); +void lpc32xx_mac_init(void); #endif /* _LPC32XX_SYS_PROTO_H */ diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3ff86b7..5a5269a 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -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 index 0000000..fcadf0c --- /dev/null +++ b/drivers/net/lpc32xx_eth.c @@ -0,0 +1,637 @@ +/* + * LPC32xx Ethernet MAC interface driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD - 3ADEV + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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), + ®s->madr); + + /* write 1 to the MII command register to cause a read */ + writel(1, ®s->mcmd); + + /* wait till the MII is not busy */ + timeout = MII_TIMEOUT; + do { + /* read MII indicators register */ + mind_reg = readl(®s->mind); + if (--timeout == 0) + break; + } while (mind_reg & MIND_BUSY); + + /* write 0 to the MII command register to finish the read */ + writel(0, ®s->mcmd); + + if (timeout == 0) { + printf("%s:%u: MII busy timeout\n", __func__, __LINE__); + return -EFAULT; + } + + *data = (u16) readl(®s->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(®s->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), + ®s->madr); + + /* write data to the MII write register */ + writel(data, ®s->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(®s->status) & STATUS_TXSTATUS) && + (readl(®s->txconsumeindex) + == readl(®s->txproduceindex))) { + if (timeout-- == 0) + return -1; + } + + /* determine next transmit packet index to use */ + tx_index = readl(®s->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, ®s->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(®s->rxproduceindex) == readl(®s->rxconsumeindex)) { + if (timeout-- == 0) + return -1; + } + + /* determine next receive packet index to use */ + rx_index = readl(®s->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, ®s->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)), ®s->sa2); + writel((unsigned long) (dev->enetaddr[2] | + (dev->enetaddr[3] << 8)), ®s->sa1); + writel((unsigned long) (dev->enetaddr[4] | + (dev->enetaddr[5] << 8)), ®s->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(®s->mac1, MAC1_SOFT_RESET); + + /* Configure Full/Half Duplex mode */ + if (miiphy_duplex(dev->name, CONFIG_PHY_ADDR) == FULL) { + setbits_le32(®s->mac2, MAC2_FULL_DUPLEX); + setbits_le32(®s->command, COMMAND_FULL_DUPLEX); + writel(0x15, ®s->ipgt); + } else { + writel(0x12, ®s->ipgt); + } + + /* Configure 100MBit/10MBit mode */ + if (miiphy_speed(dev->name, CONFIG_PHY_ADDR) == _100BASET) + writel(SUPP_SPEED, ®s->supp); + else + writel(0, ®s->supp); + + /* Initial MAC initialization */ + writel(MAC1_PASS_ALL_RX_FRAMES, ®s->mac1); + writel(MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE, ®s->mac2); + writel(PKTSIZE_ALIGN, ®s->maxf); + + /* Retries: 15 (0xF). Collision window: 57 (0x37). */ + writel(0x370F, ®s->clrt); + + /* Set IP gap pt 2 to default 0x12 but pt 1 to non-default 0 */ + writel(0x0012, ®s->ipgr); + + /* pass runt (smaller than 64 bytes) frames */ + writel(COMMAND_PASSRUNTFRAME, ®s->command); + + /* Save station address */ + writel((unsigned long) (dev->enetaddr[0] | + (dev->enetaddr[1] << 8)), ®s->sa2); + writel((unsigned long) (dev->enetaddr[2] | + (dev->enetaddr[3] << 8)), ®s->sa1); + writel((unsigned long) (dev->enetaddr[4] | + (dev->enetaddr[5] << 8)), ®s->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 *)®s->txdescriptor); + writel((u32)(&bufs->tx_stat), ®s->txstatus); + writel(TX_BUF_COUNT-1, ®s->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), ®s->rxdescriptor); + writel((u32)(&bufs->rx_stat), ®s->rxstatus); + writel(RX_BUF_COUNT-1, ®s->rxdescriptornumber); + + /* Enable broadcast and matching address packets */ + writel(RXFILTERCTRL_ACCEPTBROADCAST | + RXFILTERCTRL_ACCEPTPERFECT, ®s->rxfilterctrl); + + /* Clear and disable interrupts */ + writel(0xFFFF, ®s->intclear); + writel(0, ®s->intenable); + + /* Enable receive and transmit mode of MAC ethernet core */ + setbits_le32(®s->command, COMMAND_RXENABLE | COMMAND_TXENABLE); + setbits_le32(®s->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, ®s->mac1); + writel(COMMAND_RESETS, ®s->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, ®s->mcfg); + + /* Reset all MAC logic */ + writel(MAC1_RESETS, ®s->mac1); + writel(COMMAND_RESETS, ®s->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(®s->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; +} diff --git a/include/netdev.h b/include/netdev.h index c69533e..d96e1da 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -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); -- cgit v1.1 From c8381bf435ddb104594df00411a8ebd049dd753c Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:44 +0200 Subject: lpc32xx: mtd: nand: add MLC NAND controller The controller's Reed-Solomon ECC hardware is used except of course for raw reads and writes. It covers in- and out-of-band data together. The SPL framework is supported. Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 6 + arch/arm/include/asm/arch-lpc32xx/clk.h | 4 + arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 1 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/lpc32xx_nand_mlc.c | 764 ++++++++++++++++++++++++++ 5 files changed, 776 insertions(+) create mode 100644 drivers/mtd/nand/lpc32xx_nand_mlc.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index 062db8d..be4c93d 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -44,3 +44,9 @@ void lpc32xx_mac_init(void) 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); +} diff --git a/arch/arm/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h index 92f6c15..bc7d33d 100644 --- a/arch/arm/include/asm/arch-lpc32xx/clk.h +++ b/arch/arm/include/asm/arch-lpc32xx/clk.h @@ -147,6 +147,10 @@ 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) + unsigned int get_sys_clk_rate(void); unsigned int get_hclk_pll_rate(void); unsigned int get_hclk_clk_div(void); diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index a6b8826..0c4e712 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -9,5 +9,6 @@ void lpc32xx_uart_init(unsigned int uart_id); void lpc32xx_mac_init(void); +void lpc32xx_mlc_nand_init(void); #endif /* _LPC32XX_SYS_PROTO_H */ diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1f02bfc..347ea62 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -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 index 0000000..8156fe9 --- /dev/null +++ b/drivers/mtd/nand/lpc32xx_nand_mlc.c @@ -0,0 +1,764 @@ +/* + * LPC32xx MLC NAND flash controller driver + * + * (C) Copyright 2014 3ADEV + * Written by Albert ARIBAUD + * + * 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 +#include +#include +#include +#include +#include +#include + +/* + * 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 */ -- cgit v1.1 From 5e862b95399e6e5ea7748ee29a38756685d622fd Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:45 +0200 Subject: lpc32xx: i2c: add LPC32xx I2C interface support Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 11 ++ arch/arm/include/asm/arch-lpc32xx/clk.h | 4 + arch/arm/include/asm/arch-lpc32xx/cpu.h | 2 + arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 1 + drivers/i2c/Makefile | 1 + drivers/i2c/lpc32xx_i2c.c | 249 ++++++++++++++++++++++++++ 6 files changed, 268 insertions(+) create mode 100644 drivers/i2c/lpc32xx_i2c.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index be4c93d..81b53ea 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -50,3 +50,14 @@ 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); +} diff --git a/arch/arm/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h index bc7d33d..781ac07 100644 --- a/arch/arm/include/asm/arch-lpc32xx/clk.h +++ b/arch/arm/include/asm/arch-lpc32xx/clk.h @@ -123,6 +123,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) diff --git a/arch/arm/include/asm/arch-lpc32xx/cpu.h b/arch/arm/include/asm/arch-lpc32xx/cpu.h index 199b4a0..1067107 100644 --- a/arch/arm/include/asm/arch-lpc32xx/cpu.h +++ b/arch/arm/include/asm/arch-lpc32xx/cpu.h @@ -37,6 +37,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 */ diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index 0c4e712..a4a05d1 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -10,5 +10,6 @@ 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); #endif /* _LPC32XX_SYS_PROTO_H */ diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..26ea854 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -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 index 0000000..78d26e4 --- /dev/null +++ b/drivers/i2c/lpc32xx_i2c.c @@ -0,0 +1,249 @@ +/* + * LPC32xx I2C interface driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD - 3ADEV + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* + * 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) -- cgit v1.1 From 606f7047603422746d112e41937649d44f311af4 Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:46 +0200 Subject: lpc32xx: add GPIO support This driver only supports Driver Model, not legacy model. Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 5 + arch/arm/include/asm/arch-lpc32xx/gpio.h | 43 +++++ drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/lpc32xx_gpio.c | 293 +++++++++++++++++++++++++++++++ 5 files changed, 349 insertions(+) create mode 100644 arch/arm/include/asm/arch-lpc32xx/gpio.h create mode 100644 drivers/gpio/lpc32xx_gpio.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index 81b53ea..a407098 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -9,6 +9,7 @@ #include #include #include +#include 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; @@ -61,3 +62,7 @@ void lpc32xx_i2c_init(unsigned int devnum) ctrl |= CLK_I2C2_ENABLE; writel(ctrl, &clk->i2cclk_ctrl); } + +U_BOOT_DEVICE(lpc32xx_gpios) = { + .name = "gpio_lpc32xx" +}; diff --git a/arch/arm/include/asm/arch-lpc32xx/gpio.h b/arch/arm/include/asm/arch-lpc32xx/gpio.h new file mode 100644 index 0000000..3bd94e3 --- /dev/null +++ b/arch/arm/include/asm/arch-lpc32xx/gpio.h @@ -0,0 +1,43 @@ +/* + * LPC32xx GPIO interface + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * 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/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b609e73..7b5178a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -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. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fe9a3b2..85f71c5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -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 index 0000000..96b3125 --- /dev/null +++ b/drivers/gpio/lpc32xx_gpio.c @@ -0,0 +1,293 @@ +/* + * LPC32xxGPIO driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/** + * 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, ®s->p0_dir_clr); + break; + case 1: + writel(mask, ®s->p1_dir_clr); + break; + case 2: + /* ports 2 and 3 share a common direction */ + case 3: + writel(mask, ®s->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(®s->p0_inp_state); + break; + case 1: + value = readl(®s->p1_inp_state); + break; + case 2: + value = readl(®s->p2_inp_state); + break; + case 3: + value = readl(®s->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, ®s->p0_outp_set); + break; + case 1: + writel(mask, ®s->p1_outp_set); + break; + case 2: + writel(mask, ®s->p2_outp_set); + break; + case 3: + writel(mask, ®s->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, ®s->p0_outp_clr); + break; + case 1: + writel(mask, ®s->p1_outp_clr); + break; + case 2: + writel(mask, ®s->p2_outp_clr); + break; + case 3: + writel(mask, ®s->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, ®s->p0_dir_set); + break; + case 1: + writel(mask, ®s->p1_dir_set); + break; + case 2: + /* ports 2 and 3 share a common direction */ + case 3: + writel(mask, ®s->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), +}; -- cgit v1.1 From 981219eebe3cc29f155a37951788c18786260514 Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:47 +0200 Subject: lpc32xx: add LPC32xx SSP support (SPI mode) Reviewed-by: Jagannadha Sutradharudu Teki Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 14 +++ arch/arm/include/asm/arch-lpc32xx/clk.h | 3 + arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 1 + drivers/spi/Makefile | 1 + drivers/spi/lpc32xx_ssp.c | 144 ++++++++++++++++++++++++++ 5 files changed, 163 insertions(+) create mode 100644 drivers/spi/lpc32xx_ssp.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index a407098..5a453e3 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -8,11 +8,13 @@ #include #include #include +#include #include #include 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) { @@ -66,3 +68,15 @@ void lpc32xx_i2c_init(unsigned int devnum) 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/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h index 781ac07..2cb5703 100644 --- a/arch/arm/include/asm/arch-lpc32xx/clk.h +++ b/arch/arm/include/asm/arch-lpc32xx/clk.h @@ -155,6 +155,9 @@ struct clk_pm_regs { #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) + unsigned int get_sys_clk_rate(void); unsigned int get_hclk_pll_rate(void); unsigned int get_hclk_clk_div(void); diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index a4a05d1..86d5ee9 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -11,5 +11,6 @@ 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); #endif /* _LPC32XX_SYS_PROTO_H */ diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index edbd520..ce6f1cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -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 index 0000000..c5b766c --- /dev/null +++ b/drivers/spi/lpc32xx_ssp.c @@ -0,0 +1,144 @@ +/* + * LPC32xx SSP interface (SPI mode) + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* 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 */ +} -- cgit v1.1 From 24d528e3fa3a8370aa5263592c362b7d61642340 Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:48 +0200 Subject: dtt: add ds620 support Signed-off-by: Albert ARIBAUD (3ADEV) --- drivers/hwmon/Makefile | 1 + drivers/hwmon/ds620.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dtt.h | 15 ++++++------ 3 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 drivers/hwmon/ds620.c diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 25b8e8a..b4fb057 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -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 index 0000000..1ecc3da --- /dev/null +++ b/drivers/hwmon/ds620.c @@ -0,0 +1,65 @@ +/* + * DS620 DTT support + * + * (C) Copyright 2014 3ADEV + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Dallas Semiconductor's DS1621/1631 Digital Thermometer and Thermostat. + */ + +#include +#include +#include + +/* + * 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; +} diff --git a/include/dtt.h b/include/dtt.h index 058bca4..173159d 100644 --- a/include/dtt.h +++ b/include/dtt.h @@ -12,13 +12,14 @@ #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 */ -- cgit v1.1 From 39f520bb62be67ef98303c47dd5c2d76861fea0f Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:49 +0200 Subject: lpc32xx: add lpc32xx-spl.bin boot image target Signed-off-by: Albert ARIBAUD (3ADEV) --- common/image.c | 1 + include/image.h | 1 + tools/Makefile | 1 + tools/lpc32xximage.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 tools/lpc32xximage.c diff --git a/common/image.c b/common/image.c index a911aa9..162b682 100644 --- a/common/image.c +++ b/common/image.c @@ -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, "", "", }, }; diff --git a/include/image.h b/include/image.h index 0e6af00..3844be6 100644 --- a/include/image.h +++ b/include/image.h @@ -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 diff --git a/tools/Makefile b/tools/Makefile index 88770b0..4bbb153 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -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 index 0000000..6b3865f --- /dev/null +++ b/tools/lpc32xximage.c @@ -0,0 +1,178 @@ +/* + * Image manipulator for LPC32XX SoCs + * + * (C) Copyright 2015 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * Derived from omapimage.c: + * + * (C) Copyright 2010 + * Linaro LTD, www.linaro.org + * Author: John Rigby + * Based on TI's signGP.c + * + * (C) Copyright 2009 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2008 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "imagetool.h" +#include +#include + +/* + * 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 +); -- cgit v1.1 From 8c80eb3b533c73a4b4a505fbaf925cdaafe0b5d2 Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:50 +0200 Subject: Introduce CONFIG_SPL_PANIC_ON_RAW_IMAGE introduce CONFIG_SPL_PANIC_ON_RAW_IMAGE. An SPL which define this will panic() if the image it has loaded does not have a mkimage signature. Signed-off-by: Albert ARIBAUD (3ADEV) --- README | 10 ++++++++++ common/spl/spl.c | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/README b/README index b7c2a17..9b748cc 100644 --- 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 diff --git a/common/spl/spl.c b/common/spl/spl.c index cd75bbc..8e1fb40 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -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 } } -- cgit v1.1 From 412ae53aadb53cd63e754d638bafe6e426aeafee Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:51 +0200 Subject: lpc32xx: add support for board work_92105 Work_92105 from Work Microwave is an LPC3250- based board with the following features: - 64MB or 128MB 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 This board has SPL support, and uses the LPC32XX boot image format. Signed-off-by: Albert ARIBAUD (3ADEV) --- Makefile | 20 ++ arch/arm/Kconfig | 6 + arch/arm/cpu/arm926ejs/lpc32xx/Makefile | 2 + arch/arm/cpu/arm926ejs/lpc32xx/clk.c | 34 ++ arch/arm/cpu/arm926ejs/lpc32xx/cpu.c | 1 + arch/arm/cpu/arm926ejs/lpc32xx/dram.c | 77 +++++ arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S | 45 +++ arch/arm/include/asm/arch-lpc32xx/clk.h | 5 + arch/arm/include/asm/arch-lpc32xx/cpu.h | 1 + arch/arm/include/asm/arch-lpc32xx/emc.h | 21 ++ arch/arm/include/asm/arch-lpc32xx/mux.h | 18 ++ arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 6 +- board/work-microwave/work_92105/Kconfig | 15 + board/work-microwave/work_92105/MAINTAINERS | 6 + board/work-microwave/work_92105/Makefile | 10 + board/work-microwave/work_92105/README | 91 ++++++ board/work-microwave/work_92105/work_92105.c | 77 +++++ .../work-microwave/work_92105/work_92105_display.c | 349 +++++++++++++++++++++ .../work-microwave/work_92105/work_92105_display.h | 14 + board/work-microwave/work_92105/work_92105_spl.c | 85 +++++ configs/work_92105_defconfig | 6 + include/configs/work_92105.h | 241 ++++++++++++++ 22 files changed, 1129 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/arm926ejs/lpc32xx/dram.c create mode 100644 arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S create mode 100644 arch/arm/include/asm/arch-lpc32xx/mux.h create mode 100644 board/work-microwave/work_92105/Kconfig create mode 100644 board/work-microwave/work_92105/MAINTAINERS create mode 100644 board/work-microwave/work_92105/Makefile create mode 100644 board/work-microwave/work_92105/README create mode 100644 board/work-microwave/work_92105/work_92105.c create mode 100644 board/work-microwave/work_92105/work_92105_display.c create mode 100644 board/work-microwave/work_92105/work_92105_display.h create mode 100644 board/work-microwave/work_92105/work_92105_spl.c create mode 100644 configs/work_92105_defconfig create mode 100644 include/configs/work_92105.h diff --git a/Makefile b/Makefile index 53ad450..fa7aa89 100644 --- 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/arch/arm/Kconfig b/arch/arm/Kconfig index 80b0d34..086ca85 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -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" diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/Makefile b/arch/arm/cpu/arm926ejs/lpc32xx/Makefile index 314f004..4837377 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/Makefile +++ b/arch/arm/cpu/arm926ejs/lpc32xx/Makefile @@ -6,3 +6,5 @@ # obj-y = cpu.o clk.o devices.o timer.o + +obj-$(CONFIG_SPL_BUILD) += dram.o lowlevel_init.o diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/clk.c b/arch/arm/cpu/arm926ejs/lpc32xx/clk.c index b7a44d5..1ef8a36 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/clk.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/clk.c @@ -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(); diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c b/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c index eec4d9e..f757474 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE; diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/dram.c b/arch/arm/cpu/arm926ejs/lpc32xx/dram.c new file mode 100644 index 0000000..1eea8e2 --- /dev/null +++ b/arch/arm/cpu/arm926ejs/lpc32xx/dram.c @@ -0,0 +1,77 @@ +/* + * LPC32xx dram init + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * 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 +#include +#include +#include +#include +#include +#include + +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 index 0000000..4b8053e --- /dev/null +++ b/arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S @@ -0,0 +1,45 @@ +/* + * WORK Microwave work_92105 board low level init + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * 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/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h index 2cb5703..9449869 100644 --- a/arch/arm/include/asm/arch-lpc32xx/clk.h +++ b/arch/arm/include/asm/arch-lpc32xx/clk.h @@ -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) @@ -158,11 +159,15 @@ struct clk_pm_regs { /* 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 */ diff --git a/arch/arm/include/asm/arch-lpc32xx/cpu.h b/arch/arm/include/asm/arch-lpc32xx/cpu.h index 1067107..0b5dca1 100644 --- a/arch/arm/include/asm/arch-lpc32xx/cpu.h +++ b/arch/arm/include/asm/arch-lpc32xx/cpu.h @@ -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 */ diff --git a/arch/arm/include/asm/arch-lpc32xx/emc.h b/arch/arm/include/asm/arch-lpc32xx/emc.h index 82d9bcc..1a2bab2 100644 --- a/arch/arm/include/asm/arch-lpc32xx/emc.h +++ b/arch/arm/include/asm/arch-lpc32xx/emc.h @@ -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/mux.h b/arch/arm/include/asm/arch-lpc32xx/mux.h new file mode 100644 index 0000000..dc1b5bc --- /dev/null +++ b/arch/arm/include/asm/arch-lpc32xx/mux.h @@ -0,0 +1,18 @@ +/* + * LPC32xx MUX interface + * + * (C) Copyright 2015 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * 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; +}; diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index 86d5ee9..c3d890d 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -7,10 +7,14 @@ #ifndef _LPC32XX_SYS_PROTO_H #define _LPC32XX_SYS_PROTO_H +#include + 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 index 0000000..74f004f --- /dev/null +++ b/board/work-microwave/work_92105/Kconfig @@ -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 index 0000000..29a92c5 --- /dev/null +++ b/board/work-microwave/work_92105/MAINTAINERS @@ -0,0 +1,6 @@ +WORK_92105 BOARD +M: Albert ARIBAUD +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 index 0000000..ba31c8e --- /dev/null +++ b/board/work-microwave/work_92105/Makefile @@ -0,0 +1,10 @@ +# +# (C) Copyright 2014 DENX Software Engineering GmbH +# Written-by: Albert ARIBAUD +# +# 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 index 0000000..3c256e0 --- /dev/null +++ b/board/work-microwave/work_92105/README @@ -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 index 0000000..f782284 --- /dev/null +++ b/board/work-microwave/work_92105/work_92105.c @@ -0,0 +1,77 @@ +/* + * WORK Microwave work_92105 board support + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..c8b1013 --- /dev/null +++ b/board/work-microwave/work_92105/work_92105_display.c @@ -0,0 +1,349 @@ +/* + * work_92105 display support + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 - send command to driver;\n" + "\thd44780 data - send data to driver;\n" + "\thd44780 str \"\" - send \"\" 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 index 0000000..dd6e768 --- /dev/null +++ b/board/work-microwave/work_92105/work_92105_display.h @@ -0,0 +1,14 @@ +/* + * work_92105 display support interface + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * 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 index 0000000..282a6dd --- /dev/null +++ b/board/work-microwave/work_92105/work_92105_spl.c @@ -0,0 +1,85 @@ +/* + * WORK Microwave work_92105 board support + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/configs/work_92105_defconfig b/configs/work_92105_defconfig new file mode 100644 index 0000000..142a505 --- /dev/null +++ b/configs/work_92105_defconfig @@ -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="" diff --git a/include/configs/work_92105.h b/include/configs/work_92105.h new file mode 100644 index 0000000..dc8e99f --- /dev/null +++ b/include/configs/work_92105.h @@ -0,0 +1,241 @@ +/* + * WORK Microwave work_92105 board configuration file + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_WORK_92105_H__ +#define __CONFIG_WORK_92105_H__ + +/* SoC and board defines */ +#include +#include + +/* + * 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 + +/* + * 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 + +#endif /* __CONFIG_WORK_92105_H__*/ -- cgit v1.1