mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-18 13:11:31 +00:00
Merge branch 'master' of git://git.denx.de/u-boot-arm
This commit is contained in:
commit
59064346dd
57 changed files with 3653 additions and 77 deletions
20
Makefile
20
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
|
||||
|
|
10
README
10
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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
# Make ARMv5 to allow more compilers to work, even though its v6.
|
||||
PLATFORM_CPPFLAGS += -march=armv5
|
|
@ -1,9 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
# Make ARMv5 to allow more compilers to work, even though its v6.
|
||||
PLATFORM_CPPFLAGS += -march=armv5t
|
|
@ -1,9 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
# Marius Groeger <mgroeger@sysgo.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
PLATFORM_CPPFLAGS += -march=armv4 -mtune=arm7tdmi
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
PLATFORM_CPPFLAGS += -march=armv4
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
PLATFORM_CPPFLAGS += -march=armv5te
|
|
@ -6,3 +6,5 @@
|
|||
#
|
||||
|
||||
obj-y = cpu.o clk.o devices.o timer.o
|
||||
|
||||
obj-$(CONFIG_SPL_BUILD) += dram.o lowlevel_init.o
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <netdev.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/wdt.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
|
||||
|
@ -55,3 +57,11 @@ int print_cpuinfo(void)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LPC32XX_ETH
|
||||
int cpu_eth_init(bd_t *bis)
|
||||
{
|
||||
lpc32xx_eth_initialize(bis);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/uart.h>
|
||||
#include <asm/arch/mux.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
|
||||
static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
|
||||
static struct uart_ctrl_regs *ctrl = (struct uart_ctrl_regs *)UART_CTRL_BASE;
|
||||
static struct mux_regs *mux = (struct mux_regs *)MUX_BASE;
|
||||
|
||||
void lpc32xx_uart_init(unsigned int uart_id)
|
||||
{
|
||||
|
@ -37,3 +40,43 @@ void lpc32xx_uart_init(unsigned int uart_id)
|
|||
writel(CLK_UART_X_DIV(1) | CLK_UART_Y_DIV(1),
|
||||
&clk->u3clk + (uart_id - 3));
|
||||
}
|
||||
|
||||
void lpc32xx_mac_init(void)
|
||||
{
|
||||
/* Enable MAC interface */
|
||||
writel(CLK_MAC_REG | CLK_MAC_SLAVE | CLK_MAC_MASTER
|
||||
| CLK_MAC_MII, &clk->macclk_ctrl);
|
||||
}
|
||||
|
||||
void lpc32xx_mlc_nand_init(void)
|
||||
{
|
||||
/* Enable NAND interface */
|
||||
writel(CLK_NAND_MLC | CLK_NAND_MLC_INT, &clk->flashclk_ctrl);
|
||||
}
|
||||
|
||||
void lpc32xx_i2c_init(unsigned int devnum)
|
||||
{
|
||||
/* Enable I2C interface */
|
||||
uint32_t ctrl = readl(&clk->i2cclk_ctrl);
|
||||
if (devnum == 1)
|
||||
ctrl |= CLK_I2C1_ENABLE;
|
||||
if (devnum == 2)
|
||||
ctrl |= CLK_I2C2_ENABLE;
|
||||
writel(ctrl, &clk->i2cclk_ctrl);
|
||||
}
|
||||
|
||||
U_BOOT_DEVICE(lpc32xx_gpios) = {
|
||||
.name = "gpio_lpc32xx"
|
||||
};
|
||||
|
||||
/* Mux for SCK0, MISO0, MOSI0. We do not use SSEL0. */
|
||||
|
||||
#define P_MUX_SET_SSP0 0x1600
|
||||
|
||||
void lpc32xx_ssp_init(void)
|
||||
{
|
||||
/* Enable SSP0 interface */
|
||||
writel(CLK_SSP0_ENABLE_CLOCK, &clk->ssp_ctrl);
|
||||
/* Mux SSP0 pins */
|
||||
writel(P_MUX_SET_SSP0, &mux->p_mux_set);
|
||||
}
|
||||
|
|
77
arch/arm/cpu/arm926ejs/lpc32xx/dram.c
Normal file
77
arch/arm/cpu/arm926ejs/lpc32xx/dram.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* LPC32xx dram init
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* This is called by SPL to gain access to the SDR DRAM.
|
||||
*
|
||||
* This code runs from SRAM.
|
||||
*
|
||||
* Actual CONFIG_LPC32XX_SDRAM_* parameters must be provided
|
||||
* by the board configuration file.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <netdev.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/wdt.h>
|
||||
#include <asm/arch/emc.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
|
||||
static struct emc_regs *emc = (struct emc_regs *)EMC_BASE;
|
||||
|
||||
void ddr_init(struct emc_dram_settings *dram)
|
||||
{
|
||||
uint32_t ck;
|
||||
|
||||
/* Enable EMC interface and choose little endian mode */
|
||||
writel(1, &emc->ctrl);
|
||||
writel(0, &emc->config);
|
||||
/* Select maximum EMC Dynamic Memory Refresh Time */
|
||||
writel(0x7FF, &emc->refresh);
|
||||
/* Determine CLK */
|
||||
ck = get_sdram_clk_rate();
|
||||
/* Configure SDRAM */
|
||||
writel(dram->cmddelay, &clk->sdramclk_ctrl);
|
||||
writel(dram->config0, &emc->config0);
|
||||
writel(dram->rascas0, &emc->rascas0);
|
||||
writel(dram->rdconfig, &emc->read_config);
|
||||
/* Set timings */
|
||||
writel((ck / dram->trp) & 0x0000000F, &emc->t_rp);
|
||||
writel((ck / dram->tras) & 0x0000000F, &emc->t_ras);
|
||||
writel((ck / dram->tsrex) & 0x0000007F, &emc->t_srex);
|
||||
writel((ck / dram->twr) & 0x0000000F, &emc->t_wr);
|
||||
writel((ck / dram->trc) & 0x0000001F, &emc->t_rc);
|
||||
writel((ck / dram->trfc) & 0x0000001F, &emc->t_rfc);
|
||||
writel((ck / dram->txsr) & 0x000000FF, &emc->t_xsr);
|
||||
writel(dram->trrd, &emc->t_rrd);
|
||||
writel(dram->tmrd, &emc->t_mrd);
|
||||
writel(dram->tcdlr, &emc->t_cdlr);
|
||||
/* Dynamic refresh */
|
||||
writel((((ck / dram->refresh) >> 4) & 0x7FF), &emc->refresh);
|
||||
udelay(10);
|
||||
/* Force all clocks, enable inverted ck, issue NOP command */
|
||||
writel(0x00000193, &emc->control);
|
||||
udelay(100);
|
||||
/* Keep all clocks enabled, issue a PRECHARGE ALL command */
|
||||
writel(0x00000113, &emc->control);
|
||||
/* Fast dynamic refresh for at least a few SDRAM ck cycles */
|
||||
writel((((128) >> 4) & 0x7FF), &emc->refresh);
|
||||
udelay(10);
|
||||
/* set correct dynamic refresh timing */
|
||||
writel((((ck / dram->refresh) >> 4) & 0x7FF), &emc->refresh);
|
||||
udelay(10);
|
||||
/* set normal mode to CAS=3 */
|
||||
writel(0x00000093, &emc->control);
|
||||
readl(EMC_DYCS0_BASE | dram->mode);
|
||||
/* set extended mode to all zeroes */
|
||||
writel(0x00000093, &emc->control);
|
||||
readl(EMC_DYCS0_BASE | dram->emode);
|
||||
/* stop forcing clocks, keep inverted clock, issue normal mode */
|
||||
writel(0x00000010, &emc->control);
|
||||
}
|
45
arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S
Normal file
45
arch/arm/cpu/arm926ejs/lpc32xx/lowlevel_init.S
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* WORK Microwave work_92105 board low level init
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* Low level init is called from SPL to set up the clocks.
|
||||
* On entry, the LPC3250 is in Direct Run mode with all clocks
|
||||
* running at 13 MHz; on exit, ARM clock is 208 MHz, HCLK is
|
||||
* 104 MHz and PCLK is 13 MHz.
|
||||
*
|
||||
* This code must run from SRAM so that the clock changes do
|
||||
* not prevent it from executing.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
.globl lowlevel_init
|
||||
|
||||
lowlevel_init:
|
||||
|
||||
/* Set ARM, HCLK, PCLK dividers for normal mode */
|
||||
ldr r0, =0x0000003D
|
||||
ldr r1, =0x40004040
|
||||
str r0, [r1]
|
||||
|
||||
/* Start HCLK PLL for 208 MHz */
|
||||
ldr r0, =0x0001401E
|
||||
ldr r1, =0x40004058
|
||||
str r0, [r1]
|
||||
|
||||
/* wait for HCLK PLL to lock */
|
||||
1:
|
||||
ldr r0, [r1]
|
||||
ands r0, r0, #1
|
||||
beq 1b
|
||||
|
||||
/* switch to normal mode */
|
||||
ldr r1, =0x40004044
|
||||
ldr r0, [r1]
|
||||
orr r0, #0x00000004
|
||||
str r0, [r1]
|
||||
|
||||
/* Return to U-boot via saved link register */
|
||||
mov pc, lr
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
PLATFORM_CPPFLAGS += -march=armv4
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#
|
||||
# (C) Copyright 2002
|
||||
# Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
# Marius Groeger <mgroeger@sysgo.de>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
PLATFORM_CPPFLAGS += -march=armv4 -mtune=strongarm1100
|
|
@ -71,6 +71,7 @@ struct clk_pm_regs {
|
|||
};
|
||||
|
||||
/* HCLK Divider Control Register bits */
|
||||
#define CLK_HCLK_DDRAM_MASK (0x3 << 7)
|
||||
#define CLK_HCLK_DDRAM_HALF (0x2 << 7)
|
||||
#define CLK_HCLK_DDRAM_NOMINAL (0x1 << 7)
|
||||
#define CLK_HCLK_DDRAM_STOPPED (0x0 << 7)
|
||||
|
@ -123,6 +124,10 @@ struct clk_pm_regs {
|
|||
#define CLK_MAC_SLAVE (1 << 1)
|
||||
#define CLK_MAC_REG (1 << 0)
|
||||
|
||||
/* I2C Clock Control Register bits */
|
||||
#define CLK_I2C2_ENABLE (1 << 1)
|
||||
#define CLK_I2C1_ENABLE (1 << 0)
|
||||
|
||||
/* Timer Clock Control1 Register bits */
|
||||
#define CLK_TIMCLK_MOTOR (1 << 6)
|
||||
#define CLK_TIMCLK_TIMER3 (1 << 5)
|
||||
|
@ -147,11 +152,22 @@ struct clk_pm_regs {
|
|||
/* DMA Clock Control Register bits */
|
||||
#define CLK_DMA_ENABLE (1 << 0)
|
||||
|
||||
/* NAND Clock Control Register bits */
|
||||
#define CLK_NAND_MLC (1 << 1)
|
||||
#define CLK_NAND_MLC_INT (1 << 5)
|
||||
|
||||
/* SSP Clock Control Register bits */
|
||||
#define CLK_SSP0_ENABLE_CLOCK (1 << 0)
|
||||
|
||||
/* SDRAMCLK register bits */
|
||||
#define CLK_SDRAM_DDR_SEL (1 << 1)
|
||||
|
||||
unsigned int get_sys_clk_rate(void);
|
||||
unsigned int get_hclk_pll_rate(void);
|
||||
unsigned int get_hclk_clk_div(void);
|
||||
unsigned int get_hclk_clk_rate(void);
|
||||
unsigned int get_periph_clk_div(void);
|
||||
unsigned int get_periph_clk_rate(void);
|
||||
unsigned int get_sdram_clk_rate(void);
|
||||
|
||||
#endif /* _LPC32XX_CLK_H */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#define HS_UART7_BASE 0x4001C000 /* High speed UART 7 registers base */
|
||||
#define RTC_BASE 0x40024000 /* RTC registers base */
|
||||
#define GPIO_BASE 0x40028000 /* GPIO registers base */
|
||||
#define MUX_BASE 0x40028100 /* MUX registers base */
|
||||
#define WDT_BASE 0x4003C000 /* Watchdog timer registers base */
|
||||
#define TIMER0_BASE 0x40044000 /* Timer0 registers base */
|
||||
#define TIMER1_BASE 0x4004C000 /* Timer1 registers base */
|
||||
|
@ -37,6 +38,8 @@
|
|||
#define UART4_BASE 0x40088000 /* UART 4 registers base */
|
||||
#define UART5_BASE 0x40090000 /* UART 5 registers base */
|
||||
#define UART6_BASE 0x40098000 /* UART 6 registers base */
|
||||
#define I2C1_BASE 0x400A0000 /* I2C 1 registers base */
|
||||
#define I2C2_BASE 0x400A8000 /* I2C 2 registers base */
|
||||
|
||||
/* External SDRAM Memory Bank base addresses */
|
||||
#define EMC_DYCS0_BASE 0x80000000 /* SDRAM DYCS0 base address */
|
||||
|
|
|
@ -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 */
|
||||
|
|
43
arch/arm/include/asm/arch-lpc32xx/gpio.h
Normal file
43
arch/arm/include/asm/arch-lpc32xx/gpio.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* LPC32xx GPIO interface
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/**
|
||||
* GPIO Register map for LPC32xx
|
||||
*/
|
||||
|
||||
struct gpio_regs {
|
||||
u32 p3_inp_state;
|
||||
u32 p3_outp_set;
|
||||
u32 p3_outp_clr;
|
||||
u32 p3_outp_state;
|
||||
/* Watch out! the following are shared between p2 and p3 */
|
||||
u32 p2_p3_dir_set;
|
||||
u32 p2_p3_dir_clr;
|
||||
u32 p2_p3_dir_state;
|
||||
/* Now back to 'one register for one port' */
|
||||
u32 p2_inp_state;
|
||||
u32 p2_outp_set;
|
||||
u32 p2_outp_clr;
|
||||
u32 reserved1[6];
|
||||
u32 p0_inp_state;
|
||||
u32 p0_outp_set;
|
||||
u32 p0_outp_clr;
|
||||
u32 p0_outp_state;
|
||||
u32 p0_dir_set;
|
||||
u32 p0_dir_clr;
|
||||
u32 p0_dir_state;
|
||||
u32 reserved2;
|
||||
u32 p1_inp_state;
|
||||
u32 p1_outp_set;
|
||||
u32 p1_outp_clr;
|
||||
u32 p1_outp_state;
|
||||
u32 p1_dir_set;
|
||||
u32 p1_dir_clr;
|
||||
u32 p1_dir_state;
|
||||
};
|
18
arch/arm/include/asm/arch-lpc32xx/mux.h
Normal file
18
arch/arm/include/asm/arch-lpc32xx/mux.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* LPC32xx MUX interface
|
||||
*
|
||||
* (C) Copyright 2015 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/**
|
||||
* MUX register map for LPC32xx
|
||||
*/
|
||||
|
||||
struct mux_regs {
|
||||
u32 p_mux_set;
|
||||
u32 p_mux_clr;
|
||||
u32 p_mux_state;
|
||||
};
|
|
@ -7,6 +7,14 @@
|
|||
#ifndef _LPC32XX_SYS_PROTO_H
|
||||
#define _LPC32XX_SYS_PROTO_H
|
||||
|
||||
void lpc32xx_uart_init(unsigned int uart_id);
|
||||
#include <asm/arch/emc.h>
|
||||
|
||||
void lpc32xx_uart_init(unsigned int uart_id);
|
||||
void lpc32xx_mac_init(void);
|
||||
void lpc32xx_mlc_nand_init(void);
|
||||
void lpc32xx_i2c_init(unsigned int devnum);
|
||||
void lpc32xx_ssp_init(void);
|
||||
#if defined(CONFIG_SPL_BUILD)
|
||||
void ddr_init(const struct emc_dram_settings *dram);
|
||||
#endif
|
||||
#endif /* _LPC32XX_SYS_PROTO_H */
|
||||
|
|
15
board/work-microwave/work_92105/Kconfig
Normal file
15
board/work-microwave/work_92105/Kconfig
Normal file
|
@ -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
|
6
board/work-microwave/work_92105/MAINTAINERS
Normal file
6
board/work-microwave/work_92105/MAINTAINERS
Normal file
|
@ -0,0 +1,6 @@
|
|||
WORK_92105 BOARD
|
||||
M: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
S: Maintained
|
||||
F: board/work-microwave/work_92105/
|
||||
F: include/configs/work_92105.h
|
||||
F: configs/work_92105_defconfig
|
10
board/work-microwave/work_92105/Makefile
Normal file
10
board/work-microwave/work_92105/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
# Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y := work_92105.o work_92105_display.o
|
||||
|
||||
obj-$(CONFIG_SPL_BUILD) += work_92105_spl.o
|
91
board/work-microwave/work_92105/README
Normal file
91
board/work-microwave/work_92105/README
Normal file
|
@ -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
|
77
board/work-microwave/work_92105/work_92105.c
Normal file
77
board/work-microwave/work_92105/work_92105.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* WORK Microwave work_92105 board support
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/emc.h>
|
||||
#include <asm/arch/wdt.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <spl.h>
|
||||
#include "work_92105_display.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
|
||||
static struct wdt_regs *wdt = (struct wdt_regs *)WDT_BASE;
|
||||
|
||||
void reset_periph(void)
|
||||
{
|
||||
setbits_le32(&clk->timclk_ctrl, CLK_TIMCLK_WATCHDOG);
|
||||
writel(WDTIM_MCTRL_RESFRC1, &wdt->mctrl);
|
||||
udelay(150);
|
||||
writel(0, &wdt->mctrl);
|
||||
clrbits_le32(&clk->timclk_ctrl, CLK_TIMCLK_WATCHDOG);
|
||||
}
|
||||
|
||||
int board_early_init_f(void)
|
||||
{
|
||||
/* initialize serial port for console */
|
||||
lpc32xx_uart_init(CONFIG_SYS_LPC32XX_UART);
|
||||
/* enable I2C, SSP, MAC, NAND */
|
||||
lpc32xx_i2c_init(1); /* only I2C1 has devices, I2C2 has none */
|
||||
lpc32xx_ssp_init();
|
||||
lpc32xx_mac_init();
|
||||
lpc32xx_mlc_nand_init();
|
||||
/* Display must wait until after relocation and devices init */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GPO_19 115
|
||||
|
||||
int board_early_init_r(void)
|
||||
{
|
||||
/* Set NAND !WP to 1 through GPO_19 */
|
||||
gpio_request(GPO_19, "NAND_nWP");
|
||||
gpio_direction_output(GPO_19, 1);
|
||||
|
||||
/* initialize display */
|
||||
work_92105_display_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
reset_periph();
|
||||
/* adress of boot parameters */
|
||||
gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dram_init(void)
|
||||
{
|
||||
gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE,
|
||||
CONFIG_SYS_SDRAM_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
349
board/work-microwave/work_92105/work_92105_display.c
Normal file
349
board/work-microwave/work_92105/work_92105_display.c
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* work_92105 display support
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* The work_92105 display is a HD44780-compatible module
|
||||
* controlled through a MAX6957AAX SPI port expander, two
|
||||
* MAX518 I2C DACs and native LPC32xx GPO 15.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/emc.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <spi.h>
|
||||
#include <i2c.h>
|
||||
#include <version.h>
|
||||
#include <vsprintf.h>
|
||||
|
||||
/*
|
||||
* GPO 15 in port 3 is gpio 3*32+15 = 111
|
||||
*/
|
||||
|
||||
#define GPO_15 111
|
||||
|
||||
/**
|
||||
* MAX6957AAX registers that we will be using
|
||||
*/
|
||||
|
||||
#define MAX6957_CONF 0x04
|
||||
|
||||
#define MAX6957_CONF_08_11 0x0A
|
||||
#define MAX6957_CONF_12_15 0x0B
|
||||
#define MAX6957_CONF_16_19 0x0C
|
||||
|
||||
/**
|
||||
* Individual gpio ports (one per gpio) to HD44780
|
||||
*/
|
||||
|
||||
#define MAX6957AAX_HD44780_RS 0x29
|
||||
#define MAX6957AAX_HD44780_R_W 0x2A
|
||||
#define MAX6957AAX_HD44780_EN 0x2B
|
||||
#define MAX6957AAX_HD44780_DATA 0x4C
|
||||
|
||||
/**
|
||||
* Display controller instructions
|
||||
*/
|
||||
|
||||
/* Function set: eight bits, two lines, 8-dot font */
|
||||
#define HD44780_FUNCTION_SET 0x38
|
||||
|
||||
/* Display ON / OFF: turn display on */
|
||||
#define HD44780_DISPLAY_ON_OFF_CONTROL 0x0C
|
||||
|
||||
/* Entry mode: increment */
|
||||
#define HD44780_ENTRY_MODE_SET 0x06
|
||||
|
||||
/* Clear */
|
||||
#define HD44780_CLEAR_DISPLAY 0x01
|
||||
|
||||
/* Set DDRAM addr (to be ORed with exact address) */
|
||||
#define HD44780_SET_DDRAM_ADDR 0x80
|
||||
|
||||
/* Set CGRAM addr (to be ORed with exact address) */
|
||||
#define HD44780_SET_CGRAM_ADDR 0x40
|
||||
|
||||
/**
|
||||
* Default value for contrats
|
||||
*/
|
||||
|
||||
#define CONTRAST_DEFAULT 25
|
||||
|
||||
/**
|
||||
* Define slave as a module-wide local to save passing it around,
|
||||
* plus we will need it after init for the "hd44780" command.
|
||||
*/
|
||||
|
||||
static struct spi_slave *slave;
|
||||
|
||||
/*
|
||||
* Write a value into a MAX6957AAX register.
|
||||
*/
|
||||
|
||||
static void max6957aax_write(uint8_t reg, uint8_t value)
|
||||
{
|
||||
uint8_t dout[2];
|
||||
|
||||
dout[0] = reg;
|
||||
dout[1] = value;
|
||||
gpio_set_value(GPO_15, 0);
|
||||
/* do SPI read/write (passing din==dout is OK) */
|
||||
spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
gpio_set_value(GPO_15, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a value from a MAX6957AAX register.
|
||||
*
|
||||
* According to the MAX6957AAX datasheet, we should release the chip
|
||||
* select halfway through the read sequence, when the actual register
|
||||
* value is read; but the WORK_92105 hardware prevents the MAX6957AAX
|
||||
* SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
|
||||
* so let's release the CS an hold it again while reading the result.
|
||||
*/
|
||||
|
||||
static uint8_t max6957aax_read(uint8_t reg)
|
||||
{
|
||||
uint8_t dout[2], din[2];
|
||||
|
||||
/* send read command */
|
||||
dout[0] = reg | 0x80; /* set bit 7 to indicate read */
|
||||
dout[1] = 0;
|
||||
gpio_set_value(GPO_15, 0);
|
||||
/* do SPI read/write (passing din==dout is OK) */
|
||||
spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
/* latch read command */
|
||||
gpio_set_value(GPO_15, 1);
|
||||
/* read register -- din = noop on xmit, din[1] = reg on recv */
|
||||
din[0] = 0;
|
||||
din[1] = 0;
|
||||
gpio_set_value(GPO_15, 0);
|
||||
/* do SPI read/write (passing din==dout is OK) */
|
||||
spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
/* end of read. */
|
||||
gpio_set_value(GPO_15, 1);
|
||||
return din[1];
|
||||
}
|
||||
|
||||
static void hd44780_instruction(unsigned long instruction)
|
||||
{
|
||||
max6957aax_write(MAX6957AAX_HD44780_RS, 0);
|
||||
max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 1);
|
||||
max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
||||
/* HD44780 takes 37 us for most instructions, 1520 for clear */
|
||||
if (instruction == HD44780_CLEAR_DISPLAY)
|
||||
udelay(2000);
|
||||
else
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
static void hd44780_write_char(char c)
|
||||
{
|
||||
max6957aax_write(MAX6957AAX_HD44780_RS, 1);
|
||||
max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 1);
|
||||
max6957aax_write(MAX6957AAX_HD44780_DATA, c);
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
||||
/* HD44780 takes 37 us to write to DDRAM or CGRAM */
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
static void hd44780_write_str(char *s)
|
||||
{
|
||||
max6957aax_write(MAX6957AAX_HD44780_RS, 1);
|
||||
max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
|
||||
while (*s) {
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 1);
|
||||
max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
||||
s++;
|
||||
/* HD44780 takes 37 us to write to DDRAM or CGRAM */
|
||||
udelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Existing user code might expect these custom characters to be
|
||||
* recognized and displayed on the LCD
|
||||
*/
|
||||
|
||||
static u8 char_gen_chars[] = {
|
||||
/* #8, empty rectangle */
|
||||
0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
|
||||
/* #9, filled right arrow */
|
||||
0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
|
||||
/* #10, filled left arrow */
|
||||
0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
|
||||
/* #11, up and down arrow */
|
||||
0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
|
||||
/* #12, plus/minus */
|
||||
0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
|
||||
/* #13, fat exclamation mark */
|
||||
0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
|
||||
/* #14, empty square */
|
||||
0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
|
||||
/* #15, struck out square */
|
||||
0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static void hd44780_init_char_gen(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
hd44780_instruction(HD44780_SET_CGRAM_ADDR);
|
||||
|
||||
for (i = 0; i < sizeof(char_gen_chars); i++)
|
||||
hd44780_write_char(char_gen_chars[i]);
|
||||
|
||||
hd44780_instruction(HD44780_SET_DDRAM_ADDR);
|
||||
}
|
||||
|
||||
void work_92105_display_init(void)
|
||||
{
|
||||
int claim_err;
|
||||
char *display_contrast_str;
|
||||
uint8_t display_contrast = CONTRAST_DEFAULT;
|
||||
uint8_t enable_backlight = 0x96;
|
||||
|
||||
slave = spi_setup_slave(0, 0, 500000, 0);
|
||||
|
||||
if (!slave) {
|
||||
printf("Failed to set up SPI slave\n");
|
||||
return;
|
||||
}
|
||||
|
||||
claim_err = spi_claim_bus(slave);
|
||||
|
||||
if (claim_err)
|
||||
debug("Failed to claim SPI bus: %d\n", claim_err);
|
||||
|
||||
/* enable backlight */
|
||||
i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);
|
||||
|
||||
/* set display contrast */
|
||||
display_contrast_str = getenv("fwopt_dispcontrast");
|
||||
if (display_contrast_str)
|
||||
display_contrast = simple_strtoul(display_contrast_str,
|
||||
NULL, 10);
|
||||
i2c_write(0x2c, 0x00, 1, &display_contrast, 1);
|
||||
|
||||
/* request GPO_15 as an output initially set to 1 */
|
||||
gpio_request(GPO_15, "MAX6957_nCS");
|
||||
gpio_direction_output(GPO_15, 1);
|
||||
|
||||
/* enable MAX6957 portexpander */
|
||||
max6957aax_write(MAX6957_CONF, 0x01);
|
||||
/* configure pin 8 as input, pins 9..19 as outputs */
|
||||
max6957aax_write(MAX6957_CONF_08_11, 0x56);
|
||||
max6957aax_write(MAX6957_CONF_12_15, 0x55);
|
||||
max6957aax_write(MAX6957_CONF_16_19, 0x55);
|
||||
|
||||
/* initialize HD44780 */
|
||||
max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
||||
hd44780_instruction(HD44780_FUNCTION_SET);
|
||||
hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL);
|
||||
hd44780_instruction(HD44780_ENTRY_MODE_SET);
|
||||
|
||||
/* write custom character glyphs */
|
||||
hd44780_init_char_gen();
|
||||
|
||||
/* Show U-Boot version, date and time as a sign-of-life */
|
||||
hd44780_instruction(HD44780_CLEAR_DISPLAY);
|
||||
hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0);
|
||||
hd44780_write_str(U_BOOT_VERSION);
|
||||
hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64);
|
||||
hd44780_write_str(U_BOOT_DATE);
|
||||
hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20);
|
||||
hd44780_write_str(U_BOOT_TIME);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_MAX6957
|
||||
|
||||
static int do_max6957aax(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
int reg, val;
|
||||
|
||||
if (argc != 3)
|
||||
return CMD_RET_USAGE;
|
||||
switch (argv[1][0]) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
reg = simple_strtoul(argv[2], NULL, 0);
|
||||
val = max6957aax_read(reg);
|
||||
printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val);
|
||||
return 0;
|
||||
default:
|
||||
reg = simple_strtoul(argv[1], NULL, 0);
|
||||
val = simple_strtoul(argv[2], NULL, 0);
|
||||
max6957aax_write(reg, val);
|
||||
printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_LONGHELP
|
||||
static char max6957aax_help_text[] =
|
||||
"max6957aax - write or read display register:\n"
|
||||
"\tmax6957aax R|r reg - read display register;\n"
|
||||
"\tmax6957aax reg val - write display register.";
|
||||
#endif
|
||||
|
||||
U_BOOT_CMD(
|
||||
max6957aax, 6, 1, do_max6957aax,
|
||||
"SPI MAX6957 display write/read",
|
||||
max6957aax_help_text
|
||||
);
|
||||
#endif /* CONFIG_CMD_MAX6957 */
|
||||
|
||||
#ifdef CONFIG_CMD_HD44760
|
||||
|
||||
/*
|
||||
* We need the HUSH parser because we need string arguments, and
|
||||
* only HUSH can understand them.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_SYS_HUSH_PARSER)
|
||||
#error CONFIG_CMD_HD44760 requires CONFIG_SYS_HUSH_PARSER
|
||||
#endif
|
||||
|
||||
static int do_hd44780(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
if (argc != 3)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
cmd = argv[1];
|
||||
|
||||
if (strcasecmp(cmd, "cmd") == 0)
|
||||
hd44780_instruction(simple_strtol(argv[2], NULL, 0));
|
||||
else if (strcasecmp(cmd, "data") == 0)
|
||||
hd44780_write_char(simple_strtol(argv[2], NULL, 0));
|
||||
else if (strcasecmp(cmd, "str") == 0)
|
||||
hd44780_write_str(argv[2]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_LONGHELP
|
||||
static char hd44780_help_text[] =
|
||||
"hd44780 - control LCD driver:\n"
|
||||
"\thd44780 cmd <val> - send command <val> to driver;\n"
|
||||
"\thd44780 data <val> - send data <val> to driver;\n"
|
||||
"\thd44780 str \"<text>\" - send \"<text>\" to driver.";
|
||||
#endif
|
||||
|
||||
U_BOOT_CMD(
|
||||
hd44780, 6, 1, do_hd44780,
|
||||
"HD44780 LCD driver control",
|
||||
hd44780_help_text
|
||||
);
|
||||
#endif /* CONFIG_CMD_HD44780 */
|
14
board/work-microwave/work_92105/work_92105_display.h
Normal file
14
board/work-microwave/work_92105/work_92105_display.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* work_92105 display support interface
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* The work_92105 display is a HD44780-compatible module
|
||||
* controlled through a MAX6957AAX SPI port expander, two
|
||||
* MAX518 I2C DACs and native LPC32xx GPO 15.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
void work_92105_display_init(void);
|
85
board/work-microwave/work_92105/work_92105_spl.c
Normal file
85
board/work-microwave/work_92105/work_92105_spl.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* WORK Microwave work_92105 board support
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/emc.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <spl.h>
|
||||
#include "work_92105_display.h"
|
||||
|
||||
struct emc_dram_settings dram_64mb = {
|
||||
.cmddelay = 0x0001C000,
|
||||
.config0 = 0x00005682,
|
||||
.rascas0 = 0x00000302,
|
||||
.rdconfig = 0x00000011,
|
||||
.trp = 52631578,
|
||||
.tras = 20833333,
|
||||
.tsrex = 12500000,
|
||||
.twr = 66666666,
|
||||
.trc = 13888888,
|
||||
.trfc = 10256410,
|
||||
.txsr = 12500000,
|
||||
.trrd = 1,
|
||||
.tmrd = 1,
|
||||
.tcdlr = 0,
|
||||
.refresh = 128000,
|
||||
.mode = 0x00018000,
|
||||
.emode = 0x02000000
|
||||
};
|
||||
|
||||
const struct emc_dram_settings dram_128mb = {
|
||||
.cmddelay = 0x0001C000,
|
||||
.config0 = 0x00005882,
|
||||
.rascas0 = 0x00000302,
|
||||
.rdconfig = 0x00000011,
|
||||
.trp = 52631578,
|
||||
.tras = 22222222,
|
||||
.tsrex = 8333333,
|
||||
.twr = 66666666,
|
||||
.trc = 14814814,
|
||||
.trfc = 10256410,
|
||||
.txsr = 8333333,
|
||||
.trrd = 1,
|
||||
.tmrd = 1,
|
||||
.tcdlr = 0,
|
||||
.refresh = 128000,
|
||||
.mode = 0x00030000,
|
||||
.emode = 0x02000000
|
||||
};
|
||||
|
||||
void spl_board_init(void)
|
||||
{
|
||||
/* initialize serial port for console */
|
||||
lpc32xx_uart_init(CONFIG_SYS_LPC32XX_UART);
|
||||
/* initialize console */
|
||||
preloader_console_init();
|
||||
/* init DDR and NAND to chainload U-Boot */
|
||||
ddr_init(&dram_128mb);
|
||||
/*
|
||||
* If this is actually a 64MB module, then the highest column
|
||||
* bit in any address will be ignored, and thus address 0x80000000
|
||||
* should be mirrored at address 0x80000800. Test this.
|
||||
*/
|
||||
writel(0x31415926, 0x80000000); /* write Pi at 0x80000000 */
|
||||
writel(0x16180339, 0x80000800); /* write Phi at 0x80000800 */
|
||||
if (readl(0x80000000) == 0x16180339) /* check 0x80000000 */ {
|
||||
/* actually 64MB mirrored: reconfigure controller */
|
||||
ddr_init(&dram_64mb);
|
||||
}
|
||||
/* initialize NAND controller to load U-Boot from NAND */
|
||||
lpc32xx_mlc_nand_init();
|
||||
}
|
||||
|
||||
u32 spl_boot_device(void)
|
||||
{
|
||||
return BOOT_DEVICE_NAND;
|
||||
}
|
|
@ -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, "", "", },
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
6
configs/work_92105_defconfig
Normal file
6
configs/work_92105_defconfig
Normal file
|
@ -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=""
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
293
drivers/gpio/lpc32xx_gpio.c
Normal file
293
drivers/gpio/lpc32xx_gpio.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* LPC32xxGPIO driver
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-lpc32xx/cpu.h>
|
||||
#include <asm/arch-lpc32xx/gpio.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dm.h>
|
||||
|
||||
/**
|
||||
* LPC32xx GPIOs work in banks but are non-homogeneous:
|
||||
* - each bank holds a different number of GPIOs
|
||||
* - some GPIOs are input/ouput, some input only, some output only;
|
||||
* - some GPIOs have different meanings as an input and as an output;
|
||||
* - some GPIOs are controlled on a given port and bit index, but
|
||||
* read on another one.
|
||||
*
|
||||
* In order to keep this code simple, GPIOS are considered here as
|
||||
* homogeneous and linear, from 0 to 127.
|
||||
*
|
||||
* ** WARNING #1 **
|
||||
*
|
||||
* Client code is responsible for properly using valid GPIO numbers,
|
||||
* including cases where a single physical GPIO has differing numbers
|
||||
* for setting its direction, reading it and/or writing to it.
|
||||
*
|
||||
* ** WARNING #2 **
|
||||
*
|
||||
* Please read NOTE in description of lpc32xx_gpio_get_function().
|
||||
*/
|
||||
|
||||
#define LPC32XX_GPIOS 128
|
||||
|
||||
struct lpc32xx_gpio_platdata {
|
||||
struct gpio_regs *regs;
|
||||
/* GPIO FUNCTION: SEE WARNING #2 */
|
||||
signed char function[LPC32XX_GPIOS];
|
||||
};
|
||||
|
||||
/**
|
||||
* We have 4 GPIO ports of 32 bits each
|
||||
*/
|
||||
|
||||
#define MAX_GPIO 128
|
||||
|
||||
#define GPIO_TO_PORT(gpio) ((gpio / 32) & 3)
|
||||
#define GPIO_TO_RANK(gpio) (gpio % 32)
|
||||
#define GPIO_TO_MASK(gpio) (1 << (gpio % 32))
|
||||
|
||||
/**
|
||||
* Configure a GPIO number 'offset' as input
|
||||
*/
|
||||
|
||||
static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
int port, mask;
|
||||
struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev);
|
||||
struct gpio_regs *regs = gpio_platdata->regs;
|
||||
|
||||
port = GPIO_TO_PORT(offset);
|
||||
mask = GPIO_TO_MASK(offset);
|
||||
|
||||
switch (port) {
|
||||
case 0:
|
||||
writel(mask, ®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),
|
||||
};
|
|
@ -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
|
||||
|
|
65
drivers/hwmon/ds620.c
Normal file
65
drivers/hwmon/ds620.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* DS620 DTT support
|
||||
*
|
||||
* (C) Copyright 2014 3ADEV <http://www.3adev.com>
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/*
|
||||
* Dallas Semiconductor's DS1621/1631 Digital Thermometer and Thermostat.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <dtt.h>
|
||||
|
||||
/*
|
||||
* Device code
|
||||
*/
|
||||
#define DTT_I2C_DEV_CODE 0x48
|
||||
#define DTT_START_CONVERT 0x51
|
||||
#define DTT_TEMP 0xAA
|
||||
#define DTT_CONFIG 0xAC
|
||||
|
||||
/*
|
||||
* Config register MSB bits
|
||||
*/
|
||||
#define DTT_CONFIG_1SHOT 0x01
|
||||
#define DTT_CONFIG_AUTOC 0x02
|
||||
#define DTT_CONFIG_R0 0x04 /* always 1 */
|
||||
#define DTT_CONFIG_R1 0x08 /* always 1 */
|
||||
#define DTT_CONFIG_TLF 0x10
|
||||
#define DTT_CONFIG_THF 0x20
|
||||
#define DTT_CONFIG_NVB 0x40
|
||||
#define DTT_CONFIG_DONE 0x80
|
||||
|
||||
#define CHIP(sensor) (DTT_I2C_DEV_CODE + (sensor & 0x07))
|
||||
|
||||
int dtt_init_one(int sensor)
|
||||
{
|
||||
uint8_t config = DTT_CONFIG_1SHOT
|
||||
| DTT_CONFIG_R0
|
||||
| DTT_CONFIG_R1;
|
||||
return i2c_write(CHIP(sensor), DTT_CONFIG, 1, &config, 1);
|
||||
}
|
||||
|
||||
int dtt_get_temp(int sensor)
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t temp[2];
|
||||
|
||||
/* Start a conversion, may take up to 1 second. */
|
||||
i2c_write(CHIP(sensor), DTT_START_CONVERT, 1, NULL, 0);
|
||||
do {
|
||||
if (i2c_read(CHIP(sensor), DTT_CONFIG, 1, &status, 1))
|
||||
/* bail out if I2C error */
|
||||
status |= DTT_CONFIG_DONE;
|
||||
} while (!(status & DTT_CONFIG_DONE));
|
||||
if (i2c_read(CHIP(sensor), DTT_TEMP, 1, temp, 2))
|
||||
/* bail out if I2C error */
|
||||
return -274; /* below absolute zero == error */
|
||||
|
||||
return ((int16_t)(temp[1] | (temp[0] << 8))) >> 7;
|
||||
}
|
|
@ -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
|
||||
|
|
249
drivers/i2c/lpc32xx_i2c.c
Normal file
249
drivers/i2c/lpc32xx_i2c.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* LPC32xx I2C interface driver
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
/*
|
||||
* Provide default speed and slave if target did not
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_LPC32XX_SPEED)
|
||||
#define CONFIG_SYS_I2C_LPC32XX_SPEED 350000
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_LPC32XX_SLAVE)
|
||||
#define CONFIG_SYS_I2C_LPC32XX_SLAVE 0
|
||||
#endif
|
||||
|
||||
/* i2c register set */
|
||||
struct lpc32xx_i2c_registers {
|
||||
union {
|
||||
u32 rx;
|
||||
u32 tx;
|
||||
};
|
||||
u32 stat;
|
||||
u32 ctrl;
|
||||
u32 clk_hi;
|
||||
u32 clk_lo;
|
||||
u32 adr;
|
||||
u32 rxfl;
|
||||
u32 txfl;
|
||||
u32 rxb;
|
||||
u32 txb;
|
||||
u32 stx;
|
||||
u32 stxfl;
|
||||
};
|
||||
|
||||
/* TX register fields */
|
||||
#define LPC32XX_I2C_TX_START 0x00000100
|
||||
#define LPC32XX_I2C_TX_STOP 0x00000200
|
||||
|
||||
/* Control register values */
|
||||
#define LPC32XX_I2C_SOFT_RESET 0x00000100
|
||||
|
||||
/* Status register values */
|
||||
#define LPC32XX_I2C_STAT_TFF 0x00000400
|
||||
#define LPC32XX_I2C_STAT_RFE 0x00000200
|
||||
#define LPC32XX_I2C_STAT_DRMI 0x00000008
|
||||
#define LPC32XX_I2C_STAT_NAI 0x00000004
|
||||
#define LPC32XX_I2C_STAT_TDI 0x00000001
|
||||
|
||||
static struct lpc32xx_i2c_registers *lpc32xx_i2c[] = {
|
||||
(struct lpc32xx_i2c_registers *)I2C1_BASE,
|
||||
(struct lpc32xx_i2c_registers *)I2C2_BASE
|
||||
};
|
||||
|
||||
/* Set I2C bus speed */
|
||||
static unsigned int lpc32xx_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
int half_period;
|
||||
|
||||
if (speed == 0)
|
||||
return -EINVAL;
|
||||
|
||||
half_period = (105000000 / speed) / 2;
|
||||
|
||||
if ((half_period > 255) || (half_period < 0))
|
||||
return -EINVAL;
|
||||
|
||||
writel(half_period, &lpc32xx_i2c[adap->hwadapnr]->clk_hi);
|
||||
writel(half_period, &lpc32xx_i2c[adap->hwadapnr]->clk_lo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C init called by cmd_i2c when doing 'i2c reset'. */
|
||||
static void _i2c_init(struct i2c_adapter *adap,
|
||||
int requested_speed, int slaveadd)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
|
||||
/* soft reset (auto-clears) */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
/* set HI and LO periods for about 350 kHz */
|
||||
lpc32xx_i2c_set_bus_speed(adap, requested_speed);
|
||||
}
|
||||
|
||||
/* I2C probe called by cmd_i2c when doing 'i2c probe'. */
|
||||
static int lpc32xx_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
int stat;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
|
||||
;
|
||||
/* Addre slave for write with start before and stop after */
|
||||
writel((dev<<1) | LPC32XX_I2C_TX_START | LPC32XX_I2C_TX_STOP,
|
||||
&i2c->tx);
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* was there no acknowledge? */
|
||||
return (stat & LPC32XX_I2C_STAT_NAI) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), begin read, receive data bytes, end.
|
||||
*/
|
||||
static int lpc32xx_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
int stat, wlen;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
|
||||
;
|
||||
/* do we need to write an address at all? */
|
||||
if (alen) {
|
||||
/* Address slave in write mode */
|
||||
writel((dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
|
||||
/* write address bytes */
|
||||
while (alen--) {
|
||||
/* compute address byte + stop for the last one */
|
||||
int a = (addr >> (8 * alen)) & 0xff;
|
||||
if (!alen)
|
||||
a |= LPC32XX_I2C_TX_STOP;
|
||||
/* Send address byte */
|
||||
writel(a, &i2c->tx);
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &i2c->stat);
|
||||
}
|
||||
/* do we have to read data at all? */
|
||||
if (length) {
|
||||
/* Address slave in read mode */
|
||||
writel(1 | (dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
|
||||
wlen = length;
|
||||
/* get data */
|
||||
while (length | wlen) {
|
||||
/* read status for TFF and RFE */
|
||||
stat = readl(&i2c->stat);
|
||||
/* must we, can we write a trigger byte? */
|
||||
if ((wlen > 0)
|
||||
& (!(stat & LPC32XX_I2C_STAT_TFF))) {
|
||||
wlen--;
|
||||
/* write trigger byte + stop if last */
|
||||
writel(wlen ? 0 :
|
||||
LPC32XX_I2C_TX_STOP, &i2c->tx);
|
||||
}
|
||||
/* must we, can we read a data byte? */
|
||||
if ((length > 0)
|
||||
& (!(stat & LPC32XX_I2C_STAT_RFE))) {
|
||||
length--;
|
||||
/* read byte */
|
||||
*(data++) = readl(&i2c->rx);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &i2c->stat);
|
||||
/* success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), send data bytes, end.
|
||||
*/
|
||||
static int lpc32xx_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
int stat;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
|
||||
;
|
||||
/* do we need to write anything at all? */
|
||||
if (alen | length)
|
||||
/* Address slave in write mode */
|
||||
writel((dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
|
||||
/* write address bytes */
|
||||
while (alen) {
|
||||
/* wait for transmit fifo not full */
|
||||
stat = readl(&i2c->stat);
|
||||
if (!(stat & LPC32XX_I2C_STAT_TFF)) {
|
||||
alen--;
|
||||
int a = (addr >> (8 * alen)) & 0xff;
|
||||
if (!(alen | length))
|
||||
a |= LPC32XX_I2C_TX_STOP;
|
||||
/* Send address byte */
|
||||
writel(a, &i2c->tx);
|
||||
}
|
||||
}
|
||||
while (length) {
|
||||
/* wait for transmit fifo not full */
|
||||
stat = readl(&i2c->stat);
|
||||
if (!(stat & LPC32XX_I2C_STAT_TFF)) {
|
||||
/* compute data byte, add stop if length==0 */
|
||||
length--;
|
||||
int d = *(data++);
|
||||
if (!length)
|
||||
d |= LPC32XX_I2C_TX_STOP;
|
||||
/* Send data byte */
|
||||
writel(d, &i2c->tx);
|
||||
}
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &i2c->stat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_0, _i2c_init, lpc32xx_i2c_probe,
|
||||
lpc32xx_i2c_read, lpc32xx_i2c_write,
|
||||
lpc32xx_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_LPC32XX_SPEED,
|
||||
CONFIG_SYS_I2C_LPC32XX_SLAVE,
|
||||
0)
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_1, _i2c_init, lpc32xx_i2c_probe,
|
||||
lpc32xx_i2c_read, lpc32xx_i2c_write,
|
||||
lpc32xx_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_LPC32XX_SPEED,
|
||||
CONFIG_SYS_I2C_LPC32XX_SLAVE,
|
||||
1)
|
|
@ -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
|
||||
|
|
764
drivers/mtd/nand/lpc32xx_nand_mlc.c
Normal file
764
drivers/mtd/nand/lpc32xx_nand_mlc.c
Normal file
|
@ -0,0 +1,764 @@
|
|||
/*
|
||||
* LPC32xx MLC NAND flash controller driver
|
||||
*
|
||||
* (C) Copyright 2014 3ADEV <http://3adev.com>
|
||||
* Written by Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* The MLC NAND flash controller provides hardware Reed-Solomon ECC
|
||||
* covering in- and out-of-band data together. Therefore, in- and out-
|
||||
* of-band data must be written together in order to have a valid ECC.
|
||||
*
|
||||
* Consequently, pages with meaningful in-band data are written with
|
||||
* blank (all-ones) out-of-band data and a valid ECC, and any later
|
||||
* out-of-band data write will void the ECC.
|
||||
*
|
||||
* Therefore, code which reads such late-written out-of-band data
|
||||
* should not rely on the ECC validity.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <nand.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
/*
|
||||
* MLC NAND controller registers.
|
||||
*/
|
||||
struct lpc32xx_nand_mlc_registers {
|
||||
u8 buff[32768]; /* controller's serial data buffer */
|
||||
u8 data[32768]; /* NAND's raw data buffer */
|
||||
u32 cmd;
|
||||
u32 addr;
|
||||
u32 ecc_enc_reg;
|
||||
u32 ecc_dec_reg;
|
||||
u32 ecc_auto_enc_reg;
|
||||
u32 ecc_auto_dec_reg;
|
||||
u32 rpr;
|
||||
u32 wpr;
|
||||
u32 rubp;
|
||||
u32 robp;
|
||||
u32 sw_wp_add_low;
|
||||
u32 sw_wp_add_hig;
|
||||
u32 icr;
|
||||
u32 time_reg;
|
||||
u32 irq_mr;
|
||||
u32 irq_sr;
|
||||
u32 lock_pr;
|
||||
u32 isr;
|
||||
u32 ceh;
|
||||
};
|
||||
|
||||
/* LOCK_PR register defines */
|
||||
#define LOCK_PR_UNLOCK_KEY 0x0000A25E /* Magic unlock value */
|
||||
|
||||
/* ICR defines */
|
||||
#define ICR_LARGE_BLOCKS 0x00000004 /* configure for 2KB blocks */
|
||||
#define ICR_ADDR4 0x00000002 /* configure for 4-word addrs */
|
||||
|
||||
/* CEH defines */
|
||||
#define CEH_NORMAL_CE 0x00000001 /* do not force CE ON */
|
||||
|
||||
/* ISR register defines */
|
||||
#define ISR_NAND_READY 0x00000001
|
||||
#define ISR_CONTROLLER_READY 0x00000002
|
||||
#define ISR_ECC_READY 0x00000004
|
||||
#define ISR_DECODER_ERRORS(s) ((((s) >> 4) & 3)+1)
|
||||
#define ISR_DECODER_FAILURE 0x00000040
|
||||
#define ISR_DECODER_ERROR 0x00000008
|
||||
|
||||
/* time-out for NAND chip / controller loops, in us */
|
||||
#define LPC32X_NAND_TIMEOUT 5000
|
||||
|
||||
/*
|
||||
* There is a single instance of the NAND MLC controller
|
||||
*/
|
||||
|
||||
static struct lpc32xx_nand_mlc_registers __iomem *lpc32xx_nand_mlc_registers
|
||||
= (struct lpc32xx_nand_mlc_registers __iomem *)MLC_NAND_BASE;
|
||||
|
||||
#define clkdiv(v, w, o) (((1+(clk/v)) & w) << o)
|
||||
|
||||
/**
|
||||
* OOB data in each small page are 6 'free' then 10 ECC bytes.
|
||||
* To make things easier, when reading large pages, the four pages'
|
||||
* 'free' OOB bytes are grouped in the first 24 bytes of the OOB buffer,
|
||||
* while the the four ECC bytes are groupe in its last 40 bytes.
|
||||
*
|
||||
* The struct below represents how free vs ecc oob bytes are stored
|
||||
* in the buffer.
|
||||
*
|
||||
* Note: the OOB bytes contain the bad block marker at offsets 0 and 1.
|
||||
*/
|
||||
|
||||
struct lpc32xx_oob {
|
||||
struct {
|
||||
uint8_t free_oob_bytes[6];
|
||||
} free[4];
|
||||
struct {
|
||||
uint8_t ecc_oob_bytes[10];
|
||||
} ecc[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the controller
|
||||
*/
|
||||
|
||||
static void lpc32xx_nand_init(void)
|
||||
{
|
||||
unsigned int clk;
|
||||
|
||||
/* Configure controller for no software write protection, x8 bus
|
||||
width, large block device, and 4 address words */
|
||||
|
||||
/* unlock controller registers with magic key */
|
||||
writel(LOCK_PR_UNLOCK_KEY,
|
||||
&lpc32xx_nand_mlc_registers->lock_pr);
|
||||
|
||||
/* enable large blocks and large NANDs */
|
||||
writel(ICR_LARGE_BLOCKS | ICR_ADDR4,
|
||||
&lpc32xx_nand_mlc_registers->icr);
|
||||
|
||||
/* Make sure MLC interrupts are disabled */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->irq_mr);
|
||||
|
||||
/* Normal chip enable operation */
|
||||
writel(CEH_NORMAL_CE,
|
||||
&lpc32xx_nand_mlc_registers->ceh);
|
||||
|
||||
/* Setup NAND timing */
|
||||
clk = get_hclk_clk_rate();
|
||||
|
||||
writel(
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY, 0x03, 24) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY, 0x1F, 19) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_NAND_TA, 0x07, 16) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_HIGH, 0x0F, 12) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_LOW, 0x0F, 8) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_HIGH, 0x0F, 4) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_LOW, 0x0F, 0),
|
||||
&lpc32xx_nand_mlc_registers->time_reg);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_SPL_BUILD)
|
||||
|
||||
/**
|
||||
* lpc32xx_cmd_ctrl - write command to either cmd or data register
|
||||
*/
|
||||
|
||||
static void lpc32xx_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->cmd);
|
||||
else if (ctrl & NAND_ALE)
|
||||
writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_read_byte - read a byte from the NAND
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
|
||||
static uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
return readb(&lpc32xx_nand_mlc_registers->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_dev_ready - test if NAND device (actually controller) is ready
|
||||
* @mtd: MTD device structure
|
||||
* @mode: mode to set the ECC HW to.
|
||||
*/
|
||||
|
||||
static int lpc32xx_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
/* means *controller* ready for us */
|
||||
int status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
return status & ISR_CONTROLLER_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
* ECC layout -- this is needed whatever ECC mode we are using.
|
||||
* In a 2KB (4*512B) page, R/S codes occupy 40 (4*10) bytes.
|
||||
* To make U-Boot's life easier, we pack 'useable' OOB at the
|
||||
* front and R/S ECC at the back.
|
||||
*/
|
||||
|
||||
static struct nand_ecclayout lpc32xx_largepage_ecclayout = {
|
||||
.eccbytes = 40,
|
||||
.eccpos = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
|
||||
34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 48, 50, 51, 52, 53,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
},
|
||||
.oobfree = {
|
||||
/* bytes 0 and 1 are used for the bad block marker */
|
||||
{
|
||||
.offset = 2,
|
||||
.length = 22
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* lpc32xx_read_page_hwecc - read in- and out-of-band data with ECC
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Use large block Auto Decode Read Mode(1) as described in User Manual
|
||||
* section 8.6.2.1.
|
||||
*
|
||||
* The initial Read Mode and Read Start commands are sent by the caller.
|
||||
*
|
||||
* ECC will be false if out-of-band data has been updated since in-band
|
||||
* data was initially written.
|
||||
*/
|
||||
|
||||
static int lpc32xx_read_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout, err, max_bitflips = 0;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* go through all four small pages */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start auto decode (reads 528 NAND bytes) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if decoder failed, return failure */
|
||||
if (status & ISR_DECODER_FAILURE)
|
||||
return -1;
|
||||
/* keep count of maximum bitflips performed */
|
||||
if (status & ISR_DECODER_ERROR) {
|
||||
err = ISR_DECODER_ERRORS(status);
|
||||
if (err > max_bitflips)
|
||||
max_bitflips = err;
|
||||
}
|
||||
/* copy first 512 bytes into buffer */
|
||||
memcpy(buf+512*i, lpc32xx_nand_mlc_registers->buff, 512);
|
||||
/* copy next 6 bytes at front of OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
|
||||
/* copy last 10 bytes (R/S ECC) at back of OOB buffer */
|
||||
memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_read_page_raw - read raw (in-band, out-of-band and ECC) data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Read NAND directly; can read pages with invalid ECC.
|
||||
*/
|
||||
|
||||
static int lpc32xx_read_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* when we get here we've already had the Read Mode(1) */
|
||||
|
||||
/* go through all four small pages */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* wait for NAND to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_NAND_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if NAND stalled, return failure */
|
||||
if (!(status & ISR_NAND_READY))
|
||||
return -1;
|
||||
/* copy first 512 bytes into buffer */
|
||||
memcpy(buf+512*i, lpc32xx_nand_mlc_registers->data, 512);
|
||||
/* copy next 6 bytes at front of OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->data, 6);
|
||||
/* copy last 10 bytes (R/S ECC) at back of OOB buffer */
|
||||
memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->data, 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_read_oob - read out-of-band data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @page: page number to read
|
||||
*
|
||||
* Read out-of-band data. User Manual section 8.6.4 suggests using Read
|
||||
* Mode(3) which the controller will turn into a Read Mode(1) internally
|
||||
* but nand_base.c will turn Mode(3) into Mode(0), so let's use Mode(0)
|
||||
* directly.
|
||||
*
|
||||
* ECC covers in- and out-of-band data and was written when out-of-band
|
||||
* data was blank. Therefore, if the out-of-band being read here is not
|
||||
* blank, then the ECC will be false and the read will return bitflips,
|
||||
* even in case of ECC failure where we will return 5 bitflips. The
|
||||
* caller should be prepared to handle this.
|
||||
*/
|
||||
|
||||
static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout, err, max_bitflips = 0;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* No command was sent before calling read_oob() so send one */
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
||||
|
||||
/* go through all four small pages */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start auto decode (reads 528 NAND bytes) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if decoder failure, count 'one too many' bitflips */
|
||||
if (status & ISR_DECODER_FAILURE)
|
||||
max_bitflips = 5;
|
||||
/* keep count of maximum bitflips performed */
|
||||
if (status & ISR_DECODER_ERROR) {
|
||||
err = ISR_DECODER_ERRORS(status);
|
||||
if (err > max_bitflips)
|
||||
max_bitflips = err;
|
||||
}
|
||||
/* set read pointer to OOB area */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->robp);
|
||||
/* copy next 6 bytes at front of OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
|
||||
/* copy next 10 bytes (R/S ECC) at back of OOB buffer */
|
||||
memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_write_page_hwecc - write in- and out-of-band data with ECC
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*
|
||||
* Use large block Auto Encode as per User Manual section 8.6.4.
|
||||
*
|
||||
* The initial Write Serial Input and final Auto Program commands are
|
||||
* sent by the caller.
|
||||
*/
|
||||
|
||||
static int lpc32xx_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
||||
{
|
||||
unsigned int i, status, timeout;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* when we get here we've already had the SEQIN */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start encode (expects 518 writes to buff) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_enc_reg);
|
||||
/* copy first 512 bytes from buffer */
|
||||
memcpy(&lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
|
||||
/* copy next 6 bytes from OOB buffer -- excluding ECC */
|
||||
memcpy(&lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
|
||||
/* wait for ECC to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_ECC_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if ECC stalled, return failure */
|
||||
if (!(status & ISR_ECC_READY))
|
||||
return -1;
|
||||
/* Trigger auto encode (writes 528 bytes to NAND) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_enc_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if controller stalled, return error */
|
||||
if (!(status & ISR_CONTROLLER_READY))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_write_page_raw - write raw (in-band, out-of-band and ECC) data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Use large block write but without encode.
|
||||
*
|
||||
* The initial Write Serial Input and final Auto Program commands are
|
||||
* sent by the caller.
|
||||
*
|
||||
* This function will write the full out-of-band data, including the
|
||||
* ECC area. Therefore, it can write pages with valid *or* invalid ECC.
|
||||
*/
|
||||
|
||||
static int lpc32xx_write_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
||||
{
|
||||
unsigned int i;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* when we get here we've already had the Read Mode(1) */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* copy first 512 bytes from buffer */
|
||||
memcpy(lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
|
||||
/* copy next 6 bytes into OOB buffer -- excluding ECC */
|
||||
memcpy(lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
|
||||
/* copy next 10 bytes into OOB buffer -- that is 'ECC' */
|
||||
memcpy(lpc32xx_nand_mlc_registers->buff, &oob->ecc[i], 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_write_oob - write out-of-band data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @page: page number to read
|
||||
*
|
||||
* Since ECC covers in- and out-of-band data, writing out-of-band data
|
||||
* with ECC will render the page ECC wrong -- or, if the page was blank,
|
||||
* then it will produce a good ECC but a later in-band data write will
|
||||
* render it wrong.
|
||||
*
|
||||
* Therefore, do not compute or write any ECC, and always return success.
|
||||
*
|
||||
* This implies that we do four writes, since non-ECC out-of-band data
|
||||
* are not contiguous in a large page.
|
||||
*/
|
||||
|
||||
static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
/* update oob on all 4 subpages in sequence */
|
||||
unsigned int i, status, timeout;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start data input */
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x200+0x210*i, page);
|
||||
/* copy 6 non-ECC out-of-band bytes directly into NAND */
|
||||
memcpy(lpc32xx_nand_mlc_registers->data, &oob->free[i], 6);
|
||||
/* program page */
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
/* wait for NAND to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_NAND_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if NAND stalled, return error */
|
||||
if (!(status & ISR_NAND_READY))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_waitfunc - wait until a command is done
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip structure
|
||||
*
|
||||
* Wait for controller and FLASH to both be ready.
|
||||
*/
|
||||
|
||||
static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
{
|
||||
int status;
|
||||
unsigned int timeout;
|
||||
/* wait until both controller and NAND are ready */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
== (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if controller or NAND stalled, return error */
|
||||
if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
!= (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
return -1;
|
||||
/* write NAND status command */
|
||||
writel(NAND_CMD_STATUS, &lpc32xx_nand_mlc_registers->cmd);
|
||||
/* read back status and return it */
|
||||
return readb(&lpc32xx_nand_mlc_registers->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* We are self-initializing, so we need our own chip struct
|
||||
*/
|
||||
|
||||
static struct nand_chip lpc32xx_chip;
|
||||
|
||||
/*
|
||||
* Initialize the controller
|
||||
*/
|
||||
|
||||
void board_nand_init(void)
|
||||
{
|
||||
/* we have only one device anyway */
|
||||
struct mtd_info *mtd = &nand_info[0];
|
||||
/* chip is struct nand_chip, and is now provided by the driver. */
|
||||
mtd->priv = &lpc32xx_chip;
|
||||
/* to store return status in case we need to print it */
|
||||
int ret;
|
||||
|
||||
/* Set all BOARDSPECIFIC (actually core-specific) fields */
|
||||
|
||||
lpc32xx_chip.IO_ADDR_R = &lpc32xx_nand_mlc_registers->buff;
|
||||
lpc32xx_chip.IO_ADDR_W = &lpc32xx_nand_mlc_registers->buff;
|
||||
lpc32xx_chip.cmd_ctrl = lpc32xx_cmd_ctrl;
|
||||
/* do not set init_size: nand_base.c will read sizes from chip */
|
||||
lpc32xx_chip.dev_ready = lpc32xx_dev_ready;
|
||||
/* do not set setup_read_retry: this is NAND-chip-specific */
|
||||
/* do not set chip_delay: we have dev_ready defined. */
|
||||
lpc32xx_chip.options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
/* Set needed ECC fields */
|
||||
|
||||
lpc32xx_chip.ecc.mode = NAND_ECC_HW;
|
||||
lpc32xx_chip.ecc.layout = &lpc32xx_largepage_ecclayout;
|
||||
lpc32xx_chip.ecc.size = 512;
|
||||
lpc32xx_chip.ecc.bytes = 10;
|
||||
lpc32xx_chip.ecc.strength = 4;
|
||||
lpc32xx_chip.ecc.read_page = lpc32xx_read_page_hwecc;
|
||||
lpc32xx_chip.ecc.read_page_raw = lpc32xx_read_page_raw;
|
||||
lpc32xx_chip.ecc.write_page = lpc32xx_write_page_hwecc;
|
||||
lpc32xx_chip.ecc.write_page_raw = lpc32xx_write_page_raw;
|
||||
lpc32xx_chip.ecc.read_oob = lpc32xx_read_oob;
|
||||
lpc32xx_chip.ecc.write_oob = lpc32xx_write_oob;
|
||||
lpc32xx_chip.waitfunc = lpc32xx_waitfunc;
|
||||
|
||||
lpc32xx_chip.read_byte = lpc32xx_read_byte; /* FIXME: NEEDED? */
|
||||
|
||||
/* BBT options: read from last two pages */
|
||||
lpc32xx_chip.bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK
|
||||
| NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE
|
||||
| NAND_BBT_WRITE;
|
||||
|
||||
/* Initialize NAND interface */
|
||||
lpc32xx_nand_init();
|
||||
|
||||
/* identify chip */
|
||||
ret = nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL);
|
||||
if (ret) {
|
||||
error("nand_scan_ident returned %i", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* finish scanning the chip */
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
error("nand_scan_tail returned %i", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* chip is good, register it */
|
||||
ret = nand_register(0);
|
||||
if (ret)
|
||||
error("nand_register returned %i", ret);
|
||||
}
|
||||
|
||||
#else /* defined(CONFIG_SPL_BUILD) */
|
||||
|
||||
void nand_init(void)
|
||||
{
|
||||
/* enable NAND controller */
|
||||
lpc32xx_mlc_nand_init();
|
||||
/* initialize NAND controller */
|
||||
lpc32xx_nand_init();
|
||||
}
|
||||
|
||||
void nand_deselect(void)
|
||||
{
|
||||
/* nothing to do, but SPL requires this function */
|
||||
}
|
||||
|
||||
static int read_single_page(uint8_t *dest, int page,
|
||||
struct lpc32xx_oob *oob)
|
||||
{
|
||||
int status, i, timeout, err, max_bitflips = 0;
|
||||
|
||||
/* enter read mode */
|
||||
writel(NAND_CMD_READ0, &lpc32xx_nand_mlc_registers->cmd);
|
||||
/* send column (lsb then MSB) and page (lsb to MSB) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel(0, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel(page & 0xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel((page>>8) & 0xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel((page>>16) & 0xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
/* start reading */
|
||||
writel(NAND_CMD_READSTART, &lpc32xx_nand_mlc_registers->cmd);
|
||||
|
||||
/* large page auto decode read */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start auto decode (reads 528 NAND bytes) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if controller stalled, return error */
|
||||
if (!(status & ISR_CONTROLLER_READY))
|
||||
return -1;
|
||||
/* if decoder failure, return error */
|
||||
if (status & ISR_DECODER_FAILURE)
|
||||
return -1;
|
||||
/* keep count of maximum bitflips performed */
|
||||
if (status & ISR_DECODER_ERROR) {
|
||||
err = ISR_DECODER_ERRORS(status);
|
||||
if (err > max_bitflips)
|
||||
max_bitflips = err;
|
||||
}
|
||||
/* copy first 512 bytes into buffer */
|
||||
memcpy(dest+i*512, lpc32xx_nand_mlc_registers->buff, 512);
|
||||
/* copy next 6 bytes bytes into OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load U-Boot signed image.
|
||||
* This loads an image from NAND, skipping bad blocks.
|
||||
* A block is declared bad if at least one of its readable pages has
|
||||
* a bad block marker in its OOB at position 0.
|
||||
* If all pages ion a block are unreadable, the block is considered
|
||||
* bad (i.e., assumed not to be part of the image) and skipped.
|
||||
*
|
||||
* IMPORTANT NOTE:
|
||||
*
|
||||
* If the first block of the image is fully unreadable, it will be
|
||||
* ignored and skipped as if it had been marked bad. If it was not
|
||||
* actually marked bad at the time of writing the image, the resulting
|
||||
* image loaded will lack a header and magic number. It could thus be
|
||||
* considered as a raw, headerless, image and SPL might erroneously
|
||||
* jump into it.
|
||||
*
|
||||
* In order to avoid this risk, LPC32XX-based boards which use this
|
||||
* driver MUST define CONFIG_SPL_PANIC_ON_RAW_IMAGE.
|
||||
*/
|
||||
|
||||
#define BYTES_PER_PAGE 2048
|
||||
#define PAGES_PER_BLOCK 64
|
||||
#define BYTES_PER_BLOCK (BYTES_PER_PAGE * PAGES_PER_BLOCK)
|
||||
#define PAGES_PER_CHIP_MAX 524288
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
|
||||
{
|
||||
int bytes_left = size;
|
||||
int pages_left = DIV_ROUND_UP(size, BYTES_PER_PAGE);
|
||||
int blocks_left = DIV_ROUND_UP(size, BYTES_PER_BLOCK);
|
||||
int block = 0;
|
||||
int page = offs / BYTES_PER_PAGE;
|
||||
/* perform reads block by block */
|
||||
while (blocks_left) {
|
||||
/* compute first page number to read */
|
||||
void *block_page_dst = dst;
|
||||
/* read at most one block, possibly less */
|
||||
int block_bytes_left = bytes_left;
|
||||
if (block_bytes_left > BYTES_PER_BLOCK)
|
||||
block_bytes_left = BYTES_PER_BLOCK;
|
||||
/* keep track of good, failed, and "bad" pages */
|
||||
int block_pages_good = 0;
|
||||
int block_pages_bad = 0;
|
||||
int block_pages_err = 0;
|
||||
/* we shall read a full block of pages, maybe less */
|
||||
int block_pages_left = pages_left;
|
||||
if (block_pages_left > PAGES_PER_BLOCK)
|
||||
block_pages_left = PAGES_PER_BLOCK;
|
||||
int block_pages = block_pages_left;
|
||||
int block_page = page;
|
||||
/* while pages are left and the block is not known as bad */
|
||||
while ((block_pages > 0) && (block_pages_bad == 0)) {
|
||||
/* we will read OOB, too, for bad block markers */
|
||||
struct lpc32xx_oob oob;
|
||||
/* read page */
|
||||
int res = read_single_page(block_page_dst, block_page,
|
||||
&oob);
|
||||
/* count readable pages */
|
||||
if (res >= 0) {
|
||||
/* this page is good */
|
||||
block_pages_good++;
|
||||
/* this page is bad */
|
||||
if ((oob.free[0].free_oob_bytes[0] != 0xff)
|
||||
| (oob.free[0].free_oob_bytes[1] != 0xff))
|
||||
block_pages_bad++;
|
||||
} else
|
||||
/* count errors */
|
||||
block_pages_err++;
|
||||
/* we're done with this page */
|
||||
block_page++;
|
||||
block_page_dst += BYTES_PER_PAGE;
|
||||
if (block_pages)
|
||||
block_pages--;
|
||||
}
|
||||
/* a fully unreadable block is considered bad */
|
||||
if (block_pages_good == 0)
|
||||
block_pages_bad = block_pages_err;
|
||||
/* errors are fatal only in good blocks */
|
||||
if ((block_pages_err > 0) && (block_pages_bad == 0))
|
||||
return -1;
|
||||
/* we keep reads only of good blocks */
|
||||
if (block_pages_bad == 0) {
|
||||
dst += block_bytes_left;
|
||||
bytes_left -= block_bytes_left;
|
||||
pages_left -= block_pages_left;
|
||||
blocks_left--;
|
||||
}
|
||||
/* good or bad, we're done with this block */
|
||||
block++;
|
||||
page += PAGES_PER_BLOCK;
|
||||
}
|
||||
|
||||
/* report success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SPL_BUILD */
|
|
@ -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
|
||||
|
|
637
drivers/net/lpc32xx_eth.c
Normal file
637
drivers/net/lpc32xx_eth.c
Normal file
|
@ -0,0 +1,637 @@
|
|||
/*
|
||||
* LPC32xx Ethernet MAC interface driver
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <net.h>
|
||||
#include <malloc.h>
|
||||
#include <miiphy.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/types.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/config.h>
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
*
|
||||
* 1. Unless specified otherwise, all references to tables or paragraphs
|
||||
* are to UM10326, "LPC32x0 and LPC32x0/01 User manual".
|
||||
*
|
||||
* 2. Only bitfield masks/values which are actually used by the driver
|
||||
* are defined.
|
||||
*/
|
||||
|
||||
/* a single RX descriptor. The controller has an array of these */
|
||||
struct lpc32xx_eth_rxdesc {
|
||||
u32 packet; /* Receive packet pointer */
|
||||
u32 control; /* Descriptor command status */
|
||||
};
|
||||
|
||||
#define LPC32XX_ETH_RX_DESC_SIZE (sizeof(struct lpc32xx_eth_rxdesc))
|
||||
|
||||
/* RX control bitfields/masks (see Table 330) */
|
||||
#define LPC32XX_ETH_RX_CTRL_SIZE_MASK 0x000007FF
|
||||
#define LPC32XX_ETH_RX_CTRL_UNUSED 0x7FFFF800
|
||||
#define LPC32XX_ETH_RX_CTRL_INTERRUPT 0x80000000
|
||||
|
||||
/* a single RX status. The controller has an array of these */
|
||||
struct lpc32xx_eth_rxstat {
|
||||
u32 statusinfo; /* Transmit Descriptor status */
|
||||
u32 statushashcrc; /* Transmit Descriptor CRCs */
|
||||
};
|
||||
|
||||
#define LPC32XX_ETH_RX_STAT_SIZE (sizeof(struct lpc32xx_eth_rxstat))
|
||||
|
||||
/* RX statusinfo bitfields/masks (see Table 333) */
|
||||
#define RX_STAT_RXSIZE 0x000007FF
|
||||
/* Helper: OR of all errors except RANGE */
|
||||
#define RX_STAT_ERRORS 0x1B800000
|
||||
|
||||
/* a single TX descriptor. The controller has an array of these */
|
||||
struct lpc32xx_eth_txdesc {
|
||||
u32 packet; /* Transmit packet pointer */
|
||||
u32 control; /* Descriptor control */
|
||||
};
|
||||
|
||||
#define LPC32XX_ETH_TX_DESC_SIZE (sizeof(struct lpc32xx_eth_txdesc))
|
||||
|
||||
/* TX control bitfields/masks (see Table 335) */
|
||||
#define TX_CTRL_TXSIZE 0x000007FF
|
||||
#define TX_CTRL_LAST 0x40000000
|
||||
|
||||
/* a single TX status. The controller has an array of these */
|
||||
struct lpc32xx_eth_txstat {
|
||||
u32 statusinfo; /* Transmit Descriptor status */
|
||||
};
|
||||
|
||||
#define LPC32XX_ETH_TX_STAT_SIZE (sizeof(struct lpc32xx_eth_txstat))
|
||||
|
||||
/* Ethernet MAC interface registers (see Table 283) */
|
||||
struct lpc32xx_eth_registers {
|
||||
/* MAC registers - 0x3106_0000 to 0x3106_01FC */
|
||||
u32 mac1; /* MAC configuration register 1 */
|
||||
u32 mac2; /* MAC configuration register 2 */
|
||||
u32 ipgt; /* Back-to-back Inter-Packet Gap reg. */
|
||||
u32 ipgr; /* Non-back-to-back IPG register */
|
||||
u32 clrt; /* Collision Window / Retry register */
|
||||
u32 maxf; /* Maximum Frame register */
|
||||
u32 supp; /* Phy Support register */
|
||||
u32 test;
|
||||
u32 mcfg; /* MII management configuration reg. */
|
||||
u32 mcmd; /* MII management command register */
|
||||
u32 madr; /* MII management address register */
|
||||
u32 mwtd; /* MII management wite data register */
|
||||
u32 mrdd; /* MII management read data register */
|
||||
u32 mind; /* MII management indicators register */
|
||||
u32 reserved1[2];
|
||||
u32 sa0; /* Station address register 0 */
|
||||
u32 sa1; /* Station address register 1 */
|
||||
u32 sa2; /* Station address register 2 */
|
||||
u32 reserved2[45];
|
||||
/* Control registers */
|
||||
u32 command;
|
||||
u32 status;
|
||||
u32 rxdescriptor;
|
||||
u32 rxstatus;
|
||||
u32 rxdescriptornumber; /* actually, number MINUS ONE */
|
||||
u32 rxproduceindex; /* head of rx desc fifo */
|
||||
u32 rxconsumeindex; /* tail of rx desc fifo */
|
||||
u32 txdescriptor;
|
||||
u32 txstatus;
|
||||
u32 txdescriptornumber; /* actually, number MINUS ONE */
|
||||
u32 txproduceindex; /* head of rx desc fifo */
|
||||
u32 txconsumeindex; /* tail of rx desc fifo */
|
||||
u32 reserved3[10];
|
||||
u32 tsv0; /* Transmit status vector register 0 */
|
||||
u32 tsv1; /* Transmit status vector register 1 */
|
||||
u32 rsv; /* Receive status vector register */
|
||||
u32 reserved4[3];
|
||||
u32 flowcontrolcounter;
|
||||
u32 flowcontrolstatus;
|
||||
u32 reserved5[34];
|
||||
/* RX filter registers - 0x3106_0200 to 0x3106_0FDC */
|
||||
u32 rxfilterctrl;
|
||||
u32 rxfilterwolstatus;
|
||||
u32 rxfilterwolclear;
|
||||
u32 reserved6;
|
||||
u32 hashfilterl;
|
||||
u32 hashfilterh;
|
||||
u32 reserved7[882];
|
||||
/* Module control registers - 0x3106_0FE0 to 0x3106_0FF8 */
|
||||
u32 intstatus; /* Interrupt status register */
|
||||
u32 intenable;
|
||||
u32 intclear;
|
||||
u32 intset;
|
||||
u32 reserved8;
|
||||
u32 powerdown;
|
||||
u32 reserved9;
|
||||
};
|
||||
|
||||
/* MAC1 register bitfields/masks and offsets (see Table 283) */
|
||||
#define MAC1_RECV_ENABLE 0x00000001
|
||||
#define MAC1_PASS_ALL_RX_FRAMES 0x00000002
|
||||
#define MAC1_SOFT_RESET 0x00008000
|
||||
/* Helper: general reset */
|
||||
#define MAC1_RESETS 0x0000CF00
|
||||
|
||||
/* MAC2 register bitfields/masks and offsets (see Table 284) */
|
||||
#define MAC2_FULL_DUPLEX 0x00000001
|
||||
#define MAC2_CRC_ENABLE 0x00000010
|
||||
#define MAC2_PAD_CRC_ENABLE 0x00000020
|
||||
|
||||
/* SUPP register bitfields/masks and offsets (see Table 290) */
|
||||
#define SUPP_SPEED 0x00000100
|
||||
|
||||
/* MCFG register bitfields/masks and offsets (see Table 292) */
|
||||
#define MCFG_CLOCK_SELECT_MASK 0x0000001C
|
||||
/* divide clock by 28 (see Table 293) */
|
||||
#define MCFG_CLOCK_SELECT_DIV28 0x0000001C
|
||||
|
||||
/* MADR register bitfields/masks and offsets (see Table 295) */
|
||||
#define MADR_REG_MASK 0x0000001F
|
||||
#define MADR_PHY_MASK 0x00001F00
|
||||
#define MADR_REG_OFFSET 0
|
||||
#define MADR_PHY_OFFSET 8
|
||||
|
||||
/* MIND register bitfields/masks (see Table 298) */
|
||||
#define MIND_BUSY 0x00000001
|
||||
|
||||
/* COMMAND register bitfields/masks and offsets (see Table 283) */
|
||||
#define COMMAND_RXENABLE 0x00000001
|
||||
#define COMMAND_TXENABLE 0x00000002
|
||||
#define COMMAND_PASSRUNTFRAME 0x00000040
|
||||
#define COMMAND_FULL_DUPLEX 0x00000400
|
||||
/* Helper: general reset */
|
||||
#define COMMAND_RESETS 0x0000001C
|
||||
|
||||
/* STATUS register bitfields/masks and offsets (see Table 283) */
|
||||
#define STATUS_RXSTATUS 0x00000001
|
||||
#define STATUS_TXSTATUS 0x00000002
|
||||
|
||||
/* RXFILTERCTRL register bitfields/masks (see Table 319) */
|
||||
#define RXFILTERCTRL_ACCEPTBROADCAST 0x00000002
|
||||
#define RXFILTERCTRL_ACCEPTPERFECT 0x00000020
|
||||
|
||||
/* Buffers and descriptors */
|
||||
|
||||
#define ATTRS(n) __aligned(n)
|
||||
|
||||
#define TX_BUF_COUNT 4
|
||||
#define RX_BUF_COUNT 4
|
||||
|
||||
struct lpc32xx_eth_buffers {
|
||||
ATTRS(4) struct lpc32xx_eth_txdesc tx_desc[TX_BUF_COUNT];
|
||||
ATTRS(4) struct lpc32xx_eth_txstat tx_stat[TX_BUF_COUNT];
|
||||
ATTRS(PKTALIGN) u8 tx_buf[TX_BUF_COUNT*PKTSIZE_ALIGN];
|
||||
ATTRS(4) struct lpc32xx_eth_rxdesc rx_desc[RX_BUF_COUNT];
|
||||
ATTRS(8) struct lpc32xx_eth_rxstat rx_stat[RX_BUF_COUNT];
|
||||
ATTRS(PKTALIGN) u8 rx_buf[RX_BUF_COUNT*PKTSIZE_ALIGN];
|
||||
};
|
||||
|
||||
/* port device data struct */
|
||||
struct lpc32xx_eth_device {
|
||||
struct eth_device dev;
|
||||
struct lpc32xx_eth_registers *regs;
|
||||
struct lpc32xx_eth_buffers *bufs;
|
||||
};
|
||||
|
||||
#define LPC32XX_ETH_DEVICE_SIZE (sizeof(struct lpc32xx_eth_device))
|
||||
|
||||
/* generic macros */
|
||||
#define to_lpc32xx_eth(_d) container_of(_d, struct lpc32xx_eth_device, dev)
|
||||
|
||||
/* timeout for MII polling */
|
||||
#define MII_TIMEOUT 10000000
|
||||
|
||||
/* limits for PHY and register addresses */
|
||||
#define MII_MAX_REG (MADR_REG_MASK >> MADR_REG_OFFSET)
|
||||
|
||||
#define MII_MAX_PHY (MADR_PHY_MASK >> MADR_PHY_OFFSET)
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#if defined(CONFIG_PHYLIB) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
|
||||
/*
|
||||
* mii_reg_read - miiphy_read callback function.
|
||||
*
|
||||
* Returns 16bit phy register value, or 0xffff on error
|
||||
*/
|
||||
static int mii_reg_read(const char *devname, u8 phy_adr, u8 reg_ofs, u16 *data)
|
||||
{
|
||||
struct eth_device *dev = eth_get_dev_by_name(devname);
|
||||
struct lpc32xx_eth_device *dlpc32xx_eth = to_lpc32xx_eth(dev);
|
||||
struct lpc32xx_eth_registers *regs = dlpc32xx_eth->regs;
|
||||
u32 mind_reg;
|
||||
u32 timeout;
|
||||
|
||||
/* check parameters */
|
||||
if (phy_adr > MII_MAX_PHY) {
|
||||
printf("%s:%u: Invalid PHY address %d\n",
|
||||
__func__, __LINE__, phy_adr);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (reg_ofs > MII_MAX_REG) {
|
||||
printf("%s:%u: Invalid register offset %d\n",
|
||||
__func__, __LINE__, reg_ofs);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* write the phy and reg addressse into the MII address reg */
|
||||
writel((phy_adr << MADR_PHY_OFFSET) | (reg_ofs << MADR_REG_OFFSET),
|
||||
®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;
|
||||
}
|
|
@ -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
|
||||
|
|
144
drivers/spi/lpc32xx_ssp.c
Normal file
144
drivers/spi/lpc32xx_ssp.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* LPC32xx SSP interface (SPI mode)
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/compat.h>
|
||||
#include <asm/io.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
/* SSP chip registers */
|
||||
struct ssp_regs {
|
||||
u32 cr0;
|
||||
u32 cr1;
|
||||
u32 data;
|
||||
u32 sr;
|
||||
u32 cpsr;
|
||||
u32 imsc;
|
||||
u32 ris;
|
||||
u32 mis;
|
||||
u32 icr;
|
||||
u32 dmacr;
|
||||
};
|
||||
|
||||
/* CR1 register defines */
|
||||
#define SSP_CR1_SSP_ENABLE 0x0002
|
||||
|
||||
/* SR register defines */
|
||||
#define SSP_SR_TNF 0x0002
|
||||
/* SSP status RX FIFO not empty bit */
|
||||
#define SSP_SR_RNE 0x0004
|
||||
|
||||
/* lpc32xx spi slave */
|
||||
struct lpc32xx_spi_slave {
|
||||
struct spi_slave slave;
|
||||
struct ssp_regs *regs;
|
||||
};
|
||||
|
||||
static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(
|
||||
struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct lpc32xx_spi_slave, slave);
|
||||
}
|
||||
|
||||
/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
|
||||
void spi_init(void)
|
||||
{
|
||||
/*
|
||||
* nothing to do: clocking was enabled in lpc32xx_ssp_enable()
|
||||
* and configuration will be done in spi_setup_slave()
|
||||
*/
|
||||
}
|
||||
|
||||
/* the following is called in sequence by do_spi_xfer() */
|
||||
|
||||
struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
|
||||
{
|
||||
struct lpc32xx_spi_slave *lslave;
|
||||
|
||||
/* we only set up SSP0 for now, so ignore bus */
|
||||
|
||||
if (mode & SPI_3WIRE) {
|
||||
error("3-wire mode not supported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode & SPI_SLAVE) {
|
||||
error("slave mode not supported\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode & SPI_PREAMBLE) {
|
||||
error("preamble byte skipping not supported\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lslave = spi_alloc_slave(struct lpc32xx_spi_slave, bus, cs);
|
||||
if (!lslave) {
|
||||
printf("SPI_error: Fail to allocate lpc32xx_spi_slave\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lslave->regs = (struct ssp_regs *)SSP0_BASE;
|
||||
|
||||
/*
|
||||
* 8 bit frame, SPI fmt, 500kbps -> clock divider is 26.
|
||||
* Set SCR to 0 and CPSDVSR to 26.
|
||||
*/
|
||||
|
||||
writel(0x7, &lslave->regs->cr0); /* 8-bit chunks, SPI, 1 clk/bit */
|
||||
writel(26, &lslave->regs->cpsr); /* SSP clock = HCLK/26 = 500kbps */
|
||||
writel(0, &lslave->regs->imsc); /* do not raise any interrupts */
|
||||
writel(0, &lslave->regs->icr); /* clear any pending interrupt */
|
||||
writel(0, &lslave->regs->dmacr); /* do not do DMAs */
|
||||
writel(SSP_CR1_SSP_ENABLE, &lslave->regs->cr1); /* enable SSP0 */
|
||||
return &lslave->slave;
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
|
||||
|
||||
debug("(lpc32xx) spi_free_slave: 0x%08x\n", (u32)lslave);
|
||||
free(lslave);
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
/* only one bus and slave so far, always available */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
|
||||
int bytelen = bitlen >> 3;
|
||||
int idx_out = 0;
|
||||
int idx_in = 0;
|
||||
int start_time;
|
||||
|
||||
start_time = get_timer(0);
|
||||
while ((idx_out < bytelen) || (idx_in < bytelen)) {
|
||||
int status = readl(&lslave->regs->sr);
|
||||
if ((idx_out < bytelen) && (status & SSP_SR_TNF))
|
||||
writel(((u8 *)dout)[idx_out++], &lslave->regs->data);
|
||||
if ((idx_in < bytelen) && (status & status & SSP_SR_RNE))
|
||||
((u8 *)din)[idx_in++] = readl(&lslave->regs->data);
|
||||
if (get_timer(start_time) >= CONFIG_LPC32XX_SSP_TIMEOUT)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
241
include/configs/work_92105.h
Normal file
241
include/configs/work_92105.h
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* WORK Microwave work_92105 board configuration file
|
||||
*
|
||||
* (C) Copyright 2014 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_WORK_92105_H__
|
||||
#define __CONFIG_WORK_92105_H__
|
||||
|
||||
/* SoC and board defines */
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
|
||||
/*
|
||||
* Define work_92105 machine type by hand -- done only for compatibility
|
||||
* with original board code
|
||||
*/
|
||||
#define MACH_TYPE_WORK_92105 736
|
||||
#define CONFIG_MACH_TYPE MACH_TYPE_WORK_92105
|
||||
|
||||
#define CONFIG_SYS_ICACHE_OFF
|
||||
#define CONFIG_SYS_DCACHE_OFF
|
||||
#if !defined(CONFIG_SPL_BUILD)
|
||||
#define CONFIG_SKIP_LOWLEVEL_INIT
|
||||
#endif
|
||||
#define CONFIG_BOARD_EARLY_INIT_F
|
||||
#define CONFIG_BOARD_EARLY_INIT_R
|
||||
|
||||
/* generate LPC32XX-specific SPL image */
|
||||
#define CONFIG_LPC32XX_SPL
|
||||
|
||||
/*
|
||||
* Memory configurations
|
||||
*/
|
||||
#define CONFIG_NR_DRAM_BANKS 1
|
||||
#define CONFIG_SYS_MALLOC_LEN SZ_1M
|
||||
#define CONFIG_SYS_SDRAM_BASE EMC_DYCS0_BASE
|
||||
#define CONFIG_SYS_SDRAM_SIZE SZ_128M
|
||||
#define CONFIG_SYS_TEXT_BASE 0x80100000
|
||||
#define CONFIG_SYS_MEMTEST_START (CONFIG_SYS_SDRAM_BASE + SZ_32K)
|
||||
#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_TEXT_BASE - SZ_1M)
|
||||
|
||||
#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_32K)
|
||||
|
||||
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_512K \
|
||||
- GENERATED_GBL_DATA_SIZE)
|
||||
|
||||
/*
|
||||
* Serial Driver
|
||||
*/
|
||||
#define CONFIG_SYS_LPC32XX_UART 5 /* UART5 - NS16550 */
|
||||
#define CONFIG_BAUDRATE 115200
|
||||
|
||||
/*
|
||||
* Ethernet Driver
|
||||
*/
|
||||
|
||||
#define CONFIG_PHY_SMSC
|
||||
#define CONFIG_LPC32XX_ETH
|
||||
#define CONFIG_PHYLIB
|
||||
#define CONFIG_PHY_ADDR 0
|
||||
#define CONFIG_SYS_FAULT_ECHO_LINK_DOWN
|
||||
#define CONFIG_CMD_MII
|
||||
#define CONFIG_CMD_PING
|
||||
#define CONFIG_CMD_DHCP
|
||||
/* FIXME: remove "Waiting for PHY auto negotiation to complete..." message */
|
||||
|
||||
/*
|
||||
* I2C driver
|
||||
*/
|
||||
|
||||
#define CONFIG_SYS_I2C_LPC32XX
|
||||
#define CONFIG_SYS_I2C
|
||||
#define CONFIG_CMD_I2C
|
||||
#define CONFIG_SYS_I2C_SPEED 350000
|
||||
|
||||
/*
|
||||
* I2C EEPROM
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD_EEPROM
|
||||
#define CONFIG_SYS_I2C_EEPROM_ADDR 0x56
|
||||
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2
|
||||
|
||||
/*
|
||||
* I2C RTC
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD_DATE
|
||||
#define CONFIG_RTC_DS1374
|
||||
|
||||
/*
|
||||
* I2C Temperature Sensor (DTT)
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD_DTT
|
||||
#define CONFIG_DTT_SENSORS { 0, 1 }
|
||||
#define CONFIG_DTT_DS620
|
||||
|
||||
/*
|
||||
* U-Boot General Configurations
|
||||
*/
|
||||
#define CONFIG_SYS_GENERIC_BOARD
|
||||
#define CONFIG_SYS_LONGHELP
|
||||
#define CONFIG_SYS_CBSIZE 1024
|
||||
#define CONFIG_SYS_PBSIZE \
|
||||
(CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16)
|
||||
#define CONFIG_SYS_MAXARGS 16
|
||||
#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
|
||||
|
||||
#define CONFIG_SYS_HUSH_PARSER
|
||||
|
||||
#define CONFIG_AUTO_COMPLETE
|
||||
#define CONFIG_CMDLINE_EDITING
|
||||
#define CONFIG_VERSION_VARIABLE
|
||||
#define CONFIG_DISPLAY_CPUINFO
|
||||
#define CONFIG_DOS_PARTITION
|
||||
|
||||
/*
|
||||
* No NOR
|
||||
*/
|
||||
|
||||
#define CONFIG_SYS_NO_FLASH
|
||||
|
||||
/*
|
||||
* NAND chip timings for FIXME: which one?
|
||||
*/
|
||||
|
||||
#define CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY 333333333
|
||||
#define CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY 10000000
|
||||
#define CONFIG_LPC32XX_NAND_MLC_NAND_TA 18181818
|
||||
#define CONFIG_LPC32XX_NAND_MLC_RD_HIGH 31250000
|
||||
#define CONFIG_LPC32XX_NAND_MLC_RD_LOW 45454545
|
||||
#define CONFIG_LPC32XX_NAND_MLC_WR_HIGH 40000000
|
||||
#define CONFIG_LPC32XX_NAND_MLC_WR_LOW 83333333
|
||||
|
||||
/*
|
||||
* NAND
|
||||
*/
|
||||
|
||||
/* driver configuration */
|
||||
#define CONFIG_SYS_NAND_SELF_INIT
|
||||
#define CONFIG_SYS_MAX_NAND_DEVICE 1
|
||||
#define CONFIG_SYS_MAX_NAND_CHIPS 1
|
||||
#define CONFIG_SYS_NAND_BASE MLC_NAND_BASE
|
||||
#define CONFIG_NAND_LPC32XX_MLC
|
||||
|
||||
#define CONFIG_CMD_NAND
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD_GPIO
|
||||
#define CONFIG_LPC32XX_GPIO
|
||||
|
||||
/*
|
||||
* SSP/SPI/DISPLAY
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD_SPI
|
||||
#define CONFIG_LPC32XX_SSP
|
||||
#define CONFIG_LPC32XX_SSP_TIMEOUT 100000
|
||||
#define CONFIG_CMD_MAX6957
|
||||
#define CONFIG_CMD_HD44760
|
||||
/*
|
||||
* Environment
|
||||
*/
|
||||
|
||||
#define CONFIG_ENV_IS_IN_NAND 1
|
||||
#define CONFIG_ENV_SIZE 0x00020000
|
||||
#define CONFIG_ENV_OFFSET 0x00100000
|
||||
#define CONFIG_ENV_OFFSET_REDUND 0x00120000
|
||||
#define CONFIG_ENV_ADDR 0x80000100
|
||||
|
||||
/*
|
||||
* Provide default ethernet address
|
||||
*
|
||||
* THIS IS NORMALLY NOT DONE. HERE WE KEEP WHAT WAS IN THE PORTED
|
||||
* BOARD CONFIG IN CASE SOME PROVISIONING PROCESS OUT THERE EXPECTS
|
||||
* THIS MAC ADDRESS WHEN THE DEVICE HAS STILL ITS DEFAULT CONFIG.
|
||||
*/
|
||||
|
||||
#define CONFIG_ETHADDR 00:12:B4:00:AF:FE
|
||||
#define CONFIG_OVERWRITE_ETHADDR_ONCE
|
||||
|
||||
/*
|
||||
* U-Boot Commands
|
||||
*/
|
||||
#include <config_cmd_default.h>
|
||||
|
||||
/*
|
||||
* Boot Linux
|
||||
*/
|
||||
#define CONFIG_CMDLINE_TAG
|
||||
#define CONFIG_SETUP_MEMORY_TAGS
|
||||
#define CONFIG_INITRD_TAG
|
||||
|
||||
#define CONFIG_ZERO_BOOTDELAY_CHECK
|
||||
#define CONFIG_BOOTDELAY 3
|
||||
|
||||
#define CONFIG_BOOTFILE "uImage"
|
||||
#define CONFIG_BOOTARGS "console=ttyS2,115200n8"
|
||||
#define CONFIG_LOADADDR 0x80008000
|
||||
|
||||
/*
|
||||
* SPL
|
||||
*/
|
||||
|
||||
/* SPL will be executed at offset 0 */
|
||||
#define CONFIG_SPL_TEXT_BASE 0x00000000
|
||||
/* SPL will use SRAM as stack */
|
||||
#define CONFIG_SPL_STACK 0x0000FFF8
|
||||
#define CONFIG_SPL_BOARD_INIT
|
||||
/* Use the framework and generic lib */
|
||||
#define CONFIG_SPL_FRAMEWORK
|
||||
#define CONFIG_SPL_LIBGENERIC_SUPPORT
|
||||
#define CONFIG_SPL_LIBCOMMON_SUPPORT
|
||||
/* SPL will use serial */
|
||||
#define CONFIG_SPL_SERIAL_SUPPORT
|
||||
/* SPL will load U-Boot from NAND offset 0x40000 */
|
||||
#define CONFIG_SPL_NAND_SUPPORT
|
||||
#define CONFIG_SPL_NAND_DRIVERS
|
||||
#define CONFIG_SPL_NAND_BASE
|
||||
#define CONFIG_SPL_NAND_BOOT
|
||||
#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x00040000
|
||||
#define CONFIG_SPL_PAD_TO 0x20000
|
||||
/* U-Boot will be 0x40000 bytes, loaded and run at CONFIG_SYS_TEXT_BASE */
|
||||
#define CONFIG_SYS_MONITOR_LEN 0x40000 /* actually, MAX size */
|
||||
#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE
|
||||
#define CONFIG_SYS_NAND_U_BOOT_DST CONFIG_SYS_TEXT_BASE
|
||||
|
||||
/*
|
||||
* Include SoC specific configuration
|
||||
*/
|
||||
#include <asm/arch/config.h>
|
||||
|
||||
#endif /* __CONFIG_WORK_92105_H__*/
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 \
|
||||
|
|
178
tools/lpc32xximage.c
Normal file
178
tools/lpc32xximage.c
Normal file
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Image manipulator for LPC32XX SoCs
|
||||
*
|
||||
* (C) Copyright 2015 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* Derived from omapimage.c:
|
||||
*
|
||||
* (C) Copyright 2010
|
||||
* Linaro LTD, www.linaro.org
|
||||
* Author: John Rigby <john.rigby@linaro.org>
|
||||
* Based on TI's signGP.c
|
||||
*
|
||||
* (C) Copyright 2009
|
||||
* Stefano Babic, DENX Software Engineering, sbabic@denx.de.
|
||||
*
|
||||
* (C) Copyright 2008
|
||||
* Marvell Semiconductor <www.marvell.com>
|
||||
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include "imagetool.h"
|
||||
#include <compiler.h>
|
||||
#include <image.h>
|
||||
|
||||
/*
|
||||
* NAND page 0 boot header
|
||||
*/
|
||||
|
||||
struct nand_page_0_boot_header {
|
||||
uint32_t data[129];
|
||||
uint32_t pad[383];
|
||||
};
|
||||
|
||||
/*
|
||||
* Default ICC (interface configuration data [sic]) if none specified
|
||||
* in board config
|
||||
*/
|
||||
|
||||
#ifndef LPC32XX_BOOT_ICR
|
||||
#define LPC32XX_BOOT_ICR 0x00000096
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default boot NAND page size if none specified in board config
|
||||
*/
|
||||
|
||||
#ifndef LPC32XX_BOOT_NAND_PAGESIZE
|
||||
#define LPC32XX_BOOT_NAND_PAGESIZE 2048
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default boot NAND pages per sector if none specified in board config
|
||||
*/
|
||||
|
||||
#ifndef LPC32XX_BOOT_NAND_PAGES_PER_SECTOR
|
||||
#define LPC32XX_BOOT_NAND_PAGES_PER_SECTOR 64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Maximum size for boot code is 56K unless defined in board config
|
||||
*/
|
||||
|
||||
#ifndef LPC32XX_BOOT_CODESIZE
|
||||
#define LPC32XX_BOOT_CODESIZE (56*1024)
|
||||
#endif
|
||||
|
||||
/* signature byte for a readable block */
|
||||
|
||||
#define LPC32XX_BOOT_BLOCK_OK 0xaa
|
||||
|
||||
static struct nand_page_0_boot_header lpc32xximage_header;
|
||||
|
||||
static int lpc32xximage_check_image_types(uint8_t type)
|
||||
{
|
||||
if (type == IH_TYPE_LPC32XXIMAGE)
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int lpc32xximage_verify_header(unsigned char *ptr, int image_size,
|
||||
struct image_tool_params *params)
|
||||
{
|
||||
struct nand_page_0_boot_header *hdr =
|
||||
(struct nand_page_0_boot_header *)ptr;
|
||||
|
||||
/* turn image size from bytes to NAND pages, page 0 included */
|
||||
int image_size_in_pages = ((image_size - 1)
|
||||
/ LPC32XX_BOOT_NAND_PAGESIZE);
|
||||
|
||||
if (hdr->data[0] != (0xff & LPC32XX_BOOT_ICR))
|
||||
return -1;
|
||||
if (hdr->data[1] != (0xff & ~LPC32XX_BOOT_ICR))
|
||||
return -1;
|
||||
if (hdr->data[2] != (0xff & LPC32XX_BOOT_ICR))
|
||||
return -1;
|
||||
if (hdr->data[3] != (0xff & ~LPC32XX_BOOT_ICR))
|
||||
return -1;
|
||||
if (hdr->data[4] != (0xff & image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[5] != (0xff & ~image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[6] != (0xff & image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[7] != (0xff & ~image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[8] != (0xff & image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[9] != (0xff & ~image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[10] != (0xff & image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[11] != (0xff & ~image_size_in_pages))
|
||||
return -1;
|
||||
if (hdr->data[12] != LPC32XX_BOOT_BLOCK_OK)
|
||||
return -1;
|
||||
if (hdr->data[128] != LPC32XX_BOOT_BLOCK_OK)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_hdr_byte(struct nand_page_0_boot_header *hdr, int ofs)
|
||||
{
|
||||
printf("header[%d] = %02x\n", ofs, hdr->data[ofs]);
|
||||
}
|
||||
|
||||
static void lpc32xximage_print_header(const void *ptr)
|
||||
{
|
||||
struct nand_page_0_boot_header *hdr =
|
||||
(struct nand_page_0_boot_header *)ptr;
|
||||
int ofs;
|
||||
|
||||
for (ofs = 0; ofs <= 12; ofs++)
|
||||
print_hdr_byte(hdr, ofs);
|
||||
print_hdr_byte(hdr, 128);
|
||||
}
|
||||
|
||||
static void lpc32xximage_set_header(void *ptr, struct stat *sbuf, int ifd,
|
||||
struct image_tool_params *params)
|
||||
{
|
||||
struct nand_page_0_boot_header *hdr =
|
||||
(struct nand_page_0_boot_header *)ptr;
|
||||
|
||||
/* turn image size from bytes to NAND pages, page 0 included */
|
||||
int image_size_in_pages = ((sbuf->st_size
|
||||
+ LPC32XX_BOOT_NAND_PAGESIZE - 1)
|
||||
/ LPC32XX_BOOT_NAND_PAGESIZE);
|
||||
|
||||
/* fill header -- default byte value is 0x00, not 0xFF */
|
||||
memset((void *)hdr, 0, sizeof(*hdr));
|
||||
hdr->data[0] = (hdr->data[2] = 0xff & LPC32XX_BOOT_ICR);
|
||||
hdr->data[1] = (hdr->data[3] = 0xff & ~LPC32XX_BOOT_ICR);
|
||||
hdr->data[4] = (hdr->data[6] = (hdr->data[8]
|
||||
= (hdr->data[10] = 0xff & image_size_in_pages)));
|
||||
hdr->data[5] = (hdr->data[7] = (hdr->data[9]
|
||||
= (hdr->data[11] = 0xff & ~image_size_in_pages)));
|
||||
hdr->data[12] = (hdr->data[128] = LPC32XX_BOOT_BLOCK_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* lpc32xximage parameters
|
||||
*/
|
||||
U_BOOT_IMAGE_TYPE(
|
||||
lpc32xximage,
|
||||
"LPC32XX Boot Image",
|
||||
sizeof(lpc32xximage_header),
|
||||
(void *)&lpc32xximage_header,
|
||||
NULL,
|
||||
lpc32xximage_verify_header,
|
||||
lpc32xximage_print_header,
|
||||
lpc32xximage_set_header,
|
||||
NULL,
|
||||
lpc32xximage_check_image_types,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
Loading…
Add table
Reference in a new issue