From 11553b0de8992ded6240d034bd49f561d17bea53 Mon Sep 17 00:00:00 2001 From: Daniel Schwierzeck Date: Thu, 13 Jun 2013 01:18:02 +0200 Subject: MIPS: add support for Lantiq XWAY SoCs Signed-off-by: Luka Perkov Signed-off-by: Daniel Schwierzeck --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,13 @@ /u-boot.sb /u-boot.bd /u-boot.geany +/u-boot.bin.lzma +/u-boot.bin.lzo +/u-boot.ltq.lzma.norspl +/u-boot.ltq.lzo.norspl +/u-boot.ltq.norspl +/u-boot.lzma.img +/u-boot.lzo.img # # Generated files --- a/Makefile +++ b/Makefile @@ -435,6 +435,12 @@ $(obj)u-boot.bin: $(obj)u-boot $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ $(BOARD_SIZE_CHECK) +$(obj)u-boot.bin.lzma: $(obj)u-boot.bin + cat $< | lzma -9 -f - > $@ + +$(obj)u-boot.bin.lzo: $(obj)u-boot.bin + cat $< | lzop -9 -f - > $@ + $(obj)u-boot.ldr: $(obj)u-boot $(CREATE_LDR_ENV) $(LDR) -T $(CONFIG_BFIN_CPU) -c $@ $< $(LDR_FLAGS) @@ -454,13 +460,23 @@ ifndef CONFIG_SYS_UBOOT_START CONFIG_SYS_UBOOT_START := 0 endif -$(obj)u-boot.img: $(obj)u-boot.bin - $(obj)tools/mkimage -A $(ARCH) -T firmware -C none \ +define GEN_UBOOT_IMAGE + $(obj)tools/mkimage -A $(ARCH) -T firmware -C $(1) \ -O u-boot -a $(CONFIG_SYS_TEXT_BASE) \ -e $(CONFIG_SYS_UBOOT_START) \ -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \ sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \ -d $< $@ +endef + +$(obj)u-boot.img: $(obj)u-boot.bin + $(call GEN_UBOOT_IMAGE,none) + +$(obj)u-boot.lzma.img: $(obj)u-boot.bin.lzma + $(call GEN_UBOOT_IMAGE,lzma) + +$(obj)u-boot.lzo.img: $(obj)u-boot.bin.lzo + $(call GEN_UBOOT_IMAGE,lzo) $(obj)u-boot.imx: $(obj)u-boot.bin depend $(MAKE) -C $(SRCTREE)/arch/arm/imx-common $(OBJTREE)/u-boot.imx @@ -571,6 +587,27 @@ $(obj)u-boot-img-spl-at-end.bin: $(obj)s conv=notrunc 2>/dev/null cat $(obj)u-boot-pad.img $(obj)spl/u-boot-spl.bin > $@ +$(obj)u-boot.ltq.sfspl: $(obj)u-boot.img $(obj)spl/u-boot-spl.bin + $(obj)tools/ltq-boot-image -t sfspl -e $(CONFIG_SPL_TEXT_BASE) \ + -s $(obj)spl/u-boot-spl.bin -u $< -o $@ + +$(obj)u-boot.ltq.lzo.sfspl: $(obj)u-boot.lzo.img $(obj)spl/u-boot-spl.bin + $(obj)tools/ltq-boot-image -t sfspl -e $(CONFIG_SPL_TEXT_BASE) \ + -s $(obj)spl/u-boot-spl.bin -u $< -o $@ + +$(obj)u-boot.ltq.lzma.sfspl: $(obj)u-boot.lzma.img $(obj)spl/u-boot-spl.bin + $(obj)tools/ltq-boot-image -t sfspl -e $(CONFIG_SPL_TEXT_BASE) \ + -s $(obj)spl/u-boot-spl.bin -u $< -o $@ + +$(obj)u-boot.ltq.norspl: $(obj)u-boot.img $(obj)spl/u-boot-spl.bin + cat $(obj)spl/u-boot-spl.bin $< > $@ + +$(obj)u-boot.ltq.lzo.norspl: $(obj)u-boot.lzo.img $(obj)spl/u-boot-spl.bin + cat $(obj)spl/u-boot-spl.bin $< > $@ + +$(obj)u-boot.ltq.lzma.norspl: $(obj)u-boot.lzma.img $(obj)spl/u-boot-spl.bin + cat $(obj)spl/u-boot-spl.bin $< > $@ + ifeq ($(CONFIG_SANDBOX),y) GEN_UBOOT = \ cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \ --- a/README +++ b/README @@ -468,6 +468,11 @@ The following options need to be configu CONF_CM_CACHABLE_CUW CONF_CM_CACHABLE_ACCELERATED + CONFIG_SYS_MIPS_CACHE_EXT_INIT + + Enable this to use extended cache initialization for recent + MIPS CPU cores. + CONFIG_SYS_XWAY_EBU_BOOTCFG Special option for Lantiq XWAY SoCs for booting from NOR flash. --- a/arch/mips/config.mk +++ b/arch/mips/config.mk @@ -45,9 +45,13 @@ PLATFORM_CPPFLAGS += -DCONFIG_MIPS -D__M # On the other hand, we want PIC in the U-Boot code to relocate it from ROM # to RAM. $28 is always used as gp. # -PLATFORM_CPPFLAGS += -G 0 -mabicalls -fpic $(ENDIANNESS) +PF_ABICALLS ?= -mabicalls +PF_PIC ?= -fpic +PF_PIE ?= -pie + +PLATFORM_CPPFLAGS += -G 0 $(PF_ABICALLS) $(PF_PIC) $(ENDIANNESS) PLATFORM_CPPFLAGS += -msoft-float PLATFORM_LDFLAGS += -G 0 -static -n -nostdlib $(ENDIANNESS) PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections -LDFLAGS_FINAL += --gc-sections -pie +LDFLAGS_FINAL += --gc-sections $(PF_PIE) OBJCFLAGS += --remove-section=.dynsym --- a/arch/mips/cpu/mips32/cache.S +++ b/arch/mips/cpu/mips32/cache.S @@ -29,7 +29,11 @@ */ #define MIPS_MAX_CACHE_SIZE 0x10000 +#ifdef CONFIG_SYS_MIPS_CACHE_EXT_INIT +#define INDEX_BASE 0x9fc00000 +#else #define INDEX_BASE CKSEG0 +#endif .macro cache_op op addr .set push @@ -65,7 +69,11 @@ */ LEAF(mips_init_icache) blez a1, 9f +#ifdef CONFIG_SYS_MIPS_CACHE_EXT_INIT + mtc0 zero, CP0_ITAGLO +#else mtc0 zero, CP0_TAGLO +#endif /* clear tag to invalidate */ PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, a1 @@ -90,7 +98,11 @@ LEAF(mips_init_icache) */ LEAF(mips_init_dcache) blez a1, 9f +#ifdef CONFIG_SYS_MIPS_CACHE_EXT_INIT + mtc0 zero, CP0_DTAGLO +#else mtc0 zero, CP0_TAGLO +#endif /* clear all tags */ PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, a1 --- /dev/null +++ b/arch/mips/cpu/mips32/danube/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (C) 2000-2011 Wolfgang Denk, DENX Software Engineering, wd@denx.de +# +# SPDX-License-Identifier: GPL-2.0+ +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(SOC).o + +COBJS-y += cgu.o chipid.o ebu.o mem.o pmu.o rcu.o +SOBJS-y += cgu_init.o mem_init.o + +COBJS := $(COBJS-y) +SOBJS := $(SOBJS-y) +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### --- /dev/null +++ b/arch/mips/cpu/mips32/danube/cgu.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define LTQ_CGU_SYS_DDR_MASK 0x0003 +#define LTQ_CGU_SYS_DDR_SHIFT 0 +#define LTQ_CGU_SYS_CPU0_MASK 0x000C +#define LTQ_CGU_SYS_CPU0_SHIFT 2 +#define LTQ_CGU_SYS_FPI_MASK 0x0040 +#define LTQ_CGU_SYS_FPI_SHIFT 6 + +struct ltq_cgu_regs { + u32 rsvd0; + u32 pll0_cfg; /* PLL0 config */ + u32 pll1_cfg; /* PLL1 config */ + u32 pll2_cfg; /* PLL2 config */ + u32 sys; /* System clock */ + u32 update; /* CGU update control */ + u32 if_clk; /* Interface clock */ + u32 osc_con; /* Update OSC Control */ + u32 smd; /* SDRAM Memory Control */ + u32 rsvd1[3]; + u32 pcm_cr; /* PCM control */ + u32 pci_cr; /* PCI clock control */ +}; + +static struct ltq_cgu_regs *ltq_cgu_regs = + (struct ltq_cgu_regs *) CKSEG1ADDR(LTQ_CGU_BASE); + +static inline u32 ltq_cgu_sys_readl(u32 mask, u32 shift) +{ + return (ltq_readl(<q_cgu_regs->sys) & mask) >> shift; +} + +unsigned long ltq_get_io_region_clock(void) +{ + u32 ddr_sel; + unsigned long clk; + + ddr_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_DDR_MASK, + LTQ_CGU_SYS_DDR_SHIFT); + + switch (ddr_sel) { + case 0: + clk = CLOCK_166_MHZ; + break; + case 1: + clk = CLOCK_133_MHZ; + break; + case 2: + clk = CLOCK_111_MHZ; + break; + case 3: + clk = CLOCK_83_MHZ; + break; + default: + clk = 0; + break; + } + + return clk; +} + +unsigned long ltq_get_cpu_clock(void) +{ + u32 cpu0_sel; + unsigned long clk; + + cpu0_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_CPU0_MASK, + LTQ_CGU_SYS_CPU0_SHIFT); + + switch (cpu0_sel) { + /* Same as PLL0 output (333,33 MHz) */ + case 0: + clk = CLOCK_333_MHZ; + break; + /* 1/1 fixed ratio to DDR clock */ + case 1: + clk = ltq_get_io_region_clock(); + break; + /* 1/2 fixed ratio to DDR clock */ + case 2: + clk = ltq_get_io_region_clock() << 1; + break; + default: + clk = 0; + break; + } + + return clk; +} + +unsigned long ltq_get_bus_clock(void) +{ + u32 fpi_sel; + unsigned long clk; + + fpi_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_FPI_MASK, + LTQ_CGU_SYS_FPI_SHIFT); + + if (fpi_sel) + /* Half the DDR clock */ + clk = ltq_get_io_region_clock() >> 1; + else + /* Same as DDR clock */ + clk = ltq_get_io_region_clock(); + + return clk; +} --- /dev/null +++ b/arch/mips/cpu/mips32/danube/cgu_init.S @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* RCU module register */ +#define LTQ_RCU_RST_REQ 0x0010 +#define LTQ_RCU_RST_STAT 0x0014 +#define LTQ_RCU_RST_REQ_VALUE 0x40000008 +#define LTQ_RCU_RST_STAT_XTAL_F 0x20000 + +/* CGU module register */ +#define LTQ_CGU_PLL0_CFG 0x0004 /* PLL0 config */ +#define LTQ_CGU_PLL1_CFG 0x0008 /* PLL1 config */ +#define LTQ_CGU_PLL2_CFG 0x000C /* PLL2 config */ +#define LTQ_CGU_SYS 0x0010 /* System clock */ + +/* Valid SYS.CPU0/1 values */ +#define LTQ_CGU_SYS_CPU0_SHIFT 2 +#define LTQ_CGU_SYS_CPU1_SHIFT 4 +#define LTQ_CGU_SYS_CPU_PLL0 0x0 +#define LTQ_CGU_SYS_CPU_DDR_EQUAL 0x1 +#define LTQ_CGU_SYS_CPU_DDR_TWICE 0x2 + +/* Valid SYS.DDR values */ +#define LTQ_CGU_SYS_DDR_SHIFT 0 +#define LTQ_CGU_SYS_DDR_167_MHZ 0x0 +#define LTQ_CGU_SYS_DDR_133_MHZ 0x1 +#define LTQ_CGU_SYS_DDR_111_MHZ 0x2 +#define LTQ_CGU_SYS_DDR_83_MHZ 0x3 + +/* Valid SYS.FPI values */ +#define LTQ_CGU_SYS_FPI_SHIFT 6 +#define LTQ_CGU_SYS_FPI_DDR_EQUAL 0x0 +#define LTQ_CGU_SYS_FPI_DDR_HALF 0x1 + +/* Valid SYS.PPE values */ +#define LTQ_CGU_SYS_PPE_SHIFT 7 +#define LTQ_CGU_SYS_PPE_266_MHZ 0x0 +#define LTQ_CGU_SYS_PPE_240_MHZ 0x1 +#define LTQ_CGU_SYS_PPE_222_MHZ 0x2 +#define LTQ_CGU_SYS_PPE_133_MHZ 0x3 + +#if (CONFIG_SYS_CLOCK_MODE == LTQ_CLK_CPU_333_DDR_167) +#define LTQ_CGU_SYS_CPU_CONFIG LTQ_CGU_SYS_CPU_DDR_TWICE +#define LTQ_CGU_SYS_DDR_CONFIG LTQ_CGU_SYS_DDR_167_MHZ +#define LTQ_CGU_SYS_FPI_CONFIG LTQ_CGU_SYS_FPI_DDR_HALF +#define LTQ_CGU_SYS_PPE_CONFIG LTQ_CGU_SYS_PPE_266_MHZ +#elif (CONFIG_SYS_CLOCK_MODE == LTQ_CLK_CPU_111_DDR_111) +#define LTQ_CGU_SYS_CPU_CONFIG LTQ_CGU_SYS_CPU_DDR_EQUAL +#define LTQ_CGU_SYS_DDR_CONFIG LTQ_CGU_SYS_DDR_111_MHZ +#define LTQ_CGU_SYS_FPI_CONFIG LTQ_CGU_SYS_FPI_DDR_HALF +#define LTQ_CGU_SYS_PPE_CONFIG LTQ_CGU_SYS_PPE_133_MHZ +#else +#error "Invalid system clock configuration!" +#endif + +/* Build register values */ +#define LTQ_CGU_SYS_VALUE ((LTQ_CGU_SYS_PPE_CONFIG << \ + LTQ_CGU_SYS_PPE_SHIFT) | \ + (LTQ_CGU_SYS_FPI_CONFIG << \ + LTQ_CGU_SYS_FPI_SHIFT) | \ + (LTQ_CGU_SYS_CPU_CONFIG << \ + LTQ_CGU_SYS_CPU1_SHIFT) | \ + (LTQ_CGU_SYS_CPU_CONFIG << \ + LTQ_CGU_SYS_CPU0_SHIFT) | \ + LTQ_CGU_SYS_DDR_CONFIG) + +/* Reset values for PLL registers for usage with 35.328 MHz crystal */ +#define PLL0_35MHZ_CONFIG 0x9D861059 +#define PLL1_35MHZ_CONFIG 0x1A260CD9 +#define PLL2_35MHZ_CONFIG 0x8000f1e5 + +/* Reset values for PLL registers for usage with 36 MHz crystal */ +#define PLL0_36MHZ_CONFIG 0x1000125D +#define PLL1_36MHZ_CONFIG 0x1B1E0C99 +#define PLL2_36MHZ_CONFIG 0x8002f2a1 + +LEAF(ltq_cgu_init) + /* Load current CGU register value */ + li t0, (LTQ_CGU_BASE | KSEG1) + lw t1, LTQ_CGU_SYS(t0) + + /* Load target CGU register values */ + li t3, LTQ_CGU_SYS_VALUE + + /* Only update registers if values differ */ + beq t1, t3, finished + + /* + * Check whether the XTAL_F bit in RST_STAT register is set or not. + * This bit is latched in via pin strapping. If bit is set then + * clock source is a 36 MHz crystal. Otherwise a 35.328 MHz crystal. + */ + li t1, (LTQ_RCU_BASE | KSEG1) + lw t2, LTQ_RCU_RST_STAT(t1) + and t2, t2, LTQ_RCU_RST_STAT_XTAL_F + beq t2, LTQ_RCU_RST_STAT_XTAL_F, boot_36mhz + +boot_35mhz: + /* Configure PLL for 35.328 MHz */ + li t2, PLL0_35MHZ_CONFIG + sw t2, LTQ_CGU_PLL0_CFG(t0) + li t2, PLL1_35MHZ_CONFIG + sw t2, LTQ_CGU_PLL1_CFG(t0) + li t2, PLL2_35MHZ_CONFIG + sw t2, LTQ_CGU_PLL2_CFG(t0) + + b do_reset + +boot_36mhz: + /* Configure PLL for 36 MHz */ + li t2, PLL0_36MHZ_CONFIG + sw t2, LTQ_CGU_PLL0_CFG(t0) + li t2, PLL1_36MHZ_CONFIG + sw t2, LTQ_CGU_PLL1_CFG(t0) + li t2, PLL2_36MHZ_CONFIG + sw t2, LTQ_CGU_PLL2_CFG(t0) + +do_reset: + /* Store new clock config */ + sw t3, LTQ_CGU_SYS(t0) + + /* Perform software reset to activate new clock config */ + li t2, LTQ_RCU_RST_REQ_VALUE + sw t2, LTQ_RCU_RST_REQ(t1) + +wait_reset: + b wait_reset + +finished: + jr ra + + END(ltq_cgu_init) --- /dev/null +++ b/arch/mips/cpu/mips32/danube/chipid.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define LTQ_CHIPID_VERSION_SHIFT 28 +#define LTQ_CHIPID_VERSION_MASK (0xF << LTQ_CHIPID_VERSION_SHIFT) +#define LTQ_CHIPID_PNUM_SHIFT 12 +#define LTQ_CHIPID_PNUM_MASK (0xFFFF << LTQ_CHIPID_PNUM_SHIFT) + +struct ltq_chipid_regs { + u32 manid; /* Manufacturer identification */ + u32 chipid; /* Chip identification */ +}; + +static struct ltq_chipid_regs *ltq_chipid_regs = + (struct ltq_chipid_regs *) CKSEG1ADDR(LTQ_CHIPID_BASE); + +unsigned int ltq_chip_version_get(void) +{ + u32 chipid; + + chipid = ltq_readl(<q_chipid_regs->chipid); + + return (chipid & LTQ_CHIPID_VERSION_MASK) >> LTQ_CHIPID_VERSION_SHIFT; +} + +unsigned int ltq_chip_partnum_get(void) +{ + u32 chipid; + + chipid = ltq_readl(<q_chipid_regs->chipid); + + return (chipid & LTQ_CHIPID_PNUM_MASK) >> LTQ_CHIPID_PNUM_SHIFT; +} + +const char *ltq_chip_partnum_str(void) +{ + enum ltq_chip_partnum partnum = ltq_chip_partnum_get(); + + switch (partnum) { + case LTQ_SOC_DANUBE: + return "Danube"; + case LTQ_SOC_DANUBE_S: + return "Danube-S"; + case LTQ_SOC_TWINPASS: + return "Twinpass"; + default: + printf("Unknown partnum: %x\n", partnum); + } + + return ""; +} --- /dev/null +++ b/arch/mips/cpu/mips32/danube/config.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com +# +# SPDX-License-Identifier: GPL-2.0+ +# + +PF_CPPFLAGS_DANUBE := $(call cc-option,-mtune=24kec,) +PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_DANUBE) + +ifdef CONFIG_SPL_BUILD +PF_ABICALLS := -mno-abicalls +PF_PIC := -fno-pic +PF_PIE := +USE_PRIVATE_LIBGCC := yes +endif + +LIBS-y += $(CPUDIR)/lantiq-common/liblantiq-common.o + +ifndef CONFIG_SPL_BUILD +ifdef CONFIG_SYS_BOOT_NORSPL +ALL-y += $(obj)u-boot.ltq.norspl +ALL-$(CONFIG_SPL_LZO_SUPPORT) += $(obj)u-boot.ltq.lzo.norspl +ALL-$(CONFIG_SPL_LZMA_SUPPORT) += $(obj)u-boot.ltq.lzma.norspl +endif +endif --- /dev/null +++ b/arch/mips/cpu/mips32/danube/ebu.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define EBU_ADDRSEL_MASK(mask) ((mask & 0xf) << 4) +#define EBU_ADDRSEL_REGEN (1 << 0) + +#define EBU_CON_WRDIS (1 << 31) +#define EBU_CON_AGEN_DEMUX (0x0 << 24) +#define EBU_CON_AGEN_MUX (0x2 << 24) +#define EBU_CON_SETUP (1 << 22) +#define EBU_CON_WAIT_DIS (0x0 << 20) +#define EBU_CON_WAIT_ASYNC (0x1 << 20) +#define EBU_CON_WAIT_SYNC (0x2 << 20) +#define EBU_CON_WINV (1 << 19) +#define EBU_CON_PW_8BIT (0x0 << 16) +#define EBU_CON_PW_16BIT (0x1 << 16) +#define EBU_CON_ALEC(cycles) ((cycles & 0x3) << 14) +#define EBU_CON_BCGEN_CS (0x0 << 12) +#define EBU_CON_BCGEN_INTEL (0x1 << 12) +#define EBU_CON_BCGEN_MOTOROLA (0x2 << 12) +#define EBU_CON_WAITWRC(cycles) ((cycles & 0x7) << 8) +#define EBU_CON_WAITRDC(cycles) ((cycles & 0x3) << 6) +#define EBU_CON_HOLDC(cycles) ((cycles & 0x3) << 4) +#define EBU_CON_RECOVC(cycles) ((cycles & 0x3) << 2) +#define EBU_CON_CMULT_1 0x0 +#define EBU_CON_CMULT_4 0x1 +#define EBU_CON_CMULT_8 0x2 +#define EBU_CON_CMULT_16 0x3 + +#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) +#define ebu_region0_enable 1 +#else +#define ebu_region0_enable 0 +#endif + +#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH) +#define ebu_region1_enable 1 +#else +#define ebu_region1_enable 0 +#endif + +struct ltq_ebu_regs { + u32 clc; + u32 rsvd0[3]; + u32 con; + u32 rsvd1[3]; + u32 addr_sel_0; + u32 addr_sel_1; + u32 rsvd2[14]; + u32 con_0; + u32 con_1; +}; + +static struct ltq_ebu_regs *ltq_ebu_regs = + (struct ltq_ebu_regs *) CKSEG1ADDR(LTQ_EBU_BASE); + +void ltq_ebu_init(void) +{ + if (ebu_region0_enable) { + /* + * Map EBU region 0 to range 0x10000000-0x13ffffff and enable + * region control. This supports up to 32 MiB NOR flash in + * bank 0. + */ + ltq_writel(<q_ebu_regs->addr_sel_0, LTQ_EBU_REGION0_BASE | + EBU_ADDRSEL_MASK(1) | EBU_ADDRSEL_REGEN); + + ltq_writel(<q_ebu_regs->con_0, EBU_CON_AGEN_DEMUX | + EBU_CON_WAIT_DIS | EBU_CON_PW_16BIT | + EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL | + EBU_CON_WAITWRC(7) | EBU_CON_WAITRDC(3) | + EBU_CON_HOLDC(3) | EBU_CON_RECOVC(3) | + EBU_CON_CMULT_16); + } else + ltq_clrbits(<q_ebu_regs->addr_sel_0, EBU_ADDRSEL_REGEN); + + if (ebu_region1_enable) { + /* + * Map EBU region 1 to range 0x14000000-0x13ffffff and enable + * region control. This supports NAND flash in bank 1. + */ + ltq_writel(<q_ebu_regs->addr_sel_1, LTQ_EBU_REGION1_BASE | + EBU_ADDRSEL_MASK(3) | EBU_ADDRSEL_REGEN); + + ltq_writel(<q_ebu_regs->con_1, EBU_CON_AGEN_DEMUX | + EBU_CON_SETUP | EBU_CON_WAIT_DIS | EBU_CON_PW_8BIT | + EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL | + EBU_CON_WAITWRC(2) | EBU_CON_WAITRDC(2) | + EBU_CON_HOLDC(1) | EBU_CON_RECOVC(1) | + EBU_CON_CMULT_4); + } else + ltq_clrbits(<q_ebu_regs->addr_sel_1, EBU_ADDRSEL_REGEN); +} + +void *flash_swap_addr(unsigned long addr) +{ + return (void *)(addr ^ 2); +} --- /dev/null +++ b/arch/mips/cpu/mips32/danube/mem.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +static void *ltq_mc_ddr_base = (void *) CKSEG1ADDR(LTQ_MC_DDR_BASE); + +static inline u32 ltq_mc_dc_read(u32 index) +{ + return ltq_readl(ltq_mc_ddr_base + LTQ_MC_DDR_DC_OFFSET(index)); +} + +phys_size_t initdram(int board_type) +{ + u32 col, row, dc04, dc19, dc20; + + dc04 = ltq_mc_dc_read(4); + dc19 = ltq_mc_dc_read(19); + dc20 = ltq_mc_dc_read(20); + + row = (dc04 & 0xF) - ((dc19 & 0x700) >> 8); + col = ((dc04 & 0xF00) >> 8) - (dc20 & 0x7); + + return (1 << (row + col)) * 4 * 2; +} --- /dev/null +++ b/arch/mips/cpu/mips32/danube/mem_init.S @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* Must be configured in BOARDDIR */ +#include + +#define LTQ_MC_GEN_ERRCAUSE 0x0010 +#define LTQ_MC_GEN_ERRADDR 0x0020 +#define LTQ_MC_GEN_CON 0x0060 +#define LTQ_MC_GEN_STAT 0x0070 +#define LTQ_MC_GEN_CON_SRAM_DDR_ENABLE 0x5 +#define LTQ_MC_GEN_STAT_DLCK_PWRON 0xC + +#define LTQ_MC_DDR_DC03_MC_START 0x100 + + /* Store given value in MC DDR CCRx register */ + .macro dc_sw num, val + li t2, \val + sw t2, LTQ_MC_DDR_DC_OFFSET(\num)(t1) + .endm + +LEAF(ltq_mem_init) + /* Load MC General and MC DDR module base */ + li t0, (LTQ_MC_GEN_BASE | KSEG1) + li t1, (LTQ_MC_DDR_BASE | KSEG1) + + /* Clear access error log registers */ + sw zero, LTQ_MC_GEN_ERRCAUSE(t0) + sw zero, LTQ_MC_GEN_ERRADDR(t0) + + /* Enable DDR and SRAM module in memory controller */ + li t2, LTQ_MC_GEN_CON_SRAM_DDR_ENABLE + sw t2, LTQ_MC_GEN_CON(t0) + + /* Clear start bit of DDR memory controller */ + sw zero, LTQ_MC_DDR_DC_OFFSET(3)(t1) + + /* Init memory controller registers with values ddr_settings.h */ + dc_sw 0, MC_DC00_VALUE + dc_sw 1, MC_DC01_VALUE + dc_sw 2, MC_DC02_VALUE + dc_sw 4, MC_DC04_VALUE + dc_sw 5, MC_DC05_VALUE + dc_sw 6, MC_DC06_VALUE + dc_sw 7, MC_DC07_VALUE + dc_sw 8, MC_DC08_VALUE + dc_sw 9, MC_DC09_VALUE + + dc_sw 10, MC_DC10_VALUE + dc_sw 11, MC_DC11_VALUE + dc_sw 12, MC_DC12_VALUE + dc_sw 13, MC_DC13_VALUE + dc_sw 14, MC_DC14_VALUE + dc_sw 15, MC_DC15_VALUE + dc_sw 16, MC_DC16_VALUE + dc_sw 17, MC_DC17_VALUE + dc_sw 18, MC_DC18_VALUE + dc_sw 19, MC_DC19_VALUE + + dc_sw 20, MC_DC20_VALUE + dc_sw 21, MC_DC21_VALUE + dc_sw 22, MC_DC22_VALUE + dc_sw 23, MC_DC23_VALUE + dc_sw 24, MC_DC24_VALUE + dc_sw 25, MC_DC25_VALUE + dc_sw 26, MC_DC26_VALUE + dc_sw 27, MC_DC27_VALUE + dc_sw 28, MC_DC28_VALUE + dc_sw 29, MC_DC29_VALUE + + dc_sw 30, MC_DC30_VALUE + dc_sw 31, MC_DC31_VALUE + dc_sw 32, MC_DC32_VALUE + dc_sw 33, MC_DC33_VALUE + dc_sw 34, MC_DC34_VALUE + dc_sw 35, MC_DC35_VALUE + dc_sw 36, MC_DC36_VALUE + dc_sw 37, MC_DC37_VALUE + dc_sw 38, MC_DC38_VALUE + dc_sw 39, MC_DC39_VALUE + + dc_sw 40, MC_DC40_VALUE + dc_sw 41, MC_DC41_VALUE + dc_sw 42, MC_DC42_VALUE + dc_sw 43, MC_DC43_VALUE + dc_sw 44, MC_DC44_VALUE + dc_sw 45, MC_DC45_VALUE + dc_sw 46, MC_DC46_VALUE + + /* Set start bit of DDR memory controller */ + li t2, LTQ_MC_DDR_DC03_MC_START + sw t2, LTQ_MC_DDR_DC_OFFSET(3)(t1) + + /* Wait until DLL has locked and core is ready for data transfers */ +wait_ready: + lw t2, LTQ_MC_GEN_STAT(t0) + li t3, LTQ_MC_GEN_STAT_DLCK_PWRON + and t2, t3 + bne t2, t3, wait_ready + +finished: + jr ra + + END(ltq_mem_init) --- /dev/null +++ b/arch/mips/cpu/mips32/danube/pmu.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define LTQ_PMU_PWDCR_RESERVED 0xFD0C001C + +#define LTQ_PMU_PWDCR_TDM (1 << 25) +#define LTQ_PMU_PWDCR_PPE_ENET0 (1 << 23) +#define LTQ_PMU_PWDCR_PPE_ENET1 (1 << 22) +#define LTQ_PMU_PWDCR_PPE_TC (1 << 21) +#define LTQ_PMU_PWDCR_DEU (1 << 20) +#define LTQ_PMU_PWDCR_UART1 (1 << 17) +#define LTQ_PMU_PWDCR_SDIO (1 << 16) +#define LTQ_PMU_PWDCR_AHB (1 << 15) +#define LTQ_PMU_PWDCR_FPI0 (1 << 14) +#define LTQ_PMU_PWDCR_PPE (1 << 13) +#define LTQ_PMU_PWDCR_GPTC (1 << 12) +#define LTQ_PMU_PWDCR_LEDC (1 << 11) +#define LTQ_PMU_PWDCR_EBU (1 << 10) +#define LTQ_PMU_PWDCR_DSL (1 << 9) +#define LTQ_PMU_PWDCR_SPI (1 << 8) +#define LTQ_PMU_PWDCR_UART0 (1 << 7) +#define LTQ_PMU_PWDCR_USB (1 << 6) +#define LTQ_PMU_PWDCR_DMA (1 << 5) +#define LTQ_PMU_PWDCR_FPI1 (1 << 1) +#define LTQ_PMU_PWDCR_USB_PHY (1 << 0) + +struct ltq_pmu_regs { + u32 rsvd0[7]; + u32 pwdcr; + u32 sr; + u32 pwdcr1; + u32 sr1; +}; + +static struct ltq_pmu_regs *ltq_pmu_regs = + (struct ltq_pmu_regs *) CKSEG1ADDR(LTQ_PMU_BASE); + +u32 ltq_pm_map(enum ltq_pm_modules module) +{ + u32 val; + + switch (module) { + case LTQ_PM_CORE: + val = LTQ_PMU_PWDCR_UART1 | LTQ_PMU_PWDCR_FPI0 | + LTQ_PMU_PWDCR_LEDC | LTQ_PMU_PWDCR_EBU; + break; + case LTQ_PM_DMA: + val = LTQ_PMU_PWDCR_DMA; + break; + case LTQ_PM_ETH: + val = LTQ_PMU_PWDCR_PPE_ENET0 | LTQ_PMU_PWDCR_PPE_TC | + LTQ_PMU_PWDCR_PPE; + break; + case LTQ_PM_SPI: + val = LTQ_PMU_PWDCR_SPI; + break; + default: + val = 0; + break; + } + + return val; +} + +int ltq_pm_enable(enum ltq_pm_modules module) +{ + const unsigned long timeout = 1000; + unsigned long timebase; + u32 sr, val; + + val = ltq_pm_map(module); + if (unlikely(!val)) + return 1; + + ltq_clrbits(<q_pmu_regs->pwdcr, val); + + timebase = get_timer(0); + + do { + sr = ltq_readl(<q_pmu_regs->sr); + if (~sr & val) + return 0; + } while (get_timer(timebase) < timeout); + + return 1; +} + +int ltq_pm_disable(enum ltq_pm_modules module) +{ + u32 val; + + val = ltq_pm_map(module); + if (unlikely(!val)) + return 1; + + ltq_setbits(<q_pmu_regs->pwdcr, val); + + return 0; +} + +void ltq_pmu_init(void) +{ + u32 set, clr; + + clr = ltq_pm_map(LTQ_PM_CORE); + set = ~(LTQ_PMU_PWDCR_RESERVED | clr); + + ltq_clrsetbits(<q_pmu_regs->pwdcr, clr, set); +} --- /dev/null +++ b/arch/mips/cpu/mips32/danube/rcu.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define LTQ_RCU_RD_SRST (1 << 30) /* Global SW Reset */ +#define LTQ_RCU_RD_MC (1 << 14) /* Memory Controller */ +#define LTQ_RCU_RD_PCI (1 << 13) /* PCI core */ +#define LTQ_RCU_RD_DFE_AFE (1 << 12) /* Voice DFE/AFE */ +#define LTQ_RCU_RD_DSL_AFE (1 << 11) /* DSL AFE */ +#define LTQ_RCU_RD_SDIO (1 << 10) /* SDIO core */ +#define LTQ_RCU_RD_DMA (1 << 9) /* DMA core */ +#define LTQ_RCU_RD_PPE (1 << 8) /* PPE core */ +#define LTQ_RCU_RD_ARC_DFE (1 << 7) /* ARC/DFE core */ +#define LTQ_RCU_RD_AHB (1 << 6) /* AHB bus */ +#define LTQ_RCU_RD_ENET_MAC1 (1 << 5) /* Ethernet MAC1 */ +#define LTQ_RCU_RD_USB (1 << 4) /* USB and Phy core */ +#define LTQ_RCU_RD_CPU1 (1 << 3) /* CPU1 subsystem */ +#define LTQ_RCU_RD_FPI (1 << 2) /* FPI bus */ +#define LTQ_RCU_RD_CPU0 (1 << 1) /* CPU0 subsystem */ +#define LTQ_RCU_RD_HRST (1 << 0) /* HW reset via HRST pin */ + +#define LTQ_RCU_STAT_BOOT_SHIFT 18 +#define LTQ_RCU_STAT_BOOT_MASK (0x7 << LTQ_RCU_STAT_BOOT_SHIFT) + +struct ltq_rcu_regs { + u32 rsvd0[4]; + u32 req; /* Reset request */ + u32 stat; /* Reset status */ + u32 usb_cfg; /* USB configure */ + u32 rsvd1[2]; + u32 pci_rdy; /* PCI boot ready */ +}; + +static struct ltq_rcu_regs *ltq_rcu_regs = + (struct ltq_rcu_regs *) CKSEG1ADDR(LTQ_RCU_BASE); + +u32 ltq_reset_map(enum ltq_reset_modules module) +{ + u32 val; + + switch (module) { + case LTQ_RESET_CORE: + case LTQ_RESET_SOFT: + val = LTQ_RCU_RD_SRST | LTQ_RCU_RD_CPU1; + break; + case LTQ_RESET_DMA: + val = LTQ_RCU_RD_DMA; + break; + case LTQ_RESET_ETH: + val = LTQ_RCU_RD_PPE; + break; + case LTQ_RESET_HARD: + val = LTQ_RCU_RD_HRST; + break; + default: + val = 0; + break; + } + + return val; +} + +int ltq_reset_activate(enum ltq_reset_modules module) +{ + u32 val; + + val = ltq_reset_map(module); + if (unlikely(!val)) + return 1; + + ltq_setbits(<q_rcu_regs->req, val); + + return 0; +} + +int ltq_reset_deactivate(enum ltq_reset_modules module) +{ + u32 val; + + val = ltq_reset_map(module); + if (unlikely(!val)) + return 1; + + ltq_clrbits(<q_rcu_regs->req, val); + + return 0; +} + +enum ltq_boot_select ltq_boot_select(void) +{ + u32 stat; + unsigned int bootstrap; + + stat = ltq_readl(<q_rcu_regs->stat); + bootstrap = (stat & LTQ_RCU_STAT_BOOT_MASK) >> LTQ_RCU_STAT_BOOT_SHIFT; + + switch (bootstrap) { + case 0: + return BOOT_NOR_NO_BOOTROM; + case 1: + return BOOT_NOR; + case 2: + return BOOT_MII0; + case 3: + return BOOT_PCI; + case 4: + return BOOT_UART; + case 5: + return BOOT_SPI; + case 6: + return BOOT_NAND; + case 7: + return BOOT_RMII0; + default: + return BOOT_UNKNOWN; + } +} --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/Makefile @@ -0,0 +1,34 @@ +# +# Copyright (C) 2000-2011 Wolfgang Denk, DENX Software Engineering, wd@denx.de +# +# SPDX-License-Identifier: GPL-2.0+ +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)liblantiq-common.o + +START = start.o +COBJS-y = cpu.o pmu.o +COBJS-$(CONFIG_SPL_BUILD) += spl.o +SOBJS-y = lowlevel_init.o + +COBJS := $(COBJS-y) +SOBJS := $(SOBJS-y) +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/cpu.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static const char ltq_bootsel_strings[][16] = { + "NOR", + "NOR w/o BootROM", + "UART", + "UART w/o EEPROM", + "SPI", + "NAND", + "PCI", + "MII0", + "RMII0", + "RGMII1", + "unknown", +}; + +const char *ltq_boot_select_str(void) +{ enum ltq_boot_select bootsel = ltq_boot_select(); + + if (bootsel > BOOT_UNKNOWN) + bootsel = BOOT_UNKNOWN; + + return ltq_bootsel_strings[bootsel]; +} + +void ltq_chip_print_info(void) +{ + char buf[32]; + + printf("SoC: Lantiq %s v1.%u\n", ltq_chip_partnum_str(), + ltq_chip_version_get()); + printf("CPU: %s MHz\n", strmhz(buf, ltq_get_cpu_clock())); + printf("IO: %s MHz\n", strmhz(buf, ltq_get_io_region_clock())); + printf("BUS: %s MHz\n", strmhz(buf, ltq_get_bus_clock())); + printf("BOOT: %s\n", ltq_boot_select_str()); +} + +int arch_cpu_init(void) +{ + ltq_pmu_init(); + ltq_ebu_init(); + + return 0; +} + +void _machine_restart(void) +{ + ltq_reset_activate(LTQ_RESET_CORE); +} --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/lowlevel_init.S @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +NESTED(lowlevel_init, 0, ra) + move t8, ra + + la t7, ltq_cgu_init + jalr t7 + + la t7, ltq_mem_init + jalr t7 + + jr t8 + END(lowlevel_init) --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/pmu.c @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/spl.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_LTQ_SPL_CONSOLE) +#define spl_has_console 1 + +#if defined(CONFIG_LTQ_SPL_DEBUG) +#define spl_has_debug 1 +#else +#define spl_has_debug 0 +#endif + +#else +#define spl_has_console 0 +#define spl_has_debug 0 +#endif + +#define spl_debug(fmt, args...) \ + do { \ + if (spl_has_debug) \ + printf(fmt, ##args); \ + } while (0) + +#define spl_puts(msg) \ + do { \ + if (spl_has_console) \ + puts(msg); \ + } while (0) + +#if defined(CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH) && defined(CONFIG_SYS_BOOT_SFSPL) +#define spl_boot_spi_flash 1 +#else +#define spl_boot_spi_flash 0 +#ifndef CONFIG_SPL_SPI_BUS +#define CONFIG_SPL_SPI_BUS 0 +#endif +#ifndef CONFIG_SPL_SPI_CS +#define CONFIG_SPL_SPI_CS 0 +#endif +#ifndef CONFIG_SPL_SPI_MAX_HZ +#define CONFIG_SPL_SPI_MAX_HZ 0 +#endif +#ifndef CONFIG_SPL_SPI_MODE +#define CONFIG_SPL_SPI_MODE 0 +#endif +#endif + +#if defined(CONFIG_LTQ_SUPPORT_SPL_NOR_FLASH) && defined(CONFIG_SYS_BOOT_NORSPL) +#define spl_boot_nor_flash 1 +#else +#define spl_boot_nor_flash 0 +#endif + +#define spl_sync() __asm__ __volatile__("sync"); + +struct spl_image { + ulong data_addr; + ulong entry_addr; + ulong data_size; + ulong entry_size; + ulong data_crc; + u8 comp; +}; + +DECLARE_GLOBAL_DATA_PTR; + +/* Emulated malloc area needed for LZMA allocator in BSS */ +static u8 *spl_mem_ptr __maybe_unused; +static size_t spl_mem_size __maybe_unused; + +static int spl_is_comp_lzma(const struct spl_image *spl) +{ +#if defined(CONFIG_LTQ_SPL_COMP_LZMA) + return spl->comp == IH_COMP_LZMA; +#else + return 0; +#endif +} + +static int spl_is_comp_lzo(const struct spl_image *spl) +{ +#if defined(CONFIG_LTQ_SPL_COMP_LZO) + return spl->comp == IH_COMP_LZO; +#else + return 0; +#endif +} + +static int spl_is_compressed(const struct spl_image *spl) +{ + if (spl_is_comp_lzma(spl)) + return 1; + + if (spl_is_comp_lzo(spl)) + return 1; + + return 0; +} + +static void spl_console_init(void) +{ + if (!spl_has_console) + return; + + gd->flags |= GD_FLG_RELOC; + gd->baudrate = CONFIG_BAUDRATE; + + serial_init(); + + gd->have_console = 1; + + spl_puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \ + U_BOOT_TIME ")\n"); +} + +static int spl_parse_image(const image_header_t *hdr, struct spl_image *spl) +{ + spl_puts("SPL: checking U-Boot image\n"); + + if (!image_check_magic(hdr)) { + spl_puts("SPL: invalid magic\n"); + return -1; + } + + if (!image_check_hcrc(hdr)) { + spl_puts("SPL: invalid header CRC\n"); + return -1; + } + + spl->data_addr += image_get_header_size(); + spl->entry_addr = image_get_load(hdr); + spl->data_size = image_get_data_size(hdr); + spl->data_crc = image_get_dcrc(hdr); + spl->comp = image_get_comp(hdr); + + spl_debug("SPL: data %08lx, size %lu, entry %08lx, comp %u\n", + spl->data_addr, spl->data_size, spl->entry_addr, spl->comp); + + return 0; +} + +static int spl_check_data(const struct spl_image *spl, ulong loadaddr) +{ + ulong dcrc = crc32(0, (unsigned char *)loadaddr, spl->data_size); + + if (dcrc != spl->data_crc) { + spl_puts("SPL: invalid data CRC\n"); + return 0; + } + + return 1; +} + +static void *spl_lzma_alloc(void *p, size_t size) +{ + u8 *ret; + + if (size > spl_mem_size) + return NULL; + + ret = spl_mem_ptr; + spl_mem_ptr += size; + spl_mem_size -= size; + + return ret; +} + +static void spl_lzma_free(void *p, void *addr) +{ +} + +static int spl_copy_image(struct spl_image *spl) +{ + spl_puts("SPL: copying U-Boot to RAM\n"); + + memcpy((void *) spl->entry_addr, (const void *) spl->data_addr, + spl->data_size); + + spl->entry_size = spl->data_size; + + return 0; +} + +static int spl_uncompress_lzma(struct spl_image *spl, unsigned long loadaddr) +{ + SRes res; + const Byte *prop = (const Byte *) loadaddr; + const Byte *src = (const Byte *) loadaddr + LZMA_PROPS_SIZE + + sizeof(uint64_t); + Byte *dest = (Byte *) spl->entry_addr; + SizeT dest_len = 0xFFFFFFFF; + SizeT src_len = spl->data_size - LZMA_PROPS_SIZE; + ELzmaStatus status = 0; + ISzAlloc alloc; + + spl_puts("SPL: decompressing U-Boot with LZMA\n"); + + alloc.Alloc = spl_lzma_alloc; + alloc.Free = spl_lzma_free; + spl_mem_ptr = (u8 *) CONFIG_SPL_MALLOC_BASE; + spl_mem_size = CONFIG_SPL_MALLOC_MAX_SIZE; + + res = LzmaDecode(dest, &dest_len, src, &src_len, prop, LZMA_PROPS_SIZE, + LZMA_FINISH_ANY, &status, &alloc); + if (res != SZ_OK) + return 1; + + spl->entry_size = dest_len; + + return 0; +} + +static int spl_uncompress_lzo(struct spl_image *spl, unsigned long loadaddr) +{ + size_t len; + int ret; + + spl_puts("SPL: decompressing U-Boot with LZO\n"); + + ret = lzop_decompress( + (const unsigned char*) loadaddr, spl->data_size, + (unsigned char *) spl->entry_addr, &len); + + spl->entry_size = len; + + return ret; +} + +static int spl_uncompress(struct spl_image *spl, unsigned long loadaddr) +{ + int ret; + + if (spl_is_comp_lzma(spl)) + ret = spl_uncompress_lzma(spl, loadaddr); + else if (spl_is_comp_lzo(spl)) + ret = spl_uncompress_lzo(spl, loadaddr); + else + ret = 1; + + return ret; +} + +static int spl_load_spi_flash(struct spl_image *spl) +{ + struct spi_flash sf = { 0 }; + image_header_t hdr; + int ret; + unsigned long loadaddr; + + /* + * Image format: + * + * - 12 byte non-volatile bootstrap header + * - SPL binary + * - 12 byte non-volatile bootstrap header + * - 64 byte U-Boot mkimage header + * - U-Boot binary + */ + spl->data_addr = image_copy_end() - CONFIG_SPL_TEXT_BASE + 24; + + spl_puts("SPL: probing SPI flash\n"); + + spi_init(); + ret = spl_spi_flash_probe(&sf); + if (ret) + return ret; + + spl_debug("SPL: reading image header at offset %lx\n", spl->data_addr); + + ret = spi_flash_read(&sf, spl->data_addr, sizeof(hdr), &hdr); + if (ret) + return ret; + + spl_debug("SPL: checking image header at offset %lx\n", spl->data_addr); + + ret = spl_parse_image(&hdr, spl); + if (ret) + return ret; + + if (spl_is_compressed(spl)) + loadaddr = CONFIG_LOADADDR; + else + loadaddr = spl->entry_addr; + + spl_puts("SPL: loading U-Boot to RAM\n"); + + ret = spi_flash_read(&sf, spl->data_addr, spl->data_size, + (void *) loadaddr); + + if (!spl_check_data(spl, loadaddr)) + return -1; + + if (spl_is_compressed(spl)) + ret = spl_uncompress(spl, loadaddr); + + return ret; +} + +static int spl_load_nor_flash(struct spl_image *spl) +{ + const image_header_t *hdr; + int ret; + + /* + * Image format: + * + * - SPL binary + * - 64 byte U-Boot mkimage header + * - U-Boot binary + */ + spl->data_addr = image_copy_end(); + hdr = (const image_header_t *) image_copy_end(); + + spl_debug("SPL: checking image header at address %p\n", hdr); + + ret = spl_parse_image(hdr, spl); + if (ret) + return ret; + + if (spl_is_compressed(spl)) + ret = spl_uncompress(spl, spl->data_addr); + else + ret = spl_copy_image(spl); + + return ret; +} + +static int spl_load(struct spl_image *spl) +{ + int ret; + + if (spl_boot_spi_flash) + ret = spl_load_spi_flash(spl); + else if (spl_boot_nor_flash) + ret = spl_load_nor_flash(spl); + else + ret = 1; + + return ret; +} + +void __noreturn spl_lantiq_init(void) +{ + void (*uboot)(void) __noreturn; + struct spl_image spl; + gd_t gd_data; + int ret; + + gd = &gd_data; + barrier(); + memset((void *)gd, 0, sizeof(gd_t)); + + spl_console_init(); + + spl_debug("SPL: initializing\n"); + +#if 0 + spl_debug("CP0_CONFIG: %08x\n", read_c0_config()); + spl_debug("CP0_CONFIG1: %08x\n", read_c0_config1()); + spl_debug("CP0_CONFIG2: %08x\n", read_c0_config2()); + spl_debug("CP0_CONFIG3: %08x\n", read_c0_config3()); + spl_debug("CP0_CONFIG6: %08x\n", read_c0_config6()); + spl_debug("CP0_CONFIG7: %08x\n", read_c0_config7()); + spl_debug("CP0_STATUS: %08x\n", read_c0_status()); + spl_debug("CP0_PRID: %08x\n", read_c0_prid()); +#endif + + board_early_init_f(); + timer_init(); + + memset(&spl, 0, sizeof(spl)); + + ret = spl_load(&spl); + if (ret) + goto hang; + + spl_debug("SPL: U-Boot entry %08lx\n", spl.entry_addr); + spl_puts("SPL: jumping to U-Boot\n"); + + flush_cache(spl.entry_addr, spl.entry_size); + spl_sync(); + + uboot = (void *) spl.entry_addr; + uboot(); + +hang: + spl_puts("SPL: cannot start U-Boot\n"); + + for (;;) + ; +} --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/start.S @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define S_PRIdCoID 16 /* Company ID (R) */ +#define M_PRIdCoID (0xff << S_PRIdCoID) +#define S_PRIdImp 8 /* Implementation ID (R) */ +#define M_PRIdImp (0xff << S_PRIdImp) + +#define K_CacheAttrCWTnWA 0 /* Cacheable, write-thru, no write allocate */ +#define K_CacheAttrCWTWA 1 /* Cacheable, write-thru, write allocate */ +#define K_CacheAttrU 2 /* Uncached */ +#define K_CacheAttrC 3 /* Cacheable */ +#define K_CacheAttrCN 3 /* Cacheable, non-coherent */ +#define K_CacheAttrCCE 4 /* Cacheable, coherent, exclusive */ +#define K_CacheAttrCCS 5 /* Cacheable, coherent, shared */ +#define K_CacheAttrCCU 6 /* Cacheable, coherent, update */ +#define K_CacheAttrUA 7 /* Uncached accelerated */ + +#define S_ConfigK23 28 /* Kseg2/3 coherency algorithm (FM MMU only) (R/W) */ +#define M_ConfigK23 (0x7 << S_ConfigK23) +#define W_ConfigK23 3 +#define S_ConfigKU 25 /* Kuseg coherency algorithm (FM MMU only) (R/W) */ +#define M_ConfigKU (0x7 << S_ConfigKU) +#define W_ConfigKU 3 + +#define S_ConfigMM 18 /* Merge mode (implementation specific) */ +#define M_ConfigMM (0x1 << S_ConfigMM) + +#define S_StatusBEV 22 /* Enable Boot Exception Vectors (R/W) */ +#define M_StatusBEV (0x1 << S_StatusBEV) + +#define S_StatusFR 26 /* Enable 64-bit FPRs (R/W) */ +#define M_StatusFR (0x1 << S_StatusFR) + +#define S_ConfigK0 0 /* Kseg0 coherency algorithm (R/W) */ +#define M_ConfigK0 (0x7 << S_ConfigK0) + +#define CONFIG0_MIPS32_64_MSK 0x8000ffff +#define STATUS_MIPS32_64_MSK 0xfffcffff + +#define STATUS_MIPS24K 0 +#define CONFIG0_MIPS24K ((K_CacheAttrCN << S_ConfigK23) |\ + (K_CacheAttrCN << S_ConfigKU) |\ + (M_ConfigMM)) + +#define STATUS_MIPS34K 0 +#define CONFIG0_MIPS34K ((K_CacheAttrCN << S_ConfigK23) |\ + (K_CacheAttrCN << S_ConfigKU) |\ + (M_ConfigMM)) + +#define STATUS_MIPS32_64 (M_StatusBEV | M_StatusFR) +#define CONFIG0_MIPS32_64 (K_CacheAttrCN << S_ConfigK0) + +#ifdef CONFIG_SOC_XWAY_DANUBE +#define CONFIG0_LANTIQ (CONFIG0_MIPS24K | CONFIG0_MIPS32_64) +#define STATUS_LANTIQ (STATUS_MIPS24K | STATUS_MIPS32_64) +#endif + +#ifdef CONFIG_SOC_XWAY_VRX200 +#define CONFIG0_LANTIQ (CONFIG0_MIPS34K | CONFIG0_MIPS32_64) +#define STATUS_LANTIQ (STATUS_MIPS34K | STATUS_MIPS32_64) +#endif + + + .set noreorder + + .globl _start + .text +_start: + /* Entry point */ + b main + nop + + /* Lantiq SoC Boot config word */ + .org 0x10 +#ifdef CONFIG_SYS_XWAY_EBU_BOOTCFG + .word CONFIG_SYS_XWAY_EBU_BOOTCFG +#else + .word 0 +#endif + .word 0 + + .align 4 +main: + + /* Init Timer */ + mtc0 zero, CP0_COUNT + mtc0 zero, CP0_COMPARE + + /* Setup MIPS24K/MIPS34K specifics (implementation dependent fields) */ + mfc0 t0, CP0_CONFIG + li t1, CONFIG0_MIPS32_64_MSK + and t0, t1 + li t1, CONFIG0_LANTIQ + or t0, t1 + mtc0 t0, CP0_CONFIG + + mfc0 t0, CP0_STATUS + li t1, STATUS_MIPS32_64_MSK + and t0, t1 + li t1, STATUS_LANTIQ + or t0, t1 + mtc0 t0, CP0_STATUS + + /* Initialize CGU */ + la t9, ltq_cgu_init + jalr t9 + nop + + /* Initialize memory controller */ + la t9, ltq_mem_init + jalr t9 + nop + + /* Initialize caches... */ + la t9, mips_cache_reset + jalr t9 + nop + + /* Clear BSS */ + la t1, __bss_start + la t2, __bss_end + sub t1, 4 +1: + addi t1, 4 + bltl t1, t2, 1b + sw zero, 0(t1) + + /* Setup stack pointer and force alignment on a 16 byte boundary */ + li t0, (CONFIG_SPL_STACK_BASE & ~0xF) + la sp, 0(t0) + + la t9, spl_lantiq_init + jr t9 + nop --- /dev/null +++ b/arch/mips/cpu/mips32/lantiq-common/u-boot-spl.lds @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +MEMORY { .spl_mem : ORIGIN = CONFIG_SPL_TEXT_BASE, \ + LENGTH = CONFIG_SPL_MAX_SIZE } +MEMORY { .bss_mem : ORIGIN = CONFIG_SPL_BSS_BASE, \ + LENGTH = CONFIG_SPL_BSS_MAX_SIZE } + +OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradlittlemips") +OUTPUT_ARCH(mips) +ENTRY(_start) +SECTIONS +{ + . = ALIGN(4); + .text : { + *(.text*) + } > .spl_mem + + . = ALIGN(4); + .rodata : { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } > .spl_mem + + . = ALIGN(4); + .data : { + *(SORT_BY_ALIGNMENT(.data*)) + *(SORT_BY_ALIGNMENT(.sdata*)) + } > .spl_mem + + . = ALIGN(4); + __image_copy_end = .; + uboot_end_data = .; + + .bss : { + __bss_start = .; + *(.bss*) + *(.sbss*) + . = ALIGN(4); + __bss_end = .; + } > .bss_mem + + . = ALIGN(4); + __end = .; + uboot_end = .; +} --- a/arch/mips/cpu/mips32/start.S +++ b/arch/mips/cpu/mips32/start.S @@ -105,7 +105,7 @@ reset: mtc0 zero, CP0_COUNT mtc0 zero, CP0_COMPARE -#ifndef CONFIG_SKIP_LOWLEVEL_INIT +#if !defined(CONFIG_SKIP_LOWLEVEL_INIT) || defined(CONFIG_SYS_DISABLE_CACHE) /* CONFIG0 register */ li t0, CONF_CM_UNCACHED mtc0 t0, CP0_CONFIG --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/Makefile @@ -0,0 +1,32 @@ +# +# Copyright (C) 2000-2011 Wolfgang Denk, DENX Software Engineering, wd@denx.de +# +# SPDX-License-Identifier: GPL-2.0+ +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(SOC).o + +COBJS-y += cgu.o chipid.o dcdc.o ebu.o gphy.o mem.o pmu.o rcu.o +SOBJS-y += cgu_init.o mem_init.o +SOBJS-y += gphy_fw.o + +COBJS := $(COBJS-y) +SOBJS := $(SOBJS-y) +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/cgu.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define LTQ_CGU_PLL1_PLLN_SHIFT 6 +#define LTQ_CGU_PLL1_PLLN_MASK (0x3F << LTQ_CGU_PLL1_PLLN_SHIFT) +#define LTQ_CGU_PLL1_PLLM_SHIFT 2 +#define LTQ_CGU_PLL1_PLLM_MASK (0xF << LTQ_CGU_PLL1_PLLM_SHIFT) +#define LTQ_CGU_PLL1_PLLL (1 << 1) +#define LTQ_CGU_PLL1_PLL_EN 1 + +#define LTQ_CGU_SYS_OCP_SHIFT 0 +#define LTQ_CGU_SYS_OCP_MASK (0x3 << LTQ_CGU_SYS_OCP_SHIFT) +#define LTQ_CGU_SYS_CPU_SHIFT 4 +#define LTQ_CGU_SYS_CPU_MASK (0xF << LTQ_CGU_SYS_CPU_SHIFT) + +#define LTQ_CGU_UPDATE 1 + +#define LTQ_CGU_IFCLK_GPHY_SEL_SHIFT 2 +#define LTQ_CGU_IFCLK_GPHY_SEL_MASK (0x7 << LTQ_CGU_IFCLK_GPHY_SEL_SHIFT) + +struct ltq_cgu_regs { + u32 rsvd0; + u32 pll0_cfg; /* PLL0 config */ + u32 pll1_cfg; /* PLL1 config */ + u32 sys; /* System clock */ + u32 clk_fsr; /* Clock frequency select */ + u32 clk_gsr; /* Clock gating status */ + u32 clk_gcr0; /* Clock gating control 0 */ + u32 clk_gcr1; /* Clock gating control 1 */ + u32 update; /* CGU update control */ + u32 if_clk; /* Interface clock */ + u32 ddr; /* DDR memory control */ + u32 ct1_sr; /* CT status 1 */ + u32 ct_kval; /* CT K value */ + u32 pcm_cr; /* PCM control */ + u32 pci_cr; /* PCI clock control */ + u32 rsvd1; + u32 gphy1_cfg; /* GPHY1 config */ + u32 gphy0_cfg; /* GPHY0 config */ + u32 rsvd2[6]; + u32 pll2_cfg; /* PLL2 config */ +}; + +static struct ltq_cgu_regs *ltq_cgu_regs = + (struct ltq_cgu_regs *) CKSEG1ADDR(LTQ_CGU_BASE); + +static inline u32 ltq_cgu_sys_readl(u32 mask, u32 shift) +{ + return (ltq_readl(<q_cgu_regs->sys) & mask) >> shift; +} + +unsigned long ltq_get_io_region_clock(void) +{ + unsigned int ocp_sel; + unsigned long clk, cpu_clk; + + cpu_clk = ltq_get_cpu_clock(); + + ocp_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_OCP_MASK, + LTQ_CGU_SYS_OCP_SHIFT); + + switch (ocp_sel) { + case 0: + /* OCP ratio 1 */ + clk = cpu_clk; + break; + case 2: + /* OCP ratio 2 */ + clk = cpu_clk / 2; + break; + case 3: + /* OCP ratio 2.5 */ + clk = (cpu_clk * 2) / 5; + break; + case 4: + /* OCP ratio 3 */ + clk = cpu_clk / 3; + break; + default: + clk = 0; + break; + } + + return clk; +} + +unsigned long ltq_get_cpu_clock(void) +{ + unsigned int cpu_sel; + unsigned long clk; + + cpu_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_CPU_MASK, + LTQ_CGU_SYS_CPU_SHIFT); + + switch (cpu_sel) { + case 0: + clk = CLOCK_600_MHZ; + break; + case 1: + clk = CLOCK_500_MHZ; + break; + case 2: + clk = CLOCK_393_MHZ; + break; + case 3: + clk = CLOCK_333_MHZ; + break; + case 5: + case 6: + clk = CLOCK_197_MHZ; + break; + case 7: + clk = CLOCK_166_MHZ; + break; + case 4: + case 8: + case 9: + clk = CLOCK_125_MHZ; + break; + default: + clk = 0; + break; + } + + return clk; +} + +unsigned long ltq_get_bus_clock(void) +{ + return ltq_get_io_region_clock(); +} + +void ltq_cgu_gphy_clk_src(enum ltq_gphy_clk clk) +{ + ltq_clrbits(<q_cgu_regs->if_clk, LTQ_CGU_IFCLK_GPHY_SEL_MASK); + ltq_setbits(<q_cgu_regs->if_clk, clk << LTQ_CGU_IFCLK_GPHY_SEL_SHIFT); +} + +static inline int ltq_cgu_pll1_locked(void) +{ + u32 pll1_cfg = ltq_readl(<q_cgu_regs->pll1_cfg); + + return pll1_cfg & LTQ_CGU_PLL1_PLLL; +} + +static inline void ltq_cgu_pll1_restart(unsigned m, unsigned n) +{ + u32 pll1_cfg; + + ltq_clrbits(<q_cgu_regs->pll1_cfg, LTQ_CGU_PLL1_PLL_EN); + ltq_setbits(<q_cgu_regs->update, LTQ_CGU_UPDATE); + + pll1_cfg = ltq_readl(<q_cgu_regs->pll1_cfg); + pll1_cfg &= ~(LTQ_CGU_PLL1_PLLN_MASK | LTQ_CGU_PLL1_PLLM_MASK); + pll1_cfg |= n << LTQ_CGU_PLL1_PLLN_SHIFT; + pll1_cfg |= m << LTQ_CGU_PLL1_PLLM_SHIFT; + pll1_cfg |= LTQ_CGU_PLL1_PLL_EN; + ltq_writel(<q_cgu_regs->pll1_cfg, pll1_cfg); + ltq_setbits(<q_cgu_regs->update, LTQ_CGU_UPDATE); + + __udelay(1000); +} + +/* + * From chapter 9 in errata sheet: + * + * Under certain condition, the PLL1 may failed to enter into lock + * status by hardware default N, M setting. + * + * Since system always starts from PLL0, the system software can run + * and re-program the PLL1 settings. + */ +static void ltq_cgu_pll1_init(void) +{ + unsigned i; + const unsigned pll1_m[] = { 1, 2, 3, 4 }; + const unsigned pll1_n[] = { 21, 32, 43, 54 }; + + /* Check if PLL1 has locked with hardware default settings */ + if (ltq_cgu_pll1_locked()) + return; + + for (i = 0; i < 4; i++) { + ltq_cgu_pll1_restart(pll1_m[i], pll1_n[i]); + + if (ltq_cgu_pll1_locked()) + goto done; + } + +done: + /* Restart with hardware default values M=5, N=64 */ + ltq_cgu_pll1_restart(5, 64); +} + +void ltq_pll_init(void) +{ + ltq_cgu_pll1_init(); +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/cgu_init.S @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* RCU module register */ +#define LTQ_RCU_RST_REQ 0x0010 /* Reset request */ +#define LTQ_RCU_RST_REQ_VALUE ((1 << 14) | (1 << 1)) + +/* CGU module register */ +#define LTQ_CGU_PLL0_CFG 0x0004 /* PLL0 config */ +#define LTQ_CGU_PLL1_CFG 0x0008 /* PLL1 config */ +#define LTQ_CGU_PLL2_CFG 0x0060 /* PLL2 config */ +#define LTQ_CGU_SYS 0x000C /* System clock */ +#define LTQ_CGU_CLK_FSR 0x0010 /* Clock frequency select */ +#define LTQ_CGU_UPDATE 0x0020 /* Clock update control */ + +/* Valid SYS.CPU values */ +#define LTQ_CGU_SYS_CPU_SHIFT 4 +#define LTQ_CGU_SYS_CPU_600_MHZ 0x0 +#define LTQ_CGU_SYS_CPU_500_MHZ 0x1 +#define LTQ_CGU_SYS_CPU_393_MHZ 0x2 +#define LTQ_CGU_SYS_CPU_333_MHZ 0x3 +#define LTQ_CGU_SYS_CPU_197_MHZ 0x5 +#define LTQ_CGU_SYS_CPU_166_MHZ 0x7 +#define LTQ_CGU_SYS_CPU_125_MHZ 0x9 + +/* Valid SYS.OCP values */ +#define LTQ_CGU_SYS_OCP_SHIFT 0 +#define LTQ_CGU_SYS_OCP_1 0x0 +#define LTQ_CGU_SYS_OCP_2 0x2 +#define LTQ_CGU_SYS_OCP_2_5 0x3 +#define LTQ_CGU_SYS_OCP_3 0x4 + +/* Valid CLK_FSR.ETH values */ +#define LTQ_CGU_CLK_FSR_ETH_SHIFT 24 +#define LTQ_CGU_CLK_FSR_ETH_50_MHZ 0x0 +#define LTQ_CGU_CLK_FSR_ETH_25_MHZ 0x1 +#define LTQ_CGU_CLK_FSR_ETH_2_5_MHZ 0x2 +#define LTQ_CGU_CLK_FSR_ETH_125_MHZ 0x3 + +/* Valid CLK_FSR.PPE values */ +#define LTQ_CGU_CLK_FSR_PPE_SHIFT 16 +#define LTQ_CGU_CLK_FSR_PPE_500_MHZ 0x0 /* Overclock frequency */ +#define LTQ_CGU_CLK_FSR_PPE_450_MHZ 0x1 /* High frequency */ +#define LTQ_CGU_CLK_FSR_PPE_400_MHZ 0x2 /* Low frequency */ + +#if (CONFIG_SYS_CLOCK_MODE == LTQ_CLK_CPU_500_DDR_250) +#define LTQ_CGU_SYS_CPU_CONFIG LTQ_CGU_SYS_CPU_500_MHZ +#define LTQ_CGU_SYS_OCP_CONFIG LTQ_CGU_SYS_OCP_2 +#define LTQ_CGU_CLK_FSR_ETH_CONFIG LTQ_CGU_CLK_FSR_ETH_125_MHZ +#define LTQ_CGU_CLK_FSR_PPE_CONFIG LTQ_CGU_CLK_FSR_PPE_450_MHZ +#else +#error "Invalid system clock configuration!" +#endif + +/* Build register values */ +#define LTQ_CGU_SYS_VALUE ((LTQ_CGU_SYS_CPU_CONFIG << \ + LTQ_CGU_SYS_CPU_SHIFT) | \ + LTQ_CGU_SYS_OCP_CONFIG) + +#define LTQ_CGU_CLK_FSR_VALUE ((LTQ_CGU_CLK_FSR_ETH_CONFIG << \ + LTQ_CGU_CLK_FSR_ETH_SHIFT) | \ + (LTQ_CGU_CLK_FSR_PPE_CONFIG << \ + LTQ_CGU_CLK_FSR_PPE_SHIFT)) + + .set noreorder + +LEAF(ltq_cgu_init) + /* Load current CGU register values */ + li t0, (LTQ_CGU_BASE | KSEG1) + lw t1, LTQ_CGU_SYS(t0) + lw t2, LTQ_CGU_CLK_FSR(t0) + + /* Load target CGU register values */ + li t3, LTQ_CGU_SYS_VALUE + li t4, LTQ_CGU_CLK_FSR_VALUE + + /* Only update registers if values differ */ + bne t1, t3, update + nop + beq t2, t4, finished + nop + +update: + /* Store target register values */ + sw t3, LTQ_CGU_SYS(t0) + sw t4, LTQ_CGU_CLK_FSR(t0) + + /* Perform software reset to activate new clock config */ +#if 0 + li t0, (LTQ_RCU_BASE | KSEG1) + lw t1, LTQ_RCU_RST_REQ(t0) + or t1, LTQ_RCU_RST_REQ_VALUE + sw t1, LTQ_RCU_RST_REQ(t0) +#else + li t1, 1 + sw t1, LTQ_CGU_UPDATE(t0) +#endif + +#if 0 +wait_reset: + b wait_reset + nop +#endif + +finished: + jr ra + nop + + END(ltq_cgu_init) --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/chipid.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define LTQ_CHIPID_VERSION_SHIFT 28 +#define LTQ_CHIPID_VERSION_MASK (0x7 << LTQ_CHIPID_VERSION_SHIFT) +#define LTQ_CHIPID_PNUM_SHIFT 12 +#define LTQ_CHIPID_PNUM_MASK (0xFFFF << LTQ_CHIPID_PNUM_SHIFT) + +struct ltq_chipid_regs { + u32 manid; /* Manufacturer identification */ + u32 chipid; /* Chip identification */ +}; + +static struct ltq_chipid_regs *ltq_chipid_regs = + (struct ltq_chipid_regs *) CKSEG1ADDR(LTQ_CHIPID_BASE); + +unsigned int ltq_chip_version_get(void) +{ + u32 chipid; + + chipid = ltq_readl(<q_chipid_regs->chipid); + + return (chipid & LTQ_CHIPID_VERSION_MASK) >> LTQ_CHIPID_VERSION_SHIFT; +} + +unsigned int ltq_chip_partnum_get(void) +{ + u32 chipid; + + chipid = ltq_readl(<q_chipid_regs->chipid); + + return (chipid & LTQ_CHIPID_PNUM_MASK) >> LTQ_CHIPID_PNUM_SHIFT; +} + +const char *ltq_chip_partnum_str(void) +{ + enum ltq_chip_partnum partnum = ltq_chip_partnum_get(); + + switch (partnum) { + case LTQ_SOC_VRX268: + case LTQ_SOC_VRX268_2: + return "VRX268"; + case LTQ_SOC_VRX288: + case LTQ_SOC_VRX288_2: + return "VRX288"; + case LTQ_SOC_GRX288: + case LTQ_SOC_GRX288_2: + return "GRX288"; + default: + printf("Unknown partnum: %x\n", partnum); + } + + return ""; +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/config.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com +# +# SPDX-License-Identifier: GPL-2.0+ +# + +PF_CPPFLAGS_XRX := $(call cc-option,-mtune=34kc,) +PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_XRX) + +ifdef CONFIG_SPL_BUILD +PF_ABICALLS := -mno-abicalls +PF_PIC := -fno-pic +PF_PIE := +USE_PRIVATE_LIBGCC := yes +endif + +LIBS-y += $(CPUDIR)/lantiq-common/liblantiq-common.o + +ifndef CONFIG_SPL_BUILD +ifdef CONFIG_SYS_BOOT_SFSPL +ALL-y += $(obj)u-boot.ltq.sfspl +ALL-$(CONFIG_SPL_LZO_SUPPORT) += $(obj)u-boot.ltq.lzo.sfspl +ALL-$(CONFIG_SPL_LZMA_SUPPORT) += $(obj)u-boot.ltq.lzma.sfspl +endif +ifdef CONFIG_SYS_BOOT_NORSPL +ALL-y += $(obj)u-boot.ltq.norspl +ALL-$(CONFIG_SPL_LZO_SUPPORT) += $(obj)u-boot.ltq.lzo.norspl +ALL-$(CONFIG_SPL_LZMA_SUPPORT) += $(obj)u-boot.ltq.lzma.norspl +endif +endif --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/dcdc.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define LTQ_DCDC_CLK_SET0_CLK_SEL_P (1 << 6) +#define LTQ_DCDC_CLK_SET1_SEL_DIV25 (1 << 5) +#define LTQ_DCDC_CONF_TEST_DIG_PID_FREEZE (1 << 5) + +struct ltq_dcdc_regs { + u8 b0_coeh; /* Coefficient b0 */ + u8 b0_coel; /* Coefficient b0 */ + u8 b1_coeh; /* Coefficient b1 */ + u8 b1_coel; /* Coefficient b1 */ + u8 b2_coeh; /* Coefficient b2 */ + u8 b2_coel; /* Coefficient b2 */ + u8 clk_set0; /* Clock setup */ + u8 clk_set1; /* Clock setup */ + u8 pwm_confh; /* Configure PWM */ + u8 pwm_confl; /* Configure PWM */ + u8 bias_vreg0; /* Bias and regulator setup */ + u8 bias_vreg1; /* Bias and regulator setup */ + u8 adc_gen0; /* ADC and general control */ + u8 adc_gen1; /* ADC and general control */ + u8 adc_con0; /* ADC and general config */ + u8 adc_con1; /* ADC and general config */ + u8 conf_test_ana; /* not documented */ + u8 conf_test_dig; /* not documented */ + u8 dcdc_status; /* not documented */ + u8 pid_status; /* not documented */ + u8 duty_cycle; /* not documented */ + u8 non_ov_delay; /* not documented */ + u8 analog_gain; /* not documented */ + u8 duty_cycle_max_sat; /* not documented */ + u8 duty_cycle_min_sat; /* not documented */ + u8 duty_cycle_max; /* not documented */ + u8 duty_cycle_min; /* not documented */ + u8 error_max; /* not documented */ + u8 error_read; /* not documented */ + u8 delay_deglitch; /* not documented */ + u8 latch_control; /* not documented */ + u8 rsvd[240]; + u8 osc_conf; /* OSC general config */ + u8 osc_stat; /* OSC general status */ +}; + +static struct ltq_dcdc_regs *ltq_dcdc_regs = + (struct ltq_dcdc_regs *) CKSEG1ADDR(LTQ_DCDC_BASE); + +void ltq_dcdc_init(unsigned int dig_ref) +{ + u8 dig_ref_cur, val; + + /* Set duty cycle max sat. to 70/90, enable PID freeze */ + ltq_writeb(<q_dcdc_regs->duty_cycle_max_sat, 0x5A); + ltq_writeb(<q_dcdc_regs->duty_cycle_min_sat, 0x46); + val = ltq_readb(<q_dcdc_regs->conf_test_dig); + val |= LTQ_DCDC_CONF_TEST_DIG_PID_FREEZE; + ltq_writeb(<q_dcdc_regs->conf_test_dig, val); + + /* Program new coefficients */ + ltq_writeb(<q_dcdc_regs->b0_coeh, 0x00); + ltq_writeb(<q_dcdc_regs->b0_coel, 0x00); + ltq_writeb(<q_dcdc_regs->b1_coeh, 0xFF); + ltq_writeb(<q_dcdc_regs->b1_coel, 0xE6); + ltq_writeb(<q_dcdc_regs->b2_coeh, 0x00); + ltq_writeb(<q_dcdc_regs->b2_coel, 0x1B); + ltq_writeb(<q_dcdc_regs->non_ov_delay, 0x8B); + + /* Set duty cycle max sat. to 60/108, disable PID freeze */ + ltq_writeb(<q_dcdc_regs->duty_cycle_max_sat, 0x6C); + ltq_writeb(<q_dcdc_regs->duty_cycle_min_sat, 0x3C); + val = ltq_readb(<q_dcdc_regs->conf_test_dig); + val &= ~LTQ_DCDC_CONF_TEST_DIG_PID_FREEZE; + ltq_writeb(<q_dcdc_regs->conf_test_dig, val); + + /* Init clock and DLL settings */ + val = ltq_readb(<q_dcdc_regs->clk_set0); + val |= LTQ_DCDC_CLK_SET0_CLK_SEL_P; + ltq_writeb(<q_dcdc_regs->clk_set0, val); + val = ltq_readb(<q_dcdc_regs->clk_set1); + val |= LTQ_DCDC_CLK_SET1_SEL_DIV25; + ltq_writeb(<q_dcdc_regs->clk_set1, val); + ltq_writeb(<q_dcdc_regs->pwm_confh, 0xF9); + + wmb(); + + /* Adapt value of digital reference of DCDC converter */ + dig_ref_cur = ltq_readb(<q_dcdc_regs->bias_vreg1); + + while (dig_ref_cur != dig_ref) { + if (dig_ref >= dig_ref_cur) + dig_ref_cur++; + else if (dig_ref < dig_ref_cur) + dig_ref_cur--; + + ltq_writeb(<q_dcdc_regs->bias_vreg1, dig_ref_cur); + __udelay(1000); + } +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/ebu.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define EBU_ADDRSEL_MASK(mask) ((mask & 0xf) << 4) +#define EBU_ADDRSEL_REGEN (1 << 0) + +#define EBU_CON_WRDIS (1 << 31) +#define EBU_CON_AGEN_DEMUX (0x0 << 24) +#define EBU_CON_AGEN_MUX (0x2 << 24) +#define EBU_CON_SETUP (1 << 22) +#define EBU_CON_WAIT_DIS (0x0 << 20) +#define EBU_CON_WAIT_ASYNC (0x1 << 20) +#define EBU_CON_WAIT_SYNC (0x2 << 20) +#define EBU_CON_WINV (1 << 19) +#define EBU_CON_PW_8BIT (0x0 << 16) +#define EBU_CON_PW_16BIT (0x1 << 16) +#define EBU_CON_ALEC(cycles) ((cycles & 0x3) << 14) +#define EBU_CON_BCGEN_CS (0x0 << 12) +#define EBU_CON_BCGEN_INTEL (0x1 << 12) +#define EBU_CON_BCGEN_MOTOROLA (0x2 << 12) +#define EBU_CON_WAITWRC(cycles) ((cycles & 0x7) << 8) +#define EBU_CON_WAITRDC(cycles) ((cycles & 0x3) << 6) +#define EBU_CON_HOLDC(cycles) ((cycles & 0x3) << 4) +#define EBU_CON_RECOVC(cycles) ((cycles & 0x3) << 2) +#define EBU_CON_CMULT_1 0x0 +#define EBU_CON_CMULT_4 0x1 +#define EBU_CON_CMULT_8 0x2 +#define EBU_CON_CMULT_16 0x3 + +#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) +#define ebu_region0_enable 1 +#else +#define ebu_region0_enable 0 +#endif + +#if ((CONFIG_SYS_MAX_FLASH_BANKS == 2) && defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) ) +#define ebu_region0_addrsel_mask 3 +#else +#define ebu_region0_addrsel_mask 1 +#endif + +#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH) || ((CONFIG_SYS_MAX_FLASH_BANKS == 2) && defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) ) +#define ebu_region1_enable 1 +#else +#define ebu_region1_enable 0 +#endif + +struct ltq_ebu_regs { + u32 clc; + u32 rsvd0; + u32 id; + u32 rsvd1; + u32 con; + u32 rsvd2[3]; + u32 addr_sel_0; + u32 addr_sel_1; + u32 addr_sel_2; + u32 addr_sel_3; + u32 rsvd3[12]; + u32 con_0; + u32 con_1; + u32 con_2; + u32 con_3; +}; + +static struct ltq_ebu_regs *ltq_ebu_regs = + (struct ltq_ebu_regs *) CKSEG1ADDR(LTQ_EBU_BASE); + +void ltq_ebu_init(void) +{ + if (ebu_region0_enable) { + /* + * Map EBU region 0 to range 0x10000000-0x13ffffff and enable + * region control. This supports up to 32 MiB NOR flash in + * bank 0. + */ + ltq_writel(<q_ebu_regs->addr_sel_0, LTQ_EBU_REGION0_BASE | + EBU_ADDRSEL_MASK(ebu_region0_addrsel_mask) | EBU_ADDRSEL_REGEN); + + ltq_writel(<q_ebu_regs->con_0, EBU_CON_AGEN_DEMUX | + EBU_CON_WAIT_DIS | EBU_CON_PW_16BIT | + EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL | + EBU_CON_WAITWRC(7) | EBU_CON_WAITRDC(3) | + EBU_CON_HOLDC(3) | EBU_CON_RECOVC(3) | + EBU_CON_CMULT_16); + } else + ltq_clrbits(<q_ebu_regs->addr_sel_0, EBU_ADDRSEL_REGEN); + + if (ebu_region1_enable) { + /* + * Map EBU region 1 to range 0x14000000-0x13ffffff and enable + * region control. This supports NAND flash in bank 1. (and NOR flash in bank 2) + */ + ltq_writel(<q_ebu_regs->addr_sel_1, LTQ_EBU_REGION1_BASE | + EBU_ADDRSEL_MASK(3) | EBU_ADDRSEL_REGEN); + + if (ebu_region0_addrsel_mask == 1) + ltq_writel(<q_ebu_regs->con_1, EBU_CON_AGEN_DEMUX | + EBU_CON_SETUP | EBU_CON_WAIT_DIS | EBU_CON_PW_8BIT | + EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL | + EBU_CON_WAITWRC(2) | EBU_CON_WAITRDC(2) | + EBU_CON_HOLDC(1) | EBU_CON_RECOVC(1) | + EBU_CON_CMULT_4); + + if (ebu_region0_addrsel_mask == 3) + ltq_writel(<q_ebu_regs->con_1, EBU_CON_AGEN_DEMUX | + EBU_CON_WAIT_DIS | EBU_CON_PW_16BIT | + EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL | + EBU_CON_WAITWRC(7) | EBU_CON_WAITRDC(3) | + EBU_CON_HOLDC(3) | EBU_CON_RECOVC(3) | + EBU_CON_CMULT_16); + } else + ltq_clrbits(<q_ebu_regs->addr_sel_1, EBU_ADDRSEL_REGEN); +} + +void *flash_swap_addr(unsigned long addr) +{ + return (void *)(addr ^ 2); +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/gphy.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static inline void ltq_gphy_decompress(const void *fw_start, const void *fw_end, + ulong dst_addr) +{ + const ulong fw_len = (ulong) fw_end - (ulong) fw_start; + const ulong addr = CKSEG1ADDR(dst_addr); + + debug("ltq_gphy_decompress: addr %08lx, fw_start %p, fw_end %p\n", + addr, fw_start, fw_end); + + SizeT lzma_len = 65536; + int ret = lzmaBuffToBuffDecompress( + (unsigned char *)addr, &lzma_len, + (unsigned char *)fw_start, fw_len); +} + +void ltq_gphy_phy11g_a1x_load(ulong addr) +{ + extern ulong __ltq_fw_phy11g_a1x_start; + extern ulong __ltq_fw_phy11g_a1x_end; + + ltq_gphy_decompress(&__ltq_fw_phy11g_a1x_start, + &__ltq_fw_phy11g_a1x_end, + addr); +} + +void ltq_gphy_phy11g_a2x_load(ulong addr) +{ + extern ulong __ltq_fw_phy11g_a2x_start; + extern ulong __ltq_fw_phy11g_a2x_end; + + ltq_gphy_decompress(&__ltq_fw_phy11g_a2x_start, + &__ltq_fw_phy11g_a2x_end, + addr); +} + +void ltq_gphy_phy22f_a1x_load(ulong addr) +{ + extern ulong __ltq_fw_phy22f_a1x_start; + extern ulong __ltq_fw_phy22f_a1x_end; + + ltq_gphy_decompress(&__ltq_fw_phy22f_a1x_start, + &__ltq_fw_phy22f_a1x_end, + addr); +} + +void ltq_gphy_phy22f_a2x_load(ulong addr) +{ + extern ulong __ltq_fw_phy22f_a2x_start; + extern ulong __ltq_fw_phy22f_a2x_end; + + ltq_gphy_decompress(&__ltq_fw_phy22f_a2x_start, + &__ltq_fw_phy22f_a2x_end, + addr); +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/gphy_fw.S @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + + .section .rodata.__ltq_fw_phy11g_a1x +EXPORT(__ltq_fw_phy11g_a1x_start) + .incbin "fw_phy11g_a1x.blob" +EXPORT(__ltq_fw_phy11g_a1x_end) + + .section .rodata.__ltq_fw_phy11g_a2x +EXPORT(__ltq_fw_phy11g_a2x_start) + .incbin "fw_phy11g_a2x.blob" +EXPORT(__ltq_fw_phy11g_a2x_end) + + .section .rodata.__ltq_fw_phy22f_a1x +EXPORT(__ltq_fw_phy22f_a1x_start) + .incbin "fw_phy22f_a1x.blob" +EXPORT(__ltq_fw_phy22f_a1x_end) + + .section .rodata.__ltq_fw_phy22f_a2x +EXPORT(__ltq_fw_phy22f_a2x_start) + .incbin "fw_phy22f_a2x.blob" +EXPORT(__ltq_fw_phy22f_a2x_end) --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/mem.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define LTQ_CCR03_EIGHT_BANK_MODE (1 << 0) +#define LTQ_CCR08_CS_MAP_SHIFT 24 +#define LTQ_CCR08_CS_MAP_MASK (0x3 << LTQ_CCR08_CS_MAP_SHIFT) +#define LTQ_CCR11_COLUMN_SIZE_SHIFT 24 +#define LTQ_CCR11_COLUMN_SIZE_MASK (0x7 << LTQ_CCR11_COLUMN_SIZE_SHIFT) +#define LTQ_CCR11_ADDR_PINS_MASK 0x7 +#define LTQ_CCR15_MAX_COL_REG_SHIFT 24 +#define LTQ_CCR15_MAX_COL_REG_MASK (0xF << LTQ_CCR15_MAX_COL_REG_SHIFT) +#define LTQ_CCR16_MAX_ROW_REG_MASK 0xF + +static void *ltq_mc_ddr_base = (void *) CKSEG1ADDR(LTQ_MC_DDR_BASE); + +static inline u32 ltq_mc_ccr_read(u32 index) +{ + return ltq_readl(ltq_mc_ddr_base + LTQ_MC_DDR_CCR_OFFSET(index)); +} + +phys_size_t initdram(int board_type) +{ + u32 max_col_reg, max_row_reg, column_size, addr_pins; + u32 banks, cs_map; + phys_size_t size; + + banks = (ltq_mc_ccr_read(3) & LTQ_CCR03_EIGHT_BANK_MODE) ? 8 : 4; + + cs_map = (ltq_mc_ccr_read(8) & LTQ_CCR08_CS_MAP_MASK) >> + LTQ_CCR08_CS_MAP_SHIFT; + + column_size = (ltq_mc_ccr_read(11) & LTQ_CCR11_COLUMN_SIZE_MASK) >> + LTQ_CCR11_COLUMN_SIZE_SHIFT; + + addr_pins = ltq_mc_ccr_read(11) & LTQ_CCR11_ADDR_PINS_MASK; + + max_col_reg = (ltq_mc_ccr_read(15) & LTQ_CCR15_MAX_COL_REG_MASK) >> + LTQ_CCR15_MAX_COL_REG_SHIFT; + + max_row_reg = ltq_mc_ccr_read(16) & LTQ_CCR16_MAX_ROW_REG_MASK; + + /* + * size (bytes) = 2 ^ rowsize * 2 ^ colsize * banks * chipselects + * * datawidth (bytes) + */ + size = (2 << (max_col_reg - column_size - 1)) * + (2 << (max_row_reg - addr_pins - 1)) * banks * cs_map * 2; + + return size; +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/mem_init.S @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* Must be configured in BOARDDIR */ +#include + +#define LTQ_MC_DDR_START (1 << 8) +#define LTQ_MC_DDR_DLL_LOCK_IND 1 + +#define CCS_ALWAYS_LAST 0x0430 +#define CCS_AHBM_CR_BURST_EN (1 << 2) +#define CCS_FPIM_CR_BURST_EN (1 << 1) + +#define CCR03_EIGHT_BANK_MODE (1 << 0) + + /* Store given value in MC DDR CCRx register */ + .macro ccr_sw num, val + li t1, \val + sw t1, LTQ_MC_DDR_CCR_OFFSET(\num)(t0) + .endm + +LEAF(ltq_mem_init) + /* Load MC DDR module base */ + li t0, (LTQ_MC_DDR_BASE | KSEG1) + + /* Put memory controller in inactive mode */ + sw zero, LTQ_MC_DDR_CCR_OFFSET(7)(t0) + + /* Init MC DDR CCR registers with values from ddr_settings.h */ + ccr_sw 0, MC_CCR00_VALUE + ccr_sw 1, MC_CCR01_VALUE + ccr_sw 2, MC_CCR02_VALUE + ccr_sw 3, MC_CCR03_VALUE + ccr_sw 4, MC_CCR04_VALUE + ccr_sw 5, MC_CCR05_VALUE + ccr_sw 6, MC_CCR06_VALUE + ccr_sw 7, MC_CCR07_VALUE + ccr_sw 8, MC_CCR08_VALUE + ccr_sw 9, MC_CCR09_VALUE + + ccr_sw 10, MC_CCR10_VALUE + ccr_sw 11, MC_CCR11_VALUE + ccr_sw 12, MC_CCR12_VALUE + ccr_sw 13, MC_CCR13_VALUE + ccr_sw 14, MC_CCR14_VALUE + ccr_sw 15, MC_CCR15_VALUE + ccr_sw 16, MC_CCR16_VALUE + ccr_sw 17, MC_CCR17_VALUE + ccr_sw 18, MC_CCR18_VALUE + ccr_sw 19, MC_CCR19_VALUE + + ccr_sw 20, MC_CCR20_VALUE + ccr_sw 21, MC_CCR21_VALUE + ccr_sw 22, MC_CCR22_VALUE + ccr_sw 23, MC_CCR23_VALUE + ccr_sw 24, MC_CCR24_VALUE + ccr_sw 25, MC_CCR25_VALUE + ccr_sw 26, MC_CCR26_VALUE + ccr_sw 27, MC_CCR27_VALUE + ccr_sw 28, MC_CCR28_VALUE + ccr_sw 29, MC_CCR29_VALUE + + ccr_sw 30, MC_CCR30_VALUE + ccr_sw 31, MC_CCR31_VALUE + ccr_sw 32, MC_CCR32_VALUE + ccr_sw 33, MC_CCR33_VALUE + ccr_sw 34, MC_CCR34_VALUE + ccr_sw 35, MC_CCR35_VALUE + ccr_sw 36, MC_CCR36_VALUE + ccr_sw 37, MC_CCR37_VALUE + ccr_sw 38, MC_CCR38_VALUE + ccr_sw 39, MC_CCR39_VALUE + + ccr_sw 40, MC_CCR40_VALUE + ccr_sw 41, MC_CCR41_VALUE + ccr_sw 42, MC_CCR42_VALUE + ccr_sw 43, MC_CCR43_VALUE + ccr_sw 44, MC_CCR44_VALUE + ccr_sw 45, MC_CCR45_VALUE + ccr_sw 46, MC_CCR46_VALUE + + ccr_sw 52, MC_CCR52_VALUE + ccr_sw 53, MC_CCR53_VALUE + ccr_sw 54, MC_CCR54_VALUE + ccr_sw 55, MC_CCR55_VALUE + ccr_sw 56, MC_CCR56_VALUE + ccr_sw 57, MC_CCR57_VALUE + ccr_sw 58, MC_CCR58_VALUE + ccr_sw 59, MC_CCR59_VALUE + + ccr_sw 60, MC_CCR60_VALUE + ccr_sw 61, MC_CCR61_VALUE + + /* Disable bursts between FPI Master bus and XBAR bus */ + li t4, (LTQ_MC_GLOBAL_BASE | KSEG1) + li t5, CCS_AHBM_CR_BURST_EN + sw t5, CCS_ALWAYS_LAST(t4) + + /* Init abort condition for DRAM probe */ + move t4, zero + + /* + * Put memory controller in active mode and start initialitation + * sequence for connected DDR-SDRAM device + */ +mc_start: + lw t1, LTQ_MC_DDR_CCR_OFFSET(7)(t0) + li t2, LTQ_MC_DDR_START + or t1, t1, t2 + sw t1, LTQ_MC_DDR_CCR_OFFSET(7)(t0) + + /* + * Wait until DLL has locked and core is ready for data transfers. + * DLL lock indication is in register CCR47 and CCR48 + */ +wait_ready: + li t1, LTQ_MC_DDR_DLL_LOCK_IND + lw t2, LTQ_MC_DDR_CCR_OFFSET(47)(t0) + and t2, t2, t1 + bne t1, t2, wait_ready + + lw t2, LTQ_MC_DDR_CCR_OFFSET(48)(t0) + and t2, t2, t1 + bne t1, t2, wait_ready + +#ifdef CONFIG_SYS_DRAM_PROBE +dram_probe: + /* Initialization is finished after the second MC start */ + bnez t4, mc_finished + + /* + * Preload register values for CCR03 and CCR11. Initial settings + * are 8-bank mode enabled, 14 use address row bits, 10 used + * column address bits. + */ + li t1, CONFIG_SYS_SDRAM_BASE_UC + li t5, MC_CCR03_VALUE + li t6, MC_CCR11_VALUE + addi t4, t4, 1 + + /* + * Store test values to DRAM at offsets 0 and 2^13 (bit 2 in bank select + * address BA[3]) and read back the value at offset 0. If the resulting + * value is equal to 1 we can skip to the next test. Otherwise + * the 8-bank mode does not work with the current DRAM device, + * thus we need to clear the according bit in register CCR03. + */ + li t2, 1 + sw t2, 0x0(t1) + li t3, (1 << 13) + add t3, t3, t1 + sw zero, 0(t3) + lw t3, 0(t1) + bnez t3, row_col_test + + /* Clear CCR03.EIGHT_BANK_MODE */ + li t3, ~CCR03_EIGHT_BANK_MODE + and t5, t5, t3 + +row_col_test: + /* + * Store test values to DRAM at offsets 0, 2^27 (bit 13 of row address + * RA[14]) and 2^26 (bit 12 of RA[14]). The chosen test values + * represent the difference between max. row address bits (14) and used + * row address bits. Then the read back value at offset 0 indicates + * the useable row address bits with the current DRAM device. This + * value must be set in the CCR11 register. + */ + sw zero, 0(t1) + + li t2, 1 + li t3, (1 << 27) + add t3, t3, t1 + sw t2, 0(t3) + + li t2, 2 + li t3, (1 << 26) + add t3, t3, t1 + sw t2, 0(t3) + + /* Update CCR11.ADDR_PINS */ + lw t3, 0(t1) + add t6, t6, t3 + + /* + * Store test values to DRAM at offsets 0, 2^10 (bit 9 of column address + * CA[10]) and 2^9 (bit 8 of CA[10]). The chosen test values represent + * the difference between max. column address bits (12) and used + * column address bits. Then the read back value at offset 0 indicates + * the useable column address bits with the current DRAM device. This + * value must be set in the CCR11 register. + */ + sw zero, 0(t1) + + li t2, 1 + li t3, (1 << 10) + add t3, t3, t1 + sw t2, 0(t3) + + li t2, 2 + li t3, (1 << 9) + add t3, t3, t1 + sw t2, 0(t3) + + /* Update CCR11.COLUMN_SIZE */ + lw t3, 0(t1) + sll t3, t3, 24 + add t6, t6, t3 + + /* Put memory controller in inactive mode */ + sw zero, LTQ_MC_DDR_CCR_OFFSET(7)(t0) + + /* Update CCR03 and CCR11 and restart memory controller initialiation */ + sw t5, LTQ_MC_DDR_CCR_OFFSET(3)(t0) + sw t6, LTQ_MC_DDR_CCR_OFFSET(11)(t0) + b mc_start + +mc_finished: +#endif /* CONFIG_SYS_DRAM_PROBE */ + + jr ra + + END(ltq_mem_init) --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/pmu.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define LTQ_PMU_PWDCR_RESERVED ((1 << 13) | (1 << 4)) + +#define LTQ_PMU_PWDCR_PCIELOC_EN (1 << 31) +#define LTQ_PMU_PWDCR_GPHY (1 << 30) +#define LTQ_PMU_PWDCR_PPE_TOP (1 << 29) +#define LTQ_PMU_PWDCR_SWITCH (1 << 28) +#define LTQ_PMU_PWDCR_USB1 (1 << 27) +#define LTQ_PMU_PWDCR_USB1_PHY (1 << 26) +#define LTQ_PMU_PWDCR_TDM (1 << 25) +#define LTQ_PMU_PWDCR_PPE_DPLUS (1 << 24) +#define LTQ_PMU_PWDCR_PPE_DPLUM (1 << 23) +#define LTQ_PMU_PWDCR_PPE_EMA (1 << 22) +#define LTQ_PMU_PWDCR_PPE_TC (1 << 21) +#define LTQ_PMU_PWDCR_DEU (1 << 20) +#define LTQ_PMU_PWDCR_PPE_SLL01 (1 << 19) +#define LTQ_PMU_PWDCR_PPE_QSB (1 << 18) +#define LTQ_PMU_PWDCR_UART1 (1 << 17) +#define LTQ_PMU_PWDCR_SDIO (1 << 16) +#define LTQ_PMU_PWDCR_AHBM (1 << 15) +#define LTQ_PMU_PWDCR_FPIM (1 << 14) +#define LTQ_PMU_PWDCR_GPTC (1 << 12) +#define LTQ_PMU_PWDCR_LEDC (1 << 11) +#define LTQ_PMU_PWDCR_EBU (1 << 10) +#define LTQ_PMU_PWDCR_DSL (1 << 9) +#define LTQ_PMU_PWDCR_SPI (1 << 8) +#define LTQ_PMU_PWDCR_USIF (1 << 7) +#define LTQ_PMU_PWDCR_USB0 (1 << 6) +#define LTQ_PMU_PWDCR_DMA (1 << 5) +#define LTQ_PMU_PWDCR_DFEV1 (1 << 3) +#define LTQ_PMU_PWDCR_DFEV0 (1 << 2) +#define LTQ_PMU_PWDCR_FPIS (1 << 1) +#define LTQ_PMU_PWDCR_USB0_PHY (1 << 0) + +struct ltq_pmu_regs { + u32 rsvd0[7]; + u32 pwdcr; /* Power down control */ + u32 sr; /* Power down status */ + u32 pwdcr1; /* Power down control 1 */ + u32 sr1; /* Power down status 1 */ +}; + +static struct ltq_pmu_regs *ltq_pmu_regs = + (struct ltq_pmu_regs *) CKSEG1ADDR(LTQ_PMU_BASE); + +u32 ltq_pm_map(enum ltq_pm_modules module) +{ + u32 val; + + switch (module) { + case LTQ_PM_CORE: + val = LTQ_PMU_PWDCR_UART1 | LTQ_PMU_PWDCR_FPIM | + LTQ_PMU_PWDCR_LEDC | LTQ_PMU_PWDCR_EBU; + break; + case LTQ_PM_DMA: + val = LTQ_PMU_PWDCR_DMA; + break; + case LTQ_PM_ETH: + val = LTQ_PMU_PWDCR_GPHY | LTQ_PMU_PWDCR_PPE_TOP | + LTQ_PMU_PWDCR_SWITCH | LTQ_PMU_PWDCR_PPE_DPLUS | + LTQ_PMU_PWDCR_PPE_DPLUM | LTQ_PMU_PWDCR_PPE_EMA | + LTQ_PMU_PWDCR_PPE_TC | LTQ_PMU_PWDCR_PPE_SLL01 | + LTQ_PMU_PWDCR_PPE_QSB; + break; + case LTQ_PM_SPI: + val = LTQ_PMU_PWDCR_SPI; + break; + default: + val = 0; + break; + } + + return val; +} + +int ltq_pm_enable(enum ltq_pm_modules module) +{ + const unsigned long timeout = 1000; + unsigned long timebase; + u32 sr, val; + + val = ltq_pm_map(module); + if (unlikely(!val)) + return 1; + + ltq_clrbits(<q_pmu_regs->pwdcr, val); + + timebase = get_timer(0); + + do { + sr = ltq_readl(<q_pmu_regs->sr); + if (~sr & val) + return 0; + } while (get_timer(timebase) < timeout); + + return 1; +} + +int ltq_pm_disable(enum ltq_pm_modules module) +{ + u32 val; + + val = ltq_pm_map(module); + if (unlikely(!val)) + return 1; + + ltq_setbits(<q_pmu_regs->pwdcr, val); + + return 0; +} + +void ltq_pmu_init(void) +{ + u32 set, clr; + + clr = ltq_pm_map(LTQ_PM_CORE); + set = ~(LTQ_PMU_PWDCR_RESERVED | clr); + + ltq_clrsetbits(<q_pmu_regs->pwdcr, clr, set); +} --- /dev/null +++ b/arch/mips/cpu/mips32/vrx200/rcu.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define LTQ_RCU_RD_GPHY0 (1 << 31) /* GPHY0 */ +#define LTQ_RCU_RD_SRST (1 << 30) /* Global SW Reset */ +#define LTQ_RCU_RD_GPHY1 (1 << 29) /* GPHY1 */ +#define LTQ_RCU_RD_ENMIP2 (1 << 28) /* Enable NMI of PLL2 */ +#define LTQ_RCU_RD_REG25_PD (1 << 26) /* Power down 2.5V regulator */ +#define LTQ_RCU_RD_ENDINIT (1 << 25) /* FPI slave bus access */ +#define LTQ_RCU_RD_PPE_ATM_TC (1 << 23) /* PPE ATM TC */ +#define LTQ_RCU_RD_PCIE (1 << 22) /* PCI-E core */ +#define LTQ_RCU_RD_ETHSW (1 << 21) /* Ethernet switch */ +#define LTQ_RCU_RD_DSP_DEN (1 << 20) /* Enable DSP JTAG */ +#define LTQ_RCU_RD_TDM (1 << 19) /* TDM module interface */ +#define LTQ_RCU_RD_ENMIP1 (1 << 18) /* Enable NMI of PLL1 */ +#define LTQ_RCU_RD_SWBCK (1 << 17) /* Switch backward compat */ +#define LTQ_RCU_RD_HSNAND (1 << 16) /* HSNAND controller */ +#define LTQ_RCU_RD_ENMIP0 (1 << 15) /* Enable NMI of PLL0 */ +#define LTQ_RCU_RD_MC (1 << 14) /* Memory Controller */ +#define LTQ_RCU_RD_PCI (1 << 13) /* PCI core */ +#define LTQ_RCU_RD_PCIE_PHY (1 << 12) /* PCI-E Phy */ +#define LTQ_RCU_RD_DFE_CORE (1 << 11) /* DFE core */ +#define LTQ_RCU_RD_SDIO (1 << 10) /* SDIO core */ +#define LTQ_RCU_RD_DMA (1 << 9) /* DMA core */ +#define LTQ_RCU_RD_PPE (1 << 8) /* PPE core */ +#define LTQ_RCU_RD_DFE (1 << 7) /* DFE core */ +#define LTQ_RCU_RD_AHB (1 << 6) /* AHB bus */ +#define LTQ_RCU_RD_HRST_CFG (1 << 5) /* HW reset configuration */ +#define LTQ_RCU_RD_USB (1 << 4) /* USB and Phy core */ +#define LTQ_RCU_RD_PPE_DSP (1 << 3) /* PPE DSP interface */ +#define LTQ_RCU_RD_FPI (1 << 2) /* FPI bus */ +#define LTQ_RCU_RD_CPU (1 << 1) /* CPU subsystem */ +#define LTQ_RCU_RD_HRST (1 << 0) /* HW reset via HRST pin */ + +#define LTQ_RCU_STAT_BOOT_SHIFT 17 +#define LTQ_RCU_STAT_BOOT_MASK (0xF << LTQ_RCU_STAT_BOOT_SHIFT) +#define LTQ_RCU_STAT_BOOT_H (1 << 12) + +#define LTQ_RCU_GP_STRAP_CLOCKSOURCE (1 << 15) + +struct ltq_rcu_regs { + u32 rsvd0[4]; + u32 req; /* Reset request */ + u32 stat; /* Reset status */ + u32 usb0_cfg; /* USB0 configure */ + u32 gp_strap; /* GPIO strapping */ + u32 gfs_add0; /* GPHY0 firmware base addr */ + u32 stat2; /* SLIC and USB reset status */ + u32 pci_rdy; /* PCI boot ready */ + u32 ppe_conf; /* PPE ethernet config */ + u32 pcie_phy_con; /* PCIE PHY config/status */ + u32 usb1_cfg; /* USB1 configure */ + u32 usb_ana_cfg1a; /* USB analog config 1a */ + u32 usb_ana_cfg1b; /* USB analog config 1b */ + u32 rsvd1; + u32 gf_mdio_add; /* GPHY0/1 MDIO address */ + u32 req2; /* SLIC and USB reset request */ + u32 ahb_endian; /* AHB bus endianess */ + u32 rsvd2[4]; + u32 gcc; /* General CPU config */ + u32 rsvd3; + u32 gfs_add1; /* GPHY1 firmware base addr */ +}; + +static struct ltq_rcu_regs *ltq_rcu_regs = + (struct ltq_rcu_regs *) CKSEG1ADDR(LTQ_RCU_BASE); + +u32 ltq_reset_map(enum ltq_reset_modules module) +{ + u32 val; + + switch (module) { + case LTQ_RESET_CORE: + case LTQ_RESET_SOFT: + val = LTQ_RCU_RD_SRST | LTQ_RCU_RD_CPU | LTQ_RCU_RD_ENMIP2 | + LTQ_RCU_RD_GPHY1 | LTQ_RCU_RD_GPHY0; + break; + case LTQ_RESET_DMA: + val = LTQ_RCU_RD_DMA; + break; + case LTQ_RESET_ETH: + val = LTQ_RCU_RD_PPE | LTQ_RCU_RD_ETHSW; + break; + case LTQ_RESET_PHY: + val = LTQ_RCU_RD_GPHY1 | LTQ_RCU_RD_GPHY0; + break; + case LTQ_RESET_HARD: + val = LTQ_RCU_RD_HRST; + break; + default: + val = 0; + break; + } + + return val; +} + +int ltq_reset_activate(enum ltq_reset_modules module) +{ + u32 val; + + val = ltq_reset_map(module); + if (unlikely(!val)) + return 1; + + ltq_setbits(<q_rcu_regs->req, val); + + return 0; +} + +int ltq_reset_deactivate(enum ltq_reset_modules module) +{ + u32 val; + + val = ltq_reset_map(module); + if (unlikely(!val)) + return 1; + + ltq_clrbits(<q_rcu_regs->req, val); + + return 0; +} + +enum ltq_boot_select ltq_boot_select(void) +{ + u32 stat; + unsigned int bootstrap; + + /* + * Boot select value is built from bits 20-17 and bit 12. + * The bit sequence is read as 4-2-1-0-3. + */ + stat = ltq_readl(<q_rcu_regs->stat); + bootstrap = ((stat & LTQ_RCU_STAT_BOOT_H) << 4) | + ((stat & LTQ_RCU_STAT_BOOT_MASK) >> LTQ_RCU_STAT_BOOT_SHIFT); + + switch (bootstrap) { + case 0: + return BOOT_NOR_NO_BOOTROM; + case 1: + return BOOT_RGMII1; + case 2: + return BOOT_NOR; + case 4: + return BOOT_UART_NO_EEPROM; + case 6: + return BOOT_PCI; + case 8: + return BOOT_UART; + case 10: + return BOOT_SPI; + case 12: + return BOOT_NAND; + default: + return BOOT_UNKNOWN; + } +} + +void ltq_rcu_gphy_boot(unsigned int id, ulong addr) +{ + u32 module; + void *gfs_add; + + switch (id) { + case 0: + module = LTQ_RCU_RD_GPHY0; + gfs_add = <q_rcu_regs->gfs_add0; + break; + case 1: + module = LTQ_RCU_RD_GPHY1; + gfs_add = <q_rcu_regs->gfs_add1; + break; + default: + BUG(); + } + + /* Stop and reset GPHY */ + ltq_setbits(<q_rcu_regs->req, module); + + /* Configure firmware and boot address */ + ltq_writel(gfs_add, CPHYSADDR(addr & 0xFFFFC000)); + + /* Start GPHY by releasing reset */ + ltq_clrbits(<q_rcu_regs->req, module); +} --- /dev/null +++ b/arch/mips/include/asm/arch-danube/config.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Common board configuration for Lantiq XWAY Danube family + * + * Use following defines in your board config to enable specific features + * and drivers for this SoC: + * + * CONFIG_LTQ_SUPPORT_UART + * - support the Danube ASC/UART interface and console + * + * CONFIG_LTQ_SUPPORT_NOR_FLASH + * - support a parallel NOR flash via the CFI interface in flash bank 0 + * + * CONFIG_LTQ_SUPPORT_ETHERNET + * - support the Danube ETOP and MAC interface + * + * CONFIG_LTQ_SUPPORT_SPI_FLASH + * - support the Danube SPI interface and serial flash drivers + * - specific SPI flash drivers must be configured separately + */ + +#ifndef __DANUBE_CONFIG_H__ +#define __DANUBE_CONFIG_H__ + +/* CPU and SoC type */ +#define CONFIG_SOC_LANTIQ +#define CONFIG_SOC_XWAY_DANUBE + +/* Cache configuration */ +#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT +#define CONFIG_SYS_DCACHE_SIZE (16 * 1024) +#define CONFIG_SYS_ICACHE_SIZE (16 * 1024) +#define CONFIG_SYS_CACHELINE_SIZE 32 +#define CONFIG_SYS_MIPS_CACHE_EXT_INIT + +/* + * Supported clock modes + * PLL0 clock output is 333 MHz + * PLL1 clock output is 262.144 MHz + */ +#define LTQ_CLK_CPU_333_DDR_167 0 /* Base PLL0, OCP 2 */ +#define LTQ_CLK_CPU_111_DDR_111 1 /* Base PLL0, OCP 1 */ + +/* CPU speed */ +#define CONFIG_SYS_CLOCK_MODE LTQ_CLK_CPU_333_DDR_167 +#define CONFIG_SYS_MIPS_TIMER_FREQ 166666667 +#define CONFIG_SYS_HZ 1000 + +/* RAM */ +#define CONFIG_NR_DRAM_BANKS 1 +#define CONFIG_SYS_SDRAM_BASE 0x80000000 +#define CONFIG_SYS_MEMTEST_START 0x81000000 +#define CONFIG_SYS_MEMTEST_END 0x82000000 +#define CONFIG_SYS_LOAD_ADDR 0x81000000 +#define CONFIG_SYS_INIT_SP_OFFSET 0x4000 + +/* SRAM */ +#define CONFIG_SYS_SRAM_BASE 0xBE1A0000 +#define CONFIG_SYS_SRAM_SIZE 0x10000 + +/* ASC/UART driver and console */ +#define CONFIG_LANTIQ_SERIAL +#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } + +/* GPIO */ +#define CONFIG_LANTIQ_GPIO +#define CONFIG_LTQ_GPIO_MAX_BANKS 2 + +/* FLASH driver */ +#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) +#define CONFIG_SYS_MAX_FLASH_BANKS 1 +#define CONFIG_SYS_MAX_FLASH_SECT 256 +#define CONFIG_SYS_FLASH_BASE 0xB0000000 +#define CONFIG_FLASH_16BIT +#define CONFIG_SYS_FLASH_CFI +#define CONFIG_FLASH_CFI_DRIVER +#define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_16BIT +#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE +#define CONFIG_FLASH_SHOW_PROGRESS 50 +#define CONFIG_SYS_FLASH_PROTECTION +#define CONFIG_CFI_FLASH_USE_WEAK_ADDR_SWAP + +#define CONFIG_CMD_FLASH +#else +#define CONFIG_SYS_NO_FLASH +#endif /* CONFIG_NOR_FLASH */ + +#if defined(CONFIG_LTQ_SUPPORT_SPI_FLASH) +#define CONFIG_LANTIQ_SPI +#define CONFIG_SPI_FLASH + +#define CONFIG_CMD_SF +#define CONFIG_CMD_SPI +#endif + +#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH) +#define CONFIG_NAND_LANTIQ +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_BASE 0xB4000000 + +#define CONFIG_CMD_NAND +#endif + +#if defined(CONFIG_LTQ_SUPPORT_ETHERNET) +#define CONFIG_LANTIQ_DMA +#define CONFIG_LANTIQ_DANUBE_ETOP + +#define CONFIG_PHYLIB +#define CONFIG_MII + +#define CONFIG_CMD_MII +#define CONFIG_CMD_NET +#endif + +#define CONFIG_SPL_MAX_SIZE (32 * 1024) +#define CONFIG_SPL_BSS_MAX_SIZE (8 * 1024) +#define CONFIG_SPL_STACK_MAX_SIZE (8 * 1024) +#define CONFIG_SPL_MALLOC_MAX_SIZE (32 * 1024) +/*#define CONFIG_SPL_STACK_BSS_IN_SRAM*/ + +#if defined(CONFIG_SPL_STACK_BSS_IN_SRAM) +#define CONFIG_SPL_STACK_BASE (CONFIG_SYS_SRAM_BASE + \ + CONFIG_SPL_MAX_SIZE + \ + CONFIG_SPL_STACK_MAX_SIZE - 1) +#define CONFIG_SPL_BSS_BASE (CONFIG_SPL_STACK_BASE + 1) +#define CONFIG_SPL_MALLOC_BASE (CONFIG_SYS_SDRAM_BASE + \ + CONFIG_SYS_INIT_SP_OFFSET) +#else +#define CONFIG_SPL_STACK_BASE (CONFIG_SYS_SDRAM_BASE + \ + CONFIG_SYS_INIT_SP_OFFSET + \ + CONFIG_SPL_STACK_MAX_SIZE - 1) +#define CONFIG_SPL_BSS_BASE (CONFIG_SPL_STACK_BASE + 1) +#define CONFIG_SPL_MALLOC_BASE (CONFIG_SPL_BSS_BASE + \ + CONFIG_SPL_BSS_MAX_SIZE) +#endif + +#if defined(CONFIG_SYS_BOOT_RAM) +#define CONFIG_SYS_TEXT_BASE 0xa0100000 +#define CONFIG_SKIP_LOWLEVEL_INIT +#define CONFIG_SYS_DISABLE_CACHE +#endif + +#if defined(CONFIG_SYS_BOOT_NOR) +#define CONFIG_SYS_TEXT_BASE 0xB0000000 +#endif + +#if defined(CONFIG_SYS_BOOT_NORSPL) +#define CONFIG_SYS_TEXT_BASE 0x80100000 +#define CONFIG_SPL_TEXT_BASE 0xB0000000 +#endif + +#if defined(CONFIG_SYS_BOOT_NOR) || defined(CONFIG_SYS_BOOT_NORSPL) +#define CONFIG_SYS_XWAY_EBU_BOOTCFG 0x688C688C +#define CONFIG_XWAY_SWAP_BYTES +#endif + +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE + +#endif /* __DANUBE_CONFIG_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-danube/gpio.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DANUBE_GPIO_H__ +#define __DANUBE_GPIO_H__ + +#include + +#endif /* __DANUBE_GPIO_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-danube/nand.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DANUBE_NAND_H__ +#define __DANUBE_NAND_H__ + +struct nand_chip; +int ltq_nand_init(struct nand_chip *nand); + +#endif /* __DANUBE_NAND_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-danube/soc.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DANUBE_SOC_H__ +#define __DANUBE_SOC_H__ + +#define LTQ_ASC0_BASE 0x1E100400 +#define LTQ_SPI_BASE 0x1E100800 +#define LTQ_GPIO_BASE 0x1E100B00 +#define LTQ_SSIO_BASE 0x1E100BB0 +#define LTQ_ASC1_BASE 0x1E100C00 +#define LTQ_DMA_BASE 0x1E104100 + +#define LTQ_EBU_BASE 0x1E105300 +#define LTQ_EBU_REGION0_BASE 0x10000000 +#define LTQ_EBU_REGION1_BASE 0x14000000 +#define LTQ_EBU_NAND_BASE (LTQ_EBU_BASE + 0xB0) + +#define LTQ_PPE_BASE 0x1E180000 +#define LTQ_PPE_ETOP_BASE (LTQ_PPE_BASE + 0x11800) +#define LTQ_PPE_ENET0_BASE (LTQ_PPE_BASE + 0x11840) + +#define LTQ_PMU_BASE 0x1F102000 +#define LTQ_CGU_BASE 0x1F103000 +#define LTQ_MPS_BASE 0x1F107000 +#define LTQ_CHIPID_BASE (LTQ_MPS_BASE + 0x340) +#define LTQ_RCU_BASE 0x1F203000 + +#define LTQ_MC_GEN_BASE 0x1F800000 +#define LTQ_MC_SDR_BASE 0x1F800200 +#define LTQ_MC_DDR_BASE 0x1F801000 +#define LTQ_MC_DDR_DC_OFFSET(x) (x * 0x10) + +#endif /* __DANUBE_SOC_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-vrx200/config.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Common board configuration for Lantiq XWAY VRX200 family + * + * Use following defines in your board config to enable specific features + * and drivers for this SoC: + * + * CONFIG_LTQ_SUPPORT_UART + * - support the VRX200 ASC/UART interface and console + * + * CONFIG_LTQ_SUPPORT_NOR_FLASH + * - support a parallel NOR flash via the CFI interface in flash bank 0 + * + * CONFIG_LTQ_SUPPORT_ETHERNET + * - support the VRX200 internal switch + * + * CONFIG_LTQ_SUPPORT_SPI_FLASH + * - support the VRX200 SPI interface and serial flash drivers + * - specific SPI flash drivers must be configured separately + * + * CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH + * - build a preloader that runs in the internal SRAM and loads + * the U-Boot from SPI flash into RAM + */ + +#ifndef __VRX200_CONFIG_H__ +#define __VRX200_CONFIG_H__ + +/* CPU and SoC type */ +#define CONFIG_SOC_LANTIQ +#define CONFIG_SOC_XWAY_VRX200 + +/* Cache configuration */ +#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT +#define CONFIG_SYS_DCACHE_SIZE (32 * 1024) +#define CONFIG_SYS_ICACHE_SIZE (32 * 1024) +#define CONFIG_SYS_CACHELINE_SIZE 32 +#define CONFIG_SYS_MIPS_CACHE_EXT_INIT + +/* + * Supported clock modes + * PLL0 clock output is 1000 MHz + * PLL1 clock output is 393.219 MHz + */ +#define LTQ_CLK_CPU_600_DDR_300 0 /* Base PLL0, OCP 2 */ +#define LTQ_CLK_CPU_600_DDR_200 1 /* Base PLL0, OCP 3 */ +#define LTQ_CLK_CPU_500_DDR_250 2 /* Base PLL0, OCP 2 */ +#define LTQ_CLK_CPU_500_DDR_200 3 /* Base PLL0, OCP 2.5 */ +#define LTQ_CLK_CPU_333_DDR_167 4 /* Base PLL0, OCP 2 */ +#define LTQ_CLK_CPU_167_DDR_167 5 /* Base PLL0, OCP 1 */ +#define LTQ_CLK_CPU_125_DDR_125 6 /* Base PLL0, OCP 1 */ +#define LTQ_CLK_CPU_393_DDR_197 7 /* Base PLL1, OCP 2 */ +#define LTQ_CLK_CPU_197_DDR_197 8 /* Base PLL1, OCP 1 */ + +/* CPU speed */ +#define CONFIG_SYS_CLOCK_MODE LTQ_CLK_CPU_500_DDR_250 +#define CONFIG_SYS_MIPS_TIMER_FREQ 250000000 +#define CONFIG_SYS_HZ 1000 + +/* RAM */ +#define CONFIG_NR_DRAM_BANKS 1 +#define CONFIG_SYS_SDRAM_BASE 0x80000000 +#define CONFIG_SYS_SDRAM_BASE_UC 0xa0000000 +#define CONFIG_SYS_MEMTEST_START 0x81000000 +#define CONFIG_SYS_MEMTEST_END 0x82000000 +#define CONFIG_SYS_LOAD_ADDR 0x81000000 +#define CONFIG_SYS_INIT_SP_OFFSET (32 * 1024) + +/* SRAM */ +#define CONFIG_SYS_SRAM_BASE 0xBE220000 +#define CONFIG_SYS_SRAM_SIZE 0x10000 + +/* ASC/UART driver and console */ +#define CONFIG_LANTIQ_SERIAL +#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } + +/* GPIO */ +#define CONFIG_LANTIQ_GPIO +#define CONFIG_LTQ_GPIO_MAX_BANKS 3 +#define CONFIG_LTQ_HAS_GPIO_BANK3 + +/* FLASH driver */ +#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) +#ifndef CONFIG_SYS_MAX_FLASH_BANKS +#define CONFIG_SYS_MAX_FLASH_BANKS 1 +#endif +#define CONFIG_SYS_MAX_FLASH_SECT 256 +#define CONFIG_SYS_FLASH_BASE 0xB0000000 +#define CONFIG_SYS_FLASH2_BASE 0xB4000000 +#define CONFIG_FLASH_16BIT +#define CONFIG_SYS_FLASH_CFI +#define CONFIG_FLASH_CFI_DRIVER +#define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_16BIT +#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE +#define CONFIG_FLASH_SHOW_PROGRESS 50 +#define CONFIG_SYS_FLASH_PROTECTION +#define CONFIG_CFI_FLASH_USE_WEAK_ADDR_SWAP + +#define CONFIG_CMD_FLASH +#else +#define CONFIG_SYS_NO_FLASH +#endif /* CONFIG_NOR_FLASH */ + +#if defined(CONFIG_LTQ_SUPPORT_SPI_FLASH) +#define CONFIG_LANTIQ_SPI +#define CONFIG_SPI_FLASH + +#define CONFIG_CMD_SF +#define CONFIG_CMD_SPI +#endif + +#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH) +#define CONFIG_NAND_LANTIQ +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_BASE 0xB4000000 + +#define CONFIG_CMD_NAND +#endif + +#if defined(CONFIG_LTQ_SUPPORT_ETHERNET) +#define CONFIG_LANTIQ_DMA +#define CONFIG_LANTIQ_VRX200_SWITCH +#define CONFIG_PHY_LANTIQ + +#define CONFIG_SYS_RX_ETH_BUFFER 8 +#define CONFIG_PHYLIB +#define CONFIG_MII +#define CONFIG_UDP_CHECKSUM + +#define CONFIG_CMD_MII +#define CONFIG_CMD_NET +#endif + +#define CONFIG_SPL_MAX_SIZE (32 * 1024) +#define CONFIG_SPL_BSS_MAX_SIZE (8 * 1024) +#define CONFIG_SPL_STACK_MAX_SIZE (8 * 1024) +#define CONFIG_SPL_MALLOC_MAX_SIZE (32 * 1024) +#define CONFIG_SPL_STACK_BSS_IN_SRAM + +#if defined(CONFIG_SPL_STACK_BSS_IN_SRAM) +#define CONFIG_SPL_STACK_BASE (CONFIG_SYS_SRAM_BASE + \ + CONFIG_SPL_MAX_SIZE + \ + CONFIG_SPL_STACK_MAX_SIZE - 1) +#define CONFIG_SPL_BSS_BASE (CONFIG_SPL_STACK_BASE + 1) +#define CONFIG_SPL_MALLOC_BASE (CONFIG_SYS_SDRAM_BASE + \ + CONFIG_SYS_INIT_SP_OFFSET) +#else +#define CONFIG_SPL_STACK_BASE (CONFIG_SYS_SDRAM_BASE + \ + CONFIG_SYS_INIT_SP_OFFSET + \ + CONFIG_SPL_STACK_MAX_SIZE - 1) +#define CONFIG_SPL_BSS_BASE (CONFIG_SPL_STACK_BASE + 1) +#define CONFIG_SPL_MALLOC_BASE (CONFIG_SPL_BSS_BASE + \ + CONFIG_SPL_BSS_MAX_SIZE) +#endif + +#if defined(CONFIG_SYS_BOOT_RAM) +#define CONFIG_SYS_TEXT_BASE 0xA0100000 +#define CONFIG_SKIP_LOWLEVEL_INIT +#define CONFIG_SYS_DISABLE_CACHE +#endif + +#if defined(CONFIG_SYS_BOOT_NOR) +#define CONFIG_SYS_TEXT_BASE 0xB0000000 +#endif + +#if defined(CONFIG_SYS_BOOT_SFSPL) +#define CONFIG_SYS_TEXT_BASE 0x80100000 +#define CONFIG_SPL_TEXT_BASE 0xBE220000 +#endif + +#if defined(CONFIG_SYS_BOOT_NORSPL) +#define CONFIG_SYS_TEXT_BASE 0x80100000 +#define CONFIG_SPL_TEXT_BASE 0xB0000000 +#endif + +#if defined(CONFIG_SYS_BOOT_NOR) || defined(CONFIG_SYS_BOOT_NORSPL) +#define CONFIG_SYS_XWAY_EBU_BOOTCFG 0x688C688C +#define CONFIG_XWAY_SWAP_BYTES +#endif + +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE + +#endif /* __VRX200_CONFIG_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-vrx200/gphy.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __VRX200_GPHY_H__ +#define __VRX200_GPHY_H__ + +enum ltq_gphy_clk { + /* XTAL 36 MHz input */ + LTQ_GPHY_CLK_36MHZ_XTAL = 1, + /* 25 MHz from PLL0 with divider */ + LTQ_GPHY_CLK_25MHZ_PLL0 = 2, + /* derived from PLL2 output (XTAL is 36 MHz) */ + LTQ_GPHY_CLK_24MHZ_PLL2 = 3, + /* 25 MHz Clock from Pin GPIO3 */ + LTQ_GPHY_CLK_25MHZ_GPIO3 = 4, +}; + +/* + * Load PHY11G firmware for VRX200 v1.1 to given RAM address + * + * Address must be 16k aligned! + */ +extern void ltq_gphy_phy11g_a1x_load(ulong addr); + +/* + * Load PHY11G firmware for VRX200 v1.2 to given RAM address + * + * Address must be 16k aligned! + */ +extern void ltq_gphy_phy11g_a2x_load(ulong addr); + +/* + * Load PHY22F firmware for VRX200 v1.1 to given RAM address + * + * Address must be 16k aligned! + */ +extern void ltq_gphy_phy22f_a1x_load(ulong addr); + +/* + * Load PHY22F firmware for VRX200 v1.2 to given RAM address + * + * Address must be 16k aligned! + */ +extern void ltq_gphy_phy22f_a2x_load(ulong addr); + +/* + * Set clock source of internal GPHYs + * + * According registers resides in CGU address space. Thus this function + * is implemented by the CGU driver. + */ +extern void ltq_cgu_gphy_clk_src(enum ltq_gphy_clk clk); + +/* + * Boot internal GPHY with id from given RAM address + * + * According registers resides in RCU address space. Thus this function + * is implemented by the RCU driver. + */ +extern void ltq_rcu_gphy_boot(unsigned int id, ulong addr); + +#endif /* __VRX200_GPHY_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-vrx200/gpio.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __VRX200_GPIO_H__ +#define __VRX200_GPIO_H__ + +#include + +#endif /* __VRX200_GPIO_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-vrx200/nand.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __VRX200_NAND_H__ +#define __VRX200_NAND_H__ + +struct nand_chip; +int ltq_nand_init(struct nand_chip *nand); + +#endif /* __VRX200_NAND_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-vrx200/soc.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __VRX200_SOC_H__ +#define __VRX200_SOC_H__ + +#define LTQ_ASC0_BASE 0x1E100400 +#define LTQ_SPI_BASE 0x1E100800 +#define LTQ_GPIO_BASE 0x1E100B00 +#define LTQ_SSIO_BASE 0x1E100BB0 +#define LTQ_ASC1_BASE 0x1E100C00 +#define LTQ_DMA_BASE 0x1E104100 + +#define LTQ_EBU_BASE 0x1E105300 +#define LTQ_EBU_REGION0_BASE 0x10000000 +#define LTQ_EBU_REGION1_BASE 0x14000000 +#define LTQ_EBU_NAND_BASE (LTQ_EBU_BASE + 0xB0) + +#define LTQ_SWITCH_BASE 0x1E108000 +#define LTQ_SWITCH_CORE_BASE LTQ_SWITCH_BASE +#define LTQ_SWITCH_TOP_PDI_BASE LTQ_SWITCH_CORE_BASE +#define LTQ_SWITCH_BM_PDI_BASE (LTQ_SWITCH_CORE_BASE + 4 * 0x40) +#define LTQ_SWITCH_MAC_PDI_0_BASE (LTQ_SWITCH_CORE_BASE + 4 * 0x900) +#define LTQ_SWITCH_MAC_PDI_X_BASE(x) (LTQ_SWITCH_MAC_PDI_0_BASE + x * 0x30) +#define LTQ_SWITCH_TOPLEVEL_BASE (LTQ_SWITCH_BASE + 4 * 0xC40) +#define LTQ_SWITCH_MDIO_PDI_BASE (LTQ_SWITCH_TOPLEVEL_BASE) +#define LTQ_SWITCH_MII_PDI_BASE (LTQ_SWITCH_TOPLEVEL_BASE + 4 * 0x36) +#define LTQ_SWITCH_PMAC_PDI_BASE (LTQ_SWITCH_TOPLEVEL_BASE + 4 * 0x82) + +#define LTQ_PMU_BASE 0x1F102000 +#define LTQ_CGU_BASE 0x1F103000 +#define LTQ_DCDC_BASE 0x1F106A00 +#define LTQ_MPS_BASE 0x1F107000 +#define LTQ_CHIPID_BASE (LTQ_MPS_BASE + 0x340) +#define LTQ_RCU_BASE 0x1F203000 + +#define LTQ_MC_GLOBAL_BASE 0x1F400000 +#define LTQ_MC_DDR_BASE 0x1F401000 +#define LTQ_MC_DDR_CCR_OFFSET(x) (x * 0x10) + +#endif /* __VRX200_SOC_H__ */ --- /dev/null +++ b/arch/mips/include/asm/arch-vrx200/switch.h @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __VRX200_SWITCH_H__ +#define __VRX200_SWITCH_H__ + +/* Switch core registers */ +struct vr9_switch_core_regs { + __be32 swres; + /* TODO: implement registers */ + __be32 rsvd0[0x3f]; +}; + +/* Switch buffer management registers */ +struct vr9_switch_bm_regs { + struct bm_core { + __be32 ram_val3; /* RAM value 3 */ + __be32 ram_val2; /* RAM value 2 */ + __be32 ram_val1; /* RAM value 1 */ + __be32 ram_val0; /* RAM value 0 */ + __be32 ram_addr; /* RAM address */ + __be32 ram_ctrl; /* RAM access control */ + __be32 fsqm_gctrl; /* Free segment queue global control */ + __be32 cons_sel; /* Number of consumed segments */ + __be32 cons_pkt; /* Number of consumed packet pointers */ + __be32 gctrl; /* Global control */ + __be32 queue_gctrl; /* Queue manager global control */ + /* TODO: implement registers */ + __be32 rsvd0[0x35]; + } core; + + struct bm_port { + __be32 pcfg; /* Port config */ + __be32 rmon_ctrl; /* RMON control */ + } port[13]; + + __be32 rsvd0[0x66]; + + struct bm_queue { + __be32 rsvd0; + __be32 pqm_rs; /* Packet queue manager rate shape assignment */ + } queue[32]; + + struct bm_shaper { + __be32 ctrl; /* Rate shaper control */ + __be32 cbs; /* Rate shaper committed burst size */ + __be32 ibs; /* Rate shaper instantaneous burst size */ + __be32 cir_ext; /* Rate shaper rate exponent */ + __be32 cir_mant; /* Rate shaper rate mantissa */ + } shaper[16]; + + __be32 rsvd1[0x2a8]; +}; + +/* Switch parser and classification engine registers */ +struct vr9_switch_pce_regs { + struct pce_core { + __be32 tbl_key[16]; /* Table key data */ + __be32 tbl_mask; /* Table mask */ + __be32 tbl_val[5]; /* Table value */ + __be32 tbl_addr; /* Table entry address */ + __be32 tbl_ctrl; /* Table access control */ + __be32 tbl_stat; /* Table general status */ + __be32 age_0; /* Aging counter config 0 */ + __be32 age_1; /* Aging counter config 1 */ + __be32 pmap_1; /* Port map (monitoring) */ + __be32 pmap_2; /* Port map (multicast) */ + __be32 pmap_3; /* Port map (unknown unicast) */ + __be32 gctrl_0; /* Global control 0 */ + __be32 gctrl_1; /* Global control 1 */ + __be32 tcm_gctrl; /* Three-color marker global control */ + __be32 igmp_ctrl; /* IGMP control */ + __be32 igmp_drpm; /* IGMP default router port map */ + __be32 igmp_age_0; /* IGMP aging 0 */ + __be32 igmp_age_1; /* IGMP aging 1 */ + __be32 igmp_stat; /* IGMP status */ + __be32 wol_gctrl; /* Wake-on-LAN control */ + __be32 wol_da_0; /* Wake-on-LAN destination address 0 */ + __be32 wol_da_1; /* Wake-on-LAN destination address 1 */ + __be32 wol_da_2; /* Wake-on-LAN destination address 2 */ + __be32 wol_pw_0; /* Wake-on-LAN password 0 */ + __be32 wol_pw_1; /* Wake-on-LAN password 1 */ + __be32 wol_pw_2; /* Wake-on-LAN password 2 */ + __be32 ier_0; /* PCE global interrupt enable 0 */ + __be32 ier_1; /* PCE global interrupt enable 1 */ + __be32 isr_0; /* PCE global interrupt status 0 */ + __be32 isr_1; /* PCE global interrupt status 1 */ + __be32 parser_stat; /* Parser status */ + __be32 rsvd0[0x6]; + } core; + + __be32 rsvd0[0x10]; + + struct pce_port { + __be32 pctrl_0; /* Port control 0 */ + __be32 pctrl_1; /* Port control 1 */ + __be32 pctrl_2; /* Port control 2 */ + __be32 pctrl_3; /* Port control 3 */ + __be32 wol_ctrl; /* Wake-on-LAN control */ + __be32 vlan_ctrl; /* VLAN control */ + __be32 def_pvid; /* Default port VID */ + __be32 pstat; /* Port status */ + __be32 pier; /* Interrupt enable */ + __be32 pisr; /* Interrupt status */ + } port[13]; + + __be32 rsvd1[0x7e]; + + struct pce_meter { + /* TODO: implement registers */ + __be32 rsvd0[0x7]; + } meter[8]; + + __be32 rsvd2[0x308]; +}; + +static inline unsigned int to_pce_tbl_key_id(unsigned int id) +{ + BUG_ON(id > 15); + + return 15 - id; +} + +static inline unsigned int to_pce_tbl_value_id(unsigned int id) +{ + BUG_ON(id > 4); + + return 4 - id; +} + +/* Switch ethernet MAC registers */ +struct vr9_switch_mac_regs { + struct mac_core { + __be32 test; /* MAC test */ + __be32 pfad_cfg; /* Pause frame source address config */ + __be32 pfsa_0; /* Pause frame source address 0 */ + __be32 pfsa_1; /* Pause frame source address 1 */ + __be32 pfsa_2; /* Pause frame source address 2 */ + __be32 flen; /* Frame length */ + __be32 vlan_etype_0; /* VLAN ethertype 0 */ + __be32 vlan_etype_1; /* VLAN ethertype 1 */ + __be32 ier; /* Interrupt enable */ + __be32 isr; /* Interrupt status */ + __be32 rsvd0[0x36]; + } core; + + struct mac_port { + __be32 pstat; /* Port status */ + __be32 pisr; /* Interrupt status */ + __be32 pier; /* Interrupt enable */ + __be32 ctrl_0; /* Control 0 */ + __be32 ctrl_1; /* Control 1 */ + __be32 ctrl_2; /* Control 2 */ + __be32 ctrl_3; /* Control 3 */ + __be32 ctrl_4; /* Control 4 */ + __be32 ctrl_5; /* Control 5 */ + __be32 rsvd0[0x2]; + __be32 testen; /* Test enable */ + } port[13]; + + __be32 rsvd0[0xa4]; +}; + +/* Switch Fetch DMA registers */ +struct vr9_switch_fdma_regs { + struct fdma_core { + __be32 ctrl; /* FDMA control */ + __be32 stetype; /* Special tag ethertype control */ + __be32 vtetype; /* VLAN tag ethertype control */ + __be32 stat; /* FDMA status */ + __be32 ier; /* FDMA interrupt enable */ + __be32 isr; /* FDMA interrupt status */ + } core; + + __be32 rsvd0[0x3a]; + + struct fdma_port { + __be32 pctrl; /* Port control */ + __be32 prio; /* Port priority */ + __be32 pstat_0; /* Port status 0 */ + __be32 pstat_1; /* Port status 1 */ + __be32 tstamp_0; /* Egress time stamp 0 */ + __be32 tstamp_1; /* Egress time stamp 1 */ + } port[13]; + + __be32 rsvd1[0x72]; +}; + +/* Switch Store DMA registers */ +struct vr9_switch_sdma_regs { + struct sdma_core { + __be32 ctrl; /* SDMA Control */ + __be32 fcthr_1; /* Flow control threshold 1 */ + __be32 rsvd0; + __be32 fcthr_3; /* Flow control threshold 3 */ + __be32 fcthr_4; /* Flow control threshold 4 */ + __be32 fcthr_5; /* Flow control threshold 5 */ + __be32 fcthr_6; /* Flow control threshold 6 */ + __be32 fcthr_7; /* Flow control threshold 7 */ + __be32 stat_0; /* SDMA status 0 */ + __be32 stat_1; /* SDMA status 1 */ + __be32 stat_2; /* SDMA status 2 */ + __be32 ier; /* SDMA interrupt enable */ + __be32 isr; /* SDMA interrupt status */ + } core; + + __be32 rsvd0[0x73]; + + struct sdma_port { + __be32 pctrl; /* Port control */ + __be32 prio; /* Port priority */ + __be32 pstat_0; /* Port status 0 */ + __be32 pstat_1; /* Port status 1 */ + __be32 tstamp_0; /* Ingress time stamp 0 */ + __be32 tstamp_1; /* Ingress time stamp 1 */ + } port[13]; + + __be32 rsvd1[0x32]; +}; + +/* Switch MDIO control and status registers */ +struct vr9_switch_mdio_regs { + __be32 glob_ctrl; /* Global control 0 */ + __be32 rsvd0[7]; + __be32 mdio_ctrl; /* MDIO control */ + __be32 mdio_read; /* MDIO read data */ + __be32 mdio_write; /* MDIO write data */ + __be32 mdc_cfg_0; /* MDC clock configuration 0 */ + __be32 mdc_cfg_1; /* MDC clock configuration 1 */ + __be32 rsvd1[0x3]; + __be32 phy_addr[6]; /* PHY address port 5..0 */ + __be32 mdio_stat[6]; /* MDIO PHY polling status port 0..5 */ + __be32 aneg_eee[6]; /* EEE auto-neg overrides port 0..5 */ + __be32 rsvd2[0x14]; +}; + +static inline unsigned int to_mdio_phyaddr_id(unsigned int id) +{ + BUG_ON(id > 5); + + return 5 - id; +} + +/* Switch xMII control registers */ +struct vr9_switch_mii_regs { + __be32 mii_cfg0; /* xMII port 0 configuration */ + __be32 pcdu0; /* Port 0 clock delay configuration */ + __be32 mii_cfg1; /* xMII port 1 configuration */ + __be32 pcdu1; /* Port 1 clock delay configuration */ + __be32 rsvd0[0x6]; + __be32 mii_cfg5; /* xMII port 5 configuration */ + __be32 pcdu5; /* Port 5 clock delay configuration */ + __be32 rsvd1[0x14]; + __be32 rxb_ctl_0; /* Port 0 receive buffer control */ + __be32 rxb_ctl_1; /* Port 1 receive buffer control */ + __be32 rxb_ctl_5; /* Port 5 receive buffer control */ + __be32 rsvd2[0x28]; + __be32 dbg_ctl; /* Debug control */ +}; + +/* Switch Pseudo-MAC registers */ +struct vr9_switch_pmac_regs { + __be32 hd_ctl; /* PMAC header control */ + __be32 tl; /* PMAC type/length */ + __be32 sa1; /* PMAC source address 1 */ + __be32 sa2; /* PMAC source address 2 */ + __be32 sa3; /* PMAC source address 3 */ + __be32 da1; /* PMAC destination address 1 */ + __be32 da2; /* PMAC destination address 2 */ + __be32 da3; /* PMAC destination address 3 */ + __be32 vlan; /* PMAC VLAN */ + __be32 rx_ipg; /* PMAC interpacket gap in RX direction */ + __be32 st_etype; /* PMAC special tag ethertype */ + __be32 ewan; /* PMAC ethernet WAN group */ + __be32 ctl; /* PMAC control */ + __be32 rsvd0[0x2]; +}; + +struct vr9_switch_regs { + struct vr9_switch_core_regs core; + struct vr9_switch_bm_regs bm; + struct vr9_switch_pce_regs pce; + struct vr9_switch_mac_regs mac; + struct vr9_switch_fdma_regs fdma; + struct vr9_switch_sdma_regs sdma; + struct vr9_switch_mdio_regs mdio; + struct vr9_switch_mii_regs mii; + struct vr9_switch_pmac_regs pmac; +}; + +static inline void *to_pce_tbl_key(struct vr9_switch_regs *regs, + unsigned int id) +{ + return ®s->pce.core.tbl_key[to_pce_tbl_key_id(id)]; +} + +static inline void *to_pce_tbl_value(struct vr9_switch_regs *regs, + unsigned int id) +{ + return ®s->pce.core.tbl_val[to_pce_tbl_value_id(id)]; +} + +static inline void *to_mac_ctrl(struct vr9_switch_regs *regs, + unsigned int id, unsigned int ctrl) +{ + struct mac_port *mac = ®s->mac.port[id]; + + switch (ctrl) { + case 0: + return &mac->ctrl_0; + case 1: + return &mac->ctrl_1; + case 2: + return &mac->ctrl_2; + case 3: + return &mac->ctrl_3; + case 4: + return &mac->ctrl_4; + case 5: + return &mac->ctrl_5; + default: + return NULL; + } +} + +static inline void *to_mdio_phyaddr(struct vr9_switch_regs *regs, + unsigned int id) +{ + return ®s->mdio.phy_addr[to_mdio_phyaddr_id(id)]; +} + +static inline void *to_mii_miicfg(struct vr9_switch_regs *regs, + unsigned int id) +{ + switch (id) { + case 0: + return ®s->mii.mii_cfg0; + case 1: + return ®s->mii.mii_cfg1; + case 5: + return ®s->mii.mii_cfg5; + default: + return NULL; + } +} + +static inline void *to_mii_pcdu(struct vr9_switch_regs *regs, + unsigned int id) +{ + switch (id) { + case 0: + return ®s->mii.pcdu0; + case 1: + return ®s->mii.pcdu1; + case 5: + return ®s->mii.pcdu5; + default: + return NULL; + } +} + +#define VR9_SWITCH_REG_OFFSET(reg) (4 * (reg)) + +#define BUILD_CHECK_VR9_REG(name, offset) \ + BUILD_BUG_ON(offsetof(struct vr9_switch_regs, name) != (4 * offset)) + +static inline void build_check_vr9_registers(void) +{ + BUILD_CHECK_VR9_REG(core, 0x0); + BUILD_CHECK_VR9_REG(bm.core, 0x40); + BUILD_CHECK_VR9_REG(bm.core.queue_gctrl, 0x4a); + BUILD_CHECK_VR9_REG(bm.port[0], 0x80); + BUILD_CHECK_VR9_REG(bm.queue, 0x100); + BUILD_CHECK_VR9_REG(bm.shaper, 0x140); + BUILD_CHECK_VR9_REG(pce.core, 0x438); + BUILD_CHECK_VR9_REG(pce.core.tbl_ctrl, 0x44f); + BUILD_CHECK_VR9_REG(pce.core.parser_stat, 0x469); + BUILD_CHECK_VR9_REG(pce.port[0], 0x480); + BUILD_CHECK_VR9_REG(pce.meter[0], 0x580); + BUILD_CHECK_VR9_REG(mac.core, 0x8c0); + BUILD_CHECK_VR9_REG(mac.port[0].pstat, 0x900); + BUILD_CHECK_VR9_REG(mac.port[0].ctrl_0, 0x903); + BUILD_CHECK_VR9_REG(mac.port[1].pstat, 0x90c); + BUILD_CHECK_VR9_REG(mac.port[1].ctrl_0, 0x90f); + BUILD_CHECK_VR9_REG(mac.port[2].pstat, 0x918); + BUILD_CHECK_VR9_REG(mac.port[2].ctrl_0, 0x91b); + BUILD_CHECK_VR9_REG(fdma.core, 0xa40); + BUILD_CHECK_VR9_REG(fdma.port[0], 0xa80); + BUILD_CHECK_VR9_REG(sdma.core, 0xb40); + BUILD_CHECK_VR9_REG(sdma.port[0], 0xbc0); + BUILD_CHECK_VR9_REG(mdio, 0xc40); + BUILD_CHECK_VR9_REG(mii, (0xc40 + 0x36)); + BUILD_CHECK_VR9_REG(pmac, (0xc40 + 0x82)); +} + +#define BM_GCTRL_F_SRES 1 + +#define MAC_CTRL0_BM (1 << 12) +#define MAC_CTRL0_APADEN (1 << 11) +#define MAC_CTRL0_VPAD2EN (1 << 10) +#define MAC_CTRL0_VPADEN (1 << 9) +#define MAC_CTRL0_PADEN (1 << 8) +#define MAC_CTRL0_FCS (1 << 7) +#define MAC_CTRL0_FCON_SHIFT 4 +#define MAC_CTRL0_FCON_AUTO (0x0 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_RX (0x1 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_TX (0x2 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_RXTX (0x3 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FCON_NONE (0x4 << MAC_CTRL0_FCON_SHIFT) +#define MAC_CTRL0_FDUP_SHIFT 2 +#define MAC_CTRL0_FDUP_AUTO (0x0 << MAC_CTRL0_FDUP_SHIFT) +#define MAC_CTRL0_FDUP_EN (0x1 << MAC_CTRL0_FDUP_SHIFT) +#define MAC_CTRL0_FDUP_DIS (0x3 << MAC_CTRL0_FDUP_SHIFT) +#define MAC_CTRL0_GMII_AUTO 0x0 +#define MAC_CTRL0_GMII_MII 0x1 +#define MAC_CTRL0_GMII_GMII 0x2 +#define MAC_CTRL0_GMII_GMII_2G 0x3 + +#define MAC_CTRL1_DEFERMODE (1 << 15) +#define MAC_CTRL1_SHORTPRE (1 << 8) + +#define MAC_CTRL2_MLEN (1 << 3) +#define MAC_CTRL2_LCHKL (1 << 2) +#define MAC_CTRL2_LCHKS_DIS 0x0 +#define MAC_CTRL2_LCHKS_UNTAG 0x1 +#define MAC_CTRL2_LCHKS_TAG 0x2 + +#define PHY_ADDR_LNKST_SHIFT 13 +#define PHY_ADDR_LNKST_AUTO (0x0 << PHY_ADDR_LNKST_SHIFT) +#define PHY_ADDR_LNKST_UP (0x1 << PHY_ADDR_LNKST_SHIFT) +#define PHY_ADDR_LNKST_DOWN (0x2 << PHY_ADDR_LNKST_SHIFT) +#define PHY_ADDR_SPEED_SHIFT 11 +#define PHY_ADDR_SPEED_M10 (0x0 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_SPEED_M100 (0x1 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_SPEED_G1 (0x2 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_SPEED_AUTO (0x3 << PHY_ADDR_SPEED_SHIFT) +#define PHY_ADDR_FDUP_SHIFT 9 +#define PHY_ADDR_FDUP_AUTO (0x0 << PHY_ADDR_FDUP_SHIFT) +#define PHY_ADDR_FDUP_EN (0x1 << PHY_ADDR_FDUP_SHIFT) +#define PHY_ADDR_FDUP_DIS (0x3 << PHY_ADDR_FDUP_SHIFT) +#define PHY_ADDR_FCONTX_SHIFT 7 +#define PHY_ADDR_FCONTX_AUTO (0x0 << PHY_ADDR_FCONTX_SHIFT) +#define PHY_ADDR_FCONTX_EN (0x1 << PHY_ADDR_FCONTX_SHIFT) +#define PHY_ADDR_FCONTX_DIS (0x3 << PHY_ADDR_FCONTX_SHIFT) +#define PHY_ADDR_FCONRX_SHIFT 5 +#define PHY_ADDR_FCONRX_AUTO (0x0 << PHY_ADDR_FCONRX_SHIFT) +#define PHY_ADDR_FCONRX_EN (0x1 << PHY_ADDR_FCONRX_SHIFT) +#define PHY_ADDR_FCONRX_DIS (0x3 << PHY_ADDR_FCONRX_SHIFT) + +#define MII_CFG_RES (1 << 15) +#define MII_CFG_EN (1 << 14) +#define MII_CFG_LDCLKDIS (1 << 12) +#define MII_CFG_MIIRATE_SHIFT 4 +#define MII_CFG_MIIRATE_MASK (0x7 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M2P5 (0x0 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M25 (0x1 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M125 (0x2 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_M50 (0x3 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIRATE_AUTO (0x4 << MII_CFG_MIIRATE_SHIFT) +#define MII_CFG_MIIMODE_MASK 0xf +#define MII_CFG_MIIMODE_MIIP 0x0 +#define MII_CFG_MIIMODE_MIIM 0x1 +#define MII_CFG_MIIMODE_RMIIP 0x2 +#define MII_CFG_MIIMODE_RMIIM 0x3 +#define MII_CFG_MIIMODE_RGMII 0x4 + +#define PCDU_RXDLY_SHIFT 7 +#define PCDU_RXDLY_MASK (0x7 << PCDU_RXDLY_SHIFT) +#define PCDU_TXDLY_MASK 0x7 + +#define PMAC_HD_CTL_FC (1 << 10) +#define PMAC_HD_CTL_CCRC (1 << 9) +#define PMAC_HD_CTL_RST (1 << 8) +#define PMAC_HD_CTL_AST (1 << 7) +#define PMAC_HD_CTL_RXSH (1 << 6) +#define PMAC_HD_CTL_RC (1 << 4) +#define PMAC_HD_CTL_AS (1 << 3) +#define PMAC_HD_CTL_AC (1 << 2) + +#define PCE_PCTRL_0_IGSTEN (1 << 11) + +#define FDMA_PCTRL_STEN (1 << 1) +#define FDMA_PCTRL_EN (1 << 0) + +#define SDMA_PCTRL_EN (1 << 0) + +#define MDIO_GLOB_CTRL_SE (1 << 15) + +#define MDIO_MDC_CFG1_RES (1 << 15) +#define MDIO_MDC_CFG1_MCEN (1 << 8) + +#define MDIO_CTRL_MBUSY (1 << 12) +#define MDIO_CTRL_OP_READ (1 << 11) +#define MDIO_CTRL_OP_WRITE (1 << 10) +#define MDIO_CTRL_PHYAD_SHIFT 5 +#define MDIO_CTRL_PHYAD_MASK (0x1f << MDIO_CTRL_PHYAD_SHIFT) +#define MDIO_CTRL_REGAD_MASK 0x1f + +#endif /* __VRX200_SWITCH_H__ */ --- a/arch/mips/include/asm/asm.h +++ b/arch/mips/include/asm/asm.h @@ -53,6 +53,7 @@ .align 2; \ .type symbol, @function; \ .ent symbol, 0; \ + .section .text.symbol,"x"; \ symbol: .frame sp, 0, ra /* @@ -62,7 +63,8 @@ symbol: .frame sp, 0, ra .globl symbol; \ .align 2; \ .type symbol, @function; \ - .ent symbol, 0; \ + .ent symbol, 0; \ + .section .text.symbol,"x"; \ symbol: .frame sp, framesize, rpc /* --- /dev/null +++ b/arch/mips/include/asm/gpio.h @@ -0,0 +1,6 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include --- /dev/null +++ b/arch/mips/include/asm/lantiq/chipid.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_CHIPID_H__ +#define __LANTIQ_CHIPID_H__ + +enum ltq_chip_partnum { + LTQ_SOC_UNKNOWN = 0, + LTQ_SOC_VRX288_2 = 0x000B, /* VRX288 v1.2 */ + LTQ_SOC_VRX268_2 = 0x000C, /* VRX268 v1.2 */ + LTQ_SOC_GRX288_2 = 0x000D, /* GRX288 v1.2 */ + LTQ_SOC_DANUBE = 0x0129, + LTQ_SOC_DANUBE_S = 0x012B, + LTQ_SOC_TWINPASS = 0x012D, + LTQ_SOC_VRX288 = 0x01C0, /* VRX288 v1.1 */ + LTQ_SOC_VRX268 = 0x01C2, /* VRX268 v1.1 */ + LTQ_SOC_GRX288 = 0x01C9, /* GRX288 v1.1 */ +}; + +extern unsigned int ltq_chip_version_get(void); +extern unsigned int ltq_chip_partnum_get(void); +extern const char *ltq_chip_partnum_str(void); + +extern void ltq_chip_print_info(void); + +#ifdef CONFIG_SOC_XWAY_DANUBE +static inline int ltq_soc_is_danube(void) +{ + return 1; +} +#else +static inline int ltq_soc_is_danube(void) +{ + return 0; +} +#endif + +#ifdef CONFIG_SOC_XWAY_VRX200 +static inline int ltq_soc_is_vrx200(void) +{ + return 1; +} + +static inline int ltq_soc_is_vrx200_v1(void) +{ + return ltq_chip_version_get() == 1; +} + +static inline int ltq_soc_is_vrx200_v2(void) +{ + return ltq_chip_version_get() == 2; +} +#else +static inline int ltq_soc_is_vrx200(void) +{ + return 0; +} + +static inline int ltq_soc_is_vrx200_v1(void) +{ + return 0; +} + +static inline int ltq_soc_is_vrx200_v2(void) +{ + return 0; +} +#endif + +#endif /* __LANTIQ_CHIPID_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/clk.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_CLK_H__ +#define __LANTIQ_CLK_H__ + +/* Symbolic clock speeds */ +enum ltq_clk { + CLOCK_83_MHZ = 83333333, + CLOCK_111_MHZ = 111111111, + CLOCK_125_MHZ = 125000000, + CLOCK_133_MHZ = 133333333, + CLOCK_166_MHZ = 166666667, + CLOCK_197_MHZ = 197000000, + CLOCK_333_MHZ = 333333333, + CLOCK_393_MHZ = 393219000, + CLOCK_500_MHZ = 500000000, + CLOCK_600_MHZ = 600000000, + CLOCK_1000_MHZ = 1000000000, +}; + +extern unsigned long ltq_get_cpu_clock(void); +extern unsigned long ltq_get_bus_clock(void); +extern unsigned long ltq_get_io_region_clock(void); + +#endif /* __LANTIQ_CLK_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/config.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2007-2010 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_CONFIG_H__ +#define __LANTIQ_CONFIG_H__ + +/* Memory usage */ +#define CONFIG_SYS_MAXARGS 24 +#define CONFIG_SYS_MALLOC_LEN 1024*1024 +#define CONFIG_SYS_BOOTPARAMS_LEN 128*1024 + +/* Command line */ +#define CONFIG_SYS_PROMPT CONFIG_MACH_TYPE " # " +#define CONFIG_SYS_CBSIZE 512 +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ + sizeof(CONFIG_SYS_PROMPT)+16) + +#define CONFIG_SYS_HUSH_PARSER +#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " + +/* + * Enable advanced console features on demand to reduce + * flash and RAM footprint + */ +#if defined(CONFIG_LTQ_ADVANCED_CONSOLE) +#define CONFIG_SYS_LONGHELP +#define CONFIG_AUTO_COMPLETE +#define CONFIG_CMDLINE_EDITING +#endif + +/* SPI flash SPL */ +#if defined(CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH) && defined(CONFIG_SYS_BOOT_SFSPL) +#define CONFIG_SPL +#define CONFIG_SPL_SPI_SUPPORT +#define CONFIG_SPL_SPI_FLASH_SUPPORT +#define CONFIG_SPI_SPL_SIMPLE +#endif + +#if defined(CONFIG_LTQ_SUPPORT_SPL_NOR_FLASH) && defined(CONFIG_SYS_BOOT_NORSPL) +#define CONFIG_SPL +#endif + +/* Common SPL */ +#if defined(CONFIG_SPL) +#define CONFIG_SKIP_LOWLEVEL_INIT +#define CONFIG_SPL_LIBGENERIC_SUPPORT +#define CONFIG_SPL_GPIO_SUPPORT +#define CONFIG_SPL_START_S_PATH \ + "arch/mips/cpu/mips32/lantiq-common" +#define CONFIG_SPL_LDSCRIPT \ + "arch/mips/cpu/mips32/lantiq-common/u-boot-spl.lds" +#endif + +#if defined(CONFIG_LTQ_SPL_CONSOLE) +#define CONFIG_SPL_SERIAL_SUPPORT +#define CONFIG_SPL_LIBCOMMON_SUPPORT +#endif + +#if defined(CONFIG_LTQ_SPL_COMP_LZMA) +#define CONFIG_LZMA +#define CONFIG_SPL_LZMA_SUPPORT +#endif + +#if defined(CONFIG_LTQ_SPL_COMP_LZO) +#define CONFIG_LZO +#define CONFIG_SPL_LZO_SUPPORT +#endif + +/* Basic commands */ +#define CONFIG_CMD_BDI +#define CONFIG_CMD_EDITENV +#define CONFIG_CMD_IMI +#define CONFIG_CMD_MEMORY +#define CONFIG_CMD_RUN +#define CONFIG_CMD_SAVEENV +#define CONFIG_CMD_LOADB + +/* Other U-Boot settings */ +#define CONFIG_TIMESTAMP + +/* Default environment */ +#define CONFIG_ENV_CONSOLEDEV \ + "consoledev=" CONFIG_CONSOLE_DEV "\0" + +#define CONFIG_ENV_ADDCONSOLE \ + "addconsole=setenv bootargs $bootargs" \ + " console=$consoledev,$baudrate\0" + +#if defined(CONFIG_NET_DEV) +#define CONFIG_ENV_NETDEV \ + "netdev=" CONFIG_NET_DEV "\0" +#else +#define CONFIG_ENV_NETDEV \ + "netdev=eth0\0" +#endif + +#define CONFIG_ENV_ADDIP \ + "addip=setenv bootargs $bootargs" \ + " ip=$ipaddr:$serverip::::$netdev:off\0" + +#define CONFIG_ENV_ADDETH \ + "addeth=setenv bootargs $bootargs" \ + " ethaddr=$ethaddr\0" + +#define CONFIG_ENV_ADDMACHTYPE \ + "addmachtype=setenv bootargs $bootargs" \ + " machtype=" CONFIG_MACH_TYPE "\0" + +#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) +#define CONFIG_ENV_WRITE_UBOOT_NOR \ + "write-uboot-nor=" \ + "protect off " __stringify(CONFIG_SYS_FLASH_BASE) " +$filesize && " \ + "erase " __stringify(CONFIG_SYS_FLASH_BASE) " +$filesize && " \ + "cp.b $fileaddr " __stringify(CONFIG_SYS_FLASH_BASE) " $filesize\0" + +#define CONFIG_ENV_LOAD_UBOOT_NOR \ + "load-uboot-nor=tftpboot u-boot.bin\0" \ + "load-uboot-norspl=tftpboot u-boot.ltq.norspl\0" \ + "load-uboot-norspl-lzo=tftpboot u-boot.ltq.lzo.norspl\0" \ + "load-uboot-norspl-lzma=tftpboot u-boot.ltq.lzma.norspl\0" +#else +#define CONFIG_ENV_WRITE_UBOOT_NOR +#define CONFIG_ENV_LOAD_UBOOT_NOR +#endif + +#if defined(CONFIG_LTQ_SUPPORT_SPI_FLASH) +#define CONFIG_ENV_SF_PROBE \ + "sf-probe=sf probe " __stringify(CONFIG_ENV_SPI_CS) " " \ + __stringify(CONFIG_ENV_SPI_MAX_HZ) " " \ + __stringify(CONFIG_ENV_SPI_MODE) " \0" + +#define CONFIG_ENV_WRITE_UBOOT_SF \ + "write-uboot-sf=" \ + "run sf-probe && sf erase 0 +$filesize && " \ + "sf write $fileaddr 0 $filesize\0" + +#define CONFIG_ENV_LOAD_UBOOT_SF \ + "load-uboot-sfspl=tftpboot u-boot.ltq.sfspl\0" \ + "load-uboot-sfspl-lzo=tftpboot u-boot.ltq.lzo.sfspl\0" \ + "load-uboot-sfspl-lzma=tftpboot u-boot.ltq.lzma.sfspl\0" +#else +#define CONFIG_ENV_SF_PROBE +#define CONFIG_ENV_WRITE_UBOOT_SF +#define CONFIG_ENV_LOAD_UBOOT_SF +#endif + +#define CONFIG_ENV_LANTIQ_DEFAULTS \ + CONFIG_ENV_CONSOLEDEV \ + CONFIG_ENV_ADDCONSOLE \ + CONFIG_ENV_NETDEV \ + CONFIG_ENV_ADDIP \ + CONFIG_ENV_ADDETH \ + CONFIG_ENV_ADDMACHTYPE \ + CONFIG_ENV_WRITE_UBOOT_NOR \ + CONFIG_ENV_LOAD_UBOOT_NOR \ + CONFIG_ENV_SF_PROBE \ + CONFIG_ENV_WRITE_UBOOT_SF \ + CONFIG_ENV_LOAD_UBOOT_SF + +#endif /* __LANTIQ_CONFIG_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/cpu.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_CPU_H__ +#define __LANTIQ_CPU_H__ + +enum ltq_boot_select { + BOOT_NOR, + BOOT_NOR_NO_BOOTROM, + BOOT_UART, + BOOT_UART_NO_EEPROM, + BOOT_SPI, + BOOT_NAND, + BOOT_PCI, + BOOT_MII0, + BOOT_RMII0, + BOOT_RGMII1, + BOOT_UNKNOWN, +}; + +enum ltq_boot_select ltq_boot_select(void); +const char *ltq_boot_select_str(void); + +void ltq_pmu_init(void); +void ltq_ebu_init(void); +void ltq_gpio_init(void); + +void ltq_pll_init(void); +void ltq_dcdc_init(unsigned int dig_ref); + +#endif /* __LANTIQ_CPU_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/dma.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_DMA_H__ +#define __LANTIQ_DMA_H__ + +enum ltq_dma_endianess { + LTQ_DMA_ENDIANESS_B0_B1_B2_B3, /* No byte swapping */ + LTQ_DMA_ENDIANESS_B1_B0_B3_B2, /* B0B1B2B3 => B1B0B3B2 */ + LTQ_DMA_ENDIANESS_B2_B3_B0_B1, /* B0B1B2B3 => B2B3B0B1 */ + LTQ_DMA_ENDIANESS_B3_B2_B1_B0, /* B0B1B2B3 => B3B2B1B0 */ +}; + +enum ltq_dma_burst_len { + LTQ_DMA_BURST_2WORDS = 1, + LTQ_DMA_BURST_4WORDS = 2, + LTQ_DMA_BURST_8WORDS = 3, +}; + +struct ltq_dma_desc { + u32 ctl; + u32 addr; +}; + +struct ltq_dma_channel { + struct ltq_dma_device *dev; + u8 chan_no; + u8 class; + u16 num_desc; + struct ltq_dma_desc *desc_base; + void *mem_base; + u32 dma_addr; +}; + +struct ltq_dma_device { + enum ltq_dma_endianess rx_endian_swap; + enum ltq_dma_endianess tx_endian_swap; + enum ltq_dma_burst_len rx_burst_len; + enum ltq_dma_burst_len tx_burst_len; + struct ltq_dma_channel rx_chan; + struct ltq_dma_channel tx_chan; + u8 port; +}; + +/** + * Initialize DMA hardware and driver + */ +void ltq_dma_init(void); + +/** + * Register given DMA client context + * + * @returns 0 on success, negative value otherwise + */ +int ltq_dma_register(struct ltq_dma_device *dev); + +/** + * Reset and halt all channels related to given DMA client + */ +void ltq_dma_reset(struct ltq_dma_device *dev); +void ltq_dma_enable(struct ltq_dma_device *dev); +void ltq_dma_disable(struct ltq_dma_device *dev); + +/** + * Map RX DMA descriptor to memory region + * + * @returns 0 on success, negative value otherwise + */ +int ltq_dma_rx_map(struct ltq_dma_device *dev, int index, void *data, int len); + +/** + * Check if new data is available. + * + * @returns length of received data, 0 otherwise + */ +int ltq_dma_rx_poll(struct ltq_dma_device *dev, int index); + +int ltq_dma_rx_length(struct ltq_dma_device *dev, int index); + +/** + * Map TX DMA descriptor to memory region + * + * @returns 0 on success, negative value otherwise + */ +int ltq_dma_tx_map(struct ltq_dma_device *dev, int index, void *data, int len, + unsigned long timeout); + +int ltq_dma_tx_wait(struct ltq_dma_device *dev, int index, + unsigned long timeout); + +#endif /* __LANTIQ_DMA_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/eth.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_ETH_H__ +#define __LANTIQ_ETH_H__ + +#include + +enum LTQ_ETH_PORT_FLAGS { + LTQ_ETH_PORT_NONE = 0, + LTQ_ETH_PORT_PHY = 1, + LTQ_ETH_PORT_SWITCH = (1 << 1), + LTQ_ETH_PORT_MAC = (1 << 2), +}; + +struct ltq_eth_port_config { + u8 num; + u8 phy_addr; + u16 flags; + phy_interface_t phy_if; + u8 rgmii_rx_delay; + u8 rgmii_tx_delay; +}; + +struct ltq_eth_board_config { + const struct ltq_eth_port_config *ports; + int num_ports; +}; + +extern int ltq_eth_initialize(const struct ltq_eth_board_config *board_config); + +#endif /* __LANTIQ_ETH_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/gpio.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_GPIO_H__ +#define __LANTIQ_GPIO_H__ + +enum ltq_gpio_dir { + GPIO_DIR_IN = 0, + GPIO_DIR_OUT +}; + +enum ltq_gpio_od { + GPIO_OD_ACTIVE = 0, + GPIO_OD_NORMAL +}; + +enum ltq_gpio_altsel { + GPIO_ALTSEL_CLR = 0, + GPIO_ALTSEL_SET +}; + +extern int gpio_set_altfunc(unsigned gpio, int altsel0, int altsel1, int dir); +extern int gpio_set_opendrain(unsigned gpio, int od); + +static inline int gpio_to_port(unsigned gpio) +{ + return gpio >> 4; +} + +static inline int gpio_to_pin(unsigned gpio) +{ + return gpio & 0xF; +} + +static inline int gpio_to_bit(unsigned gpio) +{ + return 1 << gpio_to_pin(gpio); +} + +static inline int gpio_to_gpio(unsigned port, unsigned pin) +{ + return (port << 4) | (pin & 0xF); +} + +#include + +#endif /* __LANTIQ_GPIO_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/io.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 John Crispin + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_IO_H__ +#define __LANTIQ_IO_H__ + +#include + +#define ltq_readb(a) __raw_readb(a) +#define ltq_writeb(a, v) __raw_writeb(v, a) + +#define ltq_readl(a) __raw_readl(a) +#define ltq_writel(a, v) __raw_writel(v, a) + +#define ltq_clrbits(a, clear) \ + ltq_writel(a, ltq_readl(a) & ~(clear)) + +#define ltq_setbits(a, set) \ + ltq_writel(a, ltq_readl(a) | (set)) + +#define ltq_clrsetbits(a, clear, set) \ + ltq_writel(a, (ltq_readl(a) & ~(clear)) | (set)) + +static inline void ltq_reg_dump(const void *addr, const char *desc) +{ + u32 data; + + data = ltq_readl(addr); + printf("ltq_reg_dump: %s 0x%p = 0x%08x\n", + desc, addr, data); +} + +#endif /* __LANTIQ_IO_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/pm.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_PM_H__ +#define __LANTIQ_PM_H__ + +enum ltq_pm_modules { + LTQ_PM_CORE, + LTQ_PM_DMA, + LTQ_PM_ETH, + LTQ_PM_SPI, +}; + +u32 ltq_pm_map(enum ltq_pm_modules module); +int ltq_pm_enable(enum ltq_pm_modules module); +int ltq_pm_disable(enum ltq_pm_modules module); + +#endif /* __LANTIQ_PM_H__ */ --- /dev/null +++ b/arch/mips/include/asm/lantiq/reset.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LANTIQ_RESET_H__ +#define __LANTIQ_RESET_H__ + +enum ltq_reset_modules { + LTQ_RESET_CORE, + LTQ_RESET_DMA, + LTQ_RESET_ETH, + LTQ_RESET_PHY, + LTQ_RESET_HARD, + LTQ_RESET_SOFT, +}; + +extern u32 ltq_reset_map(enum ltq_reset_modules module); +extern int ltq_reset_activate(enum ltq_reset_modules module); +extern int ltq_reset_deactivate(enum ltq_reset_modules module); + +static inline int ltq_reset_once(enum ltq_reset_modules module, ulong usec) +{ + int ret; + + ret = ltq_reset_activate(module); + if (ret) + return ret; + + __udelay(usec); + ret = ltq_reset_deactivate(module); + + return ret; +} + +#endif /* __LANTIQ_RESET_H__ */ --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -46,7 +46,10 @@ #define CP0_ENTRYLO1 $3 #define CP0_CONF $3 #define CP0_CONTEXT $4 +#define CP0_CONTEXTCONFIG $4,1 +#define CP0_USERLOCAL $4,1 #define CP0_PAGEMASK $5 +#define CP0_PAGEGRAIN $5,1 #define CP0_WIRED $6 #define CP0_INFO $7 #define CP0_BADVADDR $8 @@ -54,10 +57,19 @@ #define CP0_ENTRYHI $10 #define CP0_COMPARE $11 #define CP0_STATUS $12 +#define CP0_INTCTL $12,1 +#define CP0_SRSCTL $12,2 +#define CP0_SRSMAP $12,3 +#define CP0_SRSHIGH $12,4 #define CP0_CAUSE $13 #define CP0_EPC $14 #define CP0_PRID $15 +#define CP0_EBASE $15,1 #define CP0_CONFIG $16 +#define CP0_CONFIG1 $16,1 +#define CP0_CONFIG2 $16,2 +#define CP0_CONFIG3 $16,3 +#define CP0_CONFIG7 $16,7 #define CP0_LLADDR $17 #define CP0_WATCHLO $18 #define CP0_WATCHHI $19 @@ -70,7 +82,17 @@ #define CP0_ECC $26 #define CP0_CACHEERR $27 #define CP0_TAGLO $28 +#define CP0_ITAGLO $28 +#define CP0_IDATALO $28,1 +#define CP0_DTAGLO $28,2 +#define CP0_DDATALO $28,3 +#define CP0_L23TAGLO $28,4 +#define CP0_L23DATALO $28,5 #define CP0_TAGHI $29 +#define CP0_IDATAHI $29,1 +#define CP0_DTAGHI $29,2 +#define CP0_L23TAGHI $29,4 +#define CP0_L23DATAHI $29,5 #define CP0_ERROREPC $30 #define CP0_DESAVE $31 @@ -395,6 +417,12 @@ #define CAUSEF_BD (_ULCAST_(1) << 31) /* + * Bits in the coprocessor 0 EBase register. + */ +#define EBASEB_CPUNUM 0 +#define EBASEF_CPUNUM (_ULCAST_(1023)) + +/* * Bits in the coprocessor 0 config register. */ /* Generic bits. */ --- a/arch/mips/include/asm/u-boot-mips.h +++ b/arch/mips/include/asm/u-boot-mips.h @@ -23,3 +23,4 @@ static inline unsigned long image_copy_e } extern int incaip_set_cpuclk(void); +extern int arch_cpu_init(void); --- a/arch/mips/lib/board.c +++ b/arch/mips/lib/board.c @@ -33,6 +33,16 @@ static char *failed = "*** failed ***\n" */ const unsigned long mips_io_port_base = -1; +int __arch_cpu_init(void) +{ + /* + * Nothing to do in this dummy implementation + */ + return 0; +} +int arch_cpu_init(void) + __attribute__((weak, alias("__arch_cpu_init"))); + int __board_early_init_f(void) { /* @@ -106,6 +116,7 @@ static int init_baudrate(void) typedef int (init_fnc_t)(void); init_fnc_t *init_sequence[] = { + arch_cpu_init, board_early_init_f, timer_init, env_init, /* initialize environment */ --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -12,6 +12,7 @@ LIB := $(obj)libdma.o COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o +COBJS-$(CONFIG_LANTIQ_DMA) += lantiq_dma.o COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o COBJS := $(COBJS-y) --- /dev/null +++ b/drivers/dma/lantiq_dma.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DMA_CTRL_PKTARB (1 << 31) +#define DMA_CTRL_MBRSTARB (1 << 30) +#define DMA_CTRL_MBRSTCNT_SHIFT 16 +#define DMA_CTRL_MBRSTCNT_MASK (0x3ff << DMA_CTRL_MBRSTCNT_SHIFT) +#define DMA_CTRL_DRB (1 << 8) +#define DMA_CTRL_RESET (1 << 0) + +#define DMA_CPOLL_EN (1 << 31) +#define DMA_CPOLL_CNT_SHIFT 4 +#define DMA_CPOLL_CNT_MASK (0xFFF << DMA_CPOLL_CNT_SHIFT) + +#define DMA_CCTRL_TXWGT_SHIFT 16 +#define DMA_CCTRL_TXWGT_MASK (0x3 << DMA_CCTRL_TXWGT_SHIFT) +#define DMA_CCTRL_CLASS_SHIFT 9 +#define DMA_CCTRL_CLASS_MASK (0x3 << DMA_CCTRL_CLASS_SHIFT) +#define DMA_CCTRL_RST (1 << 1) +#define DMA_CCTRL_ONOFF (1 << 0) + +#define DMA_PCTRL_TXBL_SHIFT 4 +#define DMA_PCTRL_TXBL_2WORDS (1 << DMA_PCTRL_TXBL_SHIFT) +#define DMA_PCTRL_TXBL_4WORDS (2 << DMA_PCTRL_TXBL_SHIFT) +#define DMA_PCTRL_TXBL_8WORDS (3 << DMA_PCTRL_TXBL_SHIFT) +#define DMA_PCTRL_RXBL_SHIFT 2 +#define DMA_PCTRL_RXBL_2WORDS (1 << DMA_PCTRL_RXBL_SHIFT) +#define DMA_PCTRL_RXBL_4WORDS (2 << DMA_PCTRL_RXBL_SHIFT) +#define DMA_PCTRL_RXBL_8WORDS (3 << DMA_PCTRL_RXBL_SHIFT) +#define DMA_PCTRL_TXENDI_SHIFT 10 +#define DMA_PCTRL_TXENDI_MASK (0x3 << DMA_PCTRL_TXENDI_SHIFT) +#define DMA_PCTRL_RXENDI_SHIFT 8 +#define DMA_PCTRL_RXENDI_MASK (0x3 << DMA_PCTRL_RXENDI_SHIFT) + +#define DMA_DESC_OWN (1 << 31) +#define DMA_DESC_C (1 << 30) +#define DMA_DESC_SOP (1 << 29) +#define DMA_DESC_EOP (1 << 28) +#define DMA_DESC_TX_OFFSET(x) ((x & 0x1f) << 23) +#define DMA_DESC_RX_OFFSET(x) ((x & 0x3) << 23) +#define DMA_DESC_LENGTH(x) (x & 0xffff) + +#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) + +struct ltq_dma_regs { + u32 clc; /* Clock control */ + u32 rsvd0; + u32 id; /* Identification */ + u32 rsvd1; + u32 ctrl; /* Control */ + u32 cpoll; /* Channel polling */ + u32 cs; /* Channel select */ + u32 cctrl; /* Channel control */ + u32 cdba; /* Channel descriptor base address */ + u32 cdlen; /* Channel descriptor length */ + u32 cis; /* Channel interrupt status */ + u32 cie; /* Channel interrupt enable */ + u32 cgbl; /* Channel global buffer length */ + u32 cdptnrd; /* Current descriptor pointer */ + u32 rsvd2[2]; + u32 ps; /* Port select */ + u32 pctrl; /* Port control */ + u32 rsvd3[43]; + u32 irnen; /* Interrupt node enable */ + u32 irncr; /* Interrupt node control */ + u32 irnicr; /* Interrupt capture */ +}; + +static struct ltq_dma_regs *ltq_dma_regs = + (struct ltq_dma_regs *) CKSEG1ADDR(LTQ_DMA_BASE); + +static inline unsigned long ltq_dma_addr_to_virt(u32 dma_addr) +{ + return KSEG0ADDR(dma_addr); +} + +static inline u32 ltq_virt_to_dma_addr(void *addr) +{ + return CPHYSADDR(addr); +} + +static inline int ltq_dma_burst_align(enum ltq_dma_burst_len burst_len) +{ + switch (burst_len) { + case LTQ_DMA_BURST_2WORDS: + return 2 * 4; + case LTQ_DMA_BURST_4WORDS: + return 4 * 4; + case LTQ_DMA_BURST_8WORDS: + return 8 * 4; + } + + return 0; +} + +static inline void ltq_dma_sync(void) +{ + __asm__ __volatile__("sync"); +} + +static inline void ltq_dma_dcache_wb_inv(const void *ptr, size_t size) +{ + unsigned long addr = (unsigned long) ptr; + + flush_dcache_range(addr, addr + size); + ltq_dma_sync(); +} + +static inline void ltq_dma_dcache_inv(const void *ptr, size_t size) +{ + unsigned long addr = (unsigned long) ptr; + + invalidate_dcache_range(addr, addr + size); +} + +void ltq_dma_init(void) +{ + /* Power up DMA */ + ltq_pm_enable(LTQ_PM_DMA); + + /* Reset DMA */ + ltq_setbits(<q_dma_regs->ctrl, DMA_CTRL_RESET); + + /* Disable and clear all interrupts */ + ltq_writel(<q_dma_regs->irnen, 0); + ltq_writel(<q_dma_regs->irncr, 0xFFFFF); + +#if 0 + /* Enable packet arbitration */ + ltq_setbits(<q_dma_regs->ctrl, DMA_CTRL_PKTARB); +#endif + +#if 0 + /* Enable descriptor read back */ + ltq_setbits(<q_dma_regs->ctrl, DMA_CTRL_DRB); +#endif + + /* Enable polling for descriptor fetching for all channels */ + ltq_writel(<q_dma_regs->cpoll, DMA_CPOLL_EN | + (4 << DMA_CPOLL_CNT_SHIFT)); +} + +static void ltq_dma_channel_reset(struct ltq_dma_channel *chan) +{ + ltq_writel(<q_dma_regs->cs, chan->chan_no); + ltq_setbits(<q_dma_regs->cctrl, DMA_CCTRL_RST); +} + +static void ltq_dma_channel_enable(struct ltq_dma_channel *chan) +{ + ltq_writel(<q_dma_regs->cs, chan->chan_no); + ltq_setbits(<q_dma_regs->cctrl, DMA_CCTRL_ONOFF); +} + +static void ltq_dma_channel_disable(struct ltq_dma_channel *chan) +{ + ltq_writel(<q_dma_regs->cs, chan->chan_no); + ltq_clrbits(<q_dma_regs->cctrl, DMA_CCTRL_ONOFF); +} + +static void ltq_dma_port_init(struct ltq_dma_device *dev) +{ + u32 pctrl; + + pctrl = dev->tx_endian_swap << DMA_PCTRL_TXENDI_SHIFT; + pctrl |= dev->rx_endian_swap << DMA_PCTRL_RXENDI_SHIFT; + pctrl |= dev->tx_burst_len << DMA_PCTRL_TXBL_SHIFT; + pctrl |= dev->rx_burst_len << DMA_PCTRL_RXBL_SHIFT; + + ltq_writel(<q_dma_regs->ps, dev->port); + ltq_writel(<q_dma_regs->pctrl, pctrl); +} + +static int ltq_dma_alloc_descriptors(struct ltq_dma_device *dev, + struct ltq_dma_channel *chan) +{ + size_t size; + void *desc_base; + + size = ALIGN(sizeof(struct ltq_dma_desc) * chan->num_desc + + ARCH_DMA_MINALIGN, ARCH_DMA_MINALIGN); + + chan->mem_base = malloc(size); + if (!chan->mem_base) + return 1; + + memset(chan->mem_base, 0, size); + ltq_dma_dcache_wb_inv(chan->mem_base, size); + + desc_base = PTR_ALIGN(chan->mem_base, ARCH_DMA_MINALIGN); + + debug("DMA: mem %p, desc %p\n", chan->mem_base, desc_base); + + /* Align descriptor base to 8 bytes */ + chan->desc_base = (void *) CKSEG1ADDR(desc_base); + chan->dma_addr = CPHYSADDR(desc_base); + chan->dev = dev; + + debug("DMA: desc_base %p, size %u\n", chan->desc_base, size); + + /* Configure hardware with location of descriptor list */ + ltq_writel(<q_dma_regs->cs, chan->chan_no); + ltq_writel(<q_dma_regs->cdba, chan->dma_addr); + ltq_writel(<q_dma_regs->cdlen, chan->num_desc); + ltq_writel(<q_dma_regs->cctrl, (3 << DMA_CCTRL_TXWGT_SHIFT) | + (chan->class << DMA_CCTRL_CLASS_SHIFT)); + ltq_writel(<q_dma_regs->cctrl, DMA_CCTRL_RST); + + return 0; +} + +static void ltq_dma_free_descriptors(struct ltq_dma_channel *chan) +{ + ltq_writel(<q_dma_regs->cs, chan->chan_no); + ltq_writel(<q_dma_regs->cdba, 0); + ltq_writel(<q_dma_regs->cdlen, 0); + + ltq_dma_channel_reset(chan); + + free(chan->mem_base); +} + +int ltq_dma_register(struct ltq_dma_device *dev) +{ + int ret; + + ltq_dma_port_init(dev); + + ret = ltq_dma_alloc_descriptors(dev, &dev->rx_chan); + if (ret) + return ret; + + ret = ltq_dma_alloc_descriptors(dev, &dev->tx_chan); + if (ret) { + ltq_dma_free_descriptors(&dev->rx_chan); + return ret; + } + + return 0; +} + +void ltq_dma_reset(struct ltq_dma_device *dev) +{ + ltq_dma_channel_reset(&dev->rx_chan); + ltq_dma_channel_reset(&dev->tx_chan); +} + +void ltq_dma_enable(struct ltq_dma_device *dev) +{ + ltq_dma_channel_enable(&dev->rx_chan); + ltq_dma_channel_enable(&dev->tx_chan); +} + +void ltq_dma_disable(struct ltq_dma_device *dev) +{ + ltq_dma_channel_disable(&dev->rx_chan); + ltq_dma_channel_disable(&dev->tx_chan); +} + +int ltq_dma_rx_map(struct ltq_dma_device *dev, int index, void *data, int len) +{ + struct ltq_dma_channel *chan = &dev->rx_chan; + struct ltq_dma_desc *desc = &chan->desc_base[index]; + u32 dma_addr = ltq_virt_to_dma_addr(data); + unsigned int offset; + + offset = dma_addr % ltq_dma_burst_align(dev->rx_burst_len); + + ltq_dma_dcache_inv(data, len); + +#if 0 + printf("%s: index %d, data %p, dma_addr %08x, offset %u, len %d\n", + __func__, index, data, dma_addr, offset, len); +#endif + + + desc->addr = dma_addr - offset; + desc->ctl = DMA_DESC_OWN | DMA_DESC_RX_OFFSET(offset) | + DMA_DESC_LENGTH(len); + +#if 0 + printf("%s: index %d, desc %p, desc->ctl %08x\n", + __func__, index, desc, desc->ctl); +#endif + + return 0; +} + +int ltq_dma_rx_poll(struct ltq_dma_device *dev, int index) +{ + struct ltq_dma_channel *chan = &dev->rx_chan; + struct ltq_dma_desc *desc = &chan->desc_base[index]; + +#if 0 + printf("%s: index %d, desc %p, desc->ctl %08x\n", + __func__, index, desc, desc->ctl); +#endif + + if (desc->ctl & DMA_DESC_OWN) + return 0; + + if (desc->ctl & DMA_DESC_C) + return 1; + + return 0; +} + +int ltq_dma_rx_length(struct ltq_dma_device *dev, int index) +{ + struct ltq_dma_channel *chan = &dev->rx_chan; + struct ltq_dma_desc *desc = &chan->desc_base[index]; + + return DMA_DESC_LENGTH(desc->ctl); +} + +int ltq_dma_tx_map(struct ltq_dma_device *dev, int index, void *data, int len, + unsigned long timeout) +{ + struct ltq_dma_channel *chan = &dev->tx_chan; + struct ltq_dma_desc *desc = &chan->desc_base[index]; + unsigned int offset; + unsigned long timebase = get_timer(0); + u32 dma_addr = ltq_virt_to_dma_addr(data); + + while (desc->ctl & DMA_DESC_OWN) { + WATCHDOG_RESET(); + + if (get_timer(timebase) >= timeout) { +#if 0 + printf("%s: timeout: index %d, desc %p, desc->ctl %08x\n", + __func__, index, desc, desc->ctl); +#endif + return -1; + } + } + + offset = dma_addr % ltq_dma_burst_align(dev->rx_burst_len); + +#if 0 + printf("%s: index %d, desc %p, data %p, dma_addr %08x, offset %u, len %d\n", + __func__, index, desc, data, dma_addr, offset, len); +#endif + + ltq_dma_dcache_wb_inv(data, len); + + desc->addr = dma_addr - offset; + desc->ctl = DMA_DESC_OWN | DMA_DESC_SOP | DMA_DESC_EOP | + DMA_DESC_TX_OFFSET(offset) | DMA_DESC_LENGTH(len); + +#if 0 + printf("%s: index %d, desc %p, desc->ctl %08x\n", + __func__, index, desc, desc->ctl); +#endif + + return 0; +} + +int ltq_dma_tx_wait(struct ltq_dma_device *dev, int index, + unsigned long timeout) +{ + struct ltq_dma_channel *chan = &dev->tx_chan; + struct ltq_dma_desc *desc = &chan->desc_base[index]; + unsigned long timebase = get_timer(0); + + while ((desc->ctl & (DMA_DESC_OWN | DMA_DESC_C)) != DMA_DESC_C) { + WATCHDOG_RESET(); + + if (get_timer(timebase) >= timeout) + return -1; + } + + return 0; +} --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -12,6 +12,7 @@ LIB := $(obj)libgpio.o COBJS-$(CONFIG_AT91_GPIO) += at91_gpio.o COBJS-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o COBJS-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o +COBJS-$(CONFIG_LANTIQ_GPIO) += lantiq_gpio.o COBJS-$(CONFIG_MARVELL_GPIO) += mvgpio.o COBJS-$(CONFIG_MARVELL_MFP) += mvmfp.o COBJS-$(CONFIG_MXC_GPIO) += mxc_gpio.o --- /dev/null +++ b/drivers/gpio/lantiq_gpio.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define SSIO_GPIO_BASE 64 + +#define SSIO_CON0_SWU (1 << 31) +#define SSIO_CON0_RZFL (1 << 26) +#define SSIO_CON0_GPHY1_SHIFT 27 +#define SSIO_CON0_GPHY1_CONFIG ((CONFIG_LTQ_SSIO_GPHY1_MODE & 0x7) << 27) + +#define SSIO_CON1_US_FPI (2 << 30) +#define SSIO_CON1_FPID_2HZ (0 << 23) +#define SSIO_CON1_FPID_4HZ (1 << 23) +#define SSIO_CON1_FPID_8HZ (2 << 23) +#define SSIO_CON1_FPID_10HZ (3 << 23) +#define SSIO_CON1_FPIS_1_2 (1 << 20) +#define SSIO_CON1_FPIS_1_32 (2 << 20) +#define SSIO_CON1_FPIS_1_64 (3 << 20) + +#define SSIO_CON1_GPHY2_SHIFT 15 +#define SSIO_CON1_GPHY2_CONFIG ((CONFIG_LTQ_SSIO_GPHY2_MODE & 0x7) << 15) + +#define SSIO_CON1_GROUP2 (1 << 2) +#define SSIO_CON1_GROUP1 (1 << 1) +#define SSIO_CON1_GROUP0 (1 << 0) +#define SSIO_CON1_GROUP_CONFIG (0x3) + +#ifdef CONFIG_LTQ_SSIO_SHIFT_REGS +#define enable_ssio 1 +#else +#define enable_ssio 0 + +#define CONFIG_LTQ_SSIO_GPHY1_MODE 0 +#define CONFIG_LTQ_SSIO_GPHY2_MODE 0 +#define CONFIG_LTQ_SSIO_INIT_VALUE 0 +#endif + +#ifdef CONFIG_LTQ_SSIO_EDGE_FALLING +#define SSIO_RZFL_CONFIG SSIO_CON0_RZFL +#else +#define SSIO_RZFL_CONFIG 0 +#endif + +struct ltq_gpio_port_regs { + __be32 out; + __be32 in; + __be32 dir; + __be32 altsel0; + __be32 altsel1; + __be32 od; + __be32 stoff; + __be32 pudsel; + __be32 puden; + __be32 rsvd1[3]; +}; + +struct ltq_gpio_regs { + u32 rsvd[4]; + struct ltq_gpio_port_regs ports[CONFIG_LTQ_GPIO_MAX_BANKS]; +}; + +struct ltq_gpio3_regs { + u32 rsvd0[13]; + __be32 od; + __be32 pudsel; + __be32 puden; + u32 rsvd1[9]; + __be32 altsel1; + u32 rsvd2[14]; + __be32 out; + __be32 in; + __be32 dir; + __be32 altsel0; +}; + +struct ltq_ssio_regs { + __be32 con0; + __be32 con1; + __be32 cpu0; + __be32 cpu1; + __be32 ar; +}; + +static struct ltq_gpio_regs *ltq_gpio_regs = + (struct ltq_gpio_regs *) CKSEG1ADDR(LTQ_GPIO_BASE); + +static struct ltq_gpio3_regs *ltq_gpio3_regs = + (struct ltq_gpio3_regs *) CKSEG1ADDR(LTQ_GPIO_BASE); + +static struct ltq_ssio_regs *ltq_ssio_regs = + (struct ltq_ssio_regs *) CKSEG1ADDR(LTQ_SSIO_BASE); + +static int is_gpio_bank3(unsigned int port) +{ +#ifdef CONFIG_LTQ_HAS_GPIO_BANK3 + return port == 3; +#else + return 0; +#endif +} + +static int is_gpio_ssio(unsigned int gpio) +{ +#ifdef CONFIG_LTQ_SSIO_SHIFT_REGS + return gpio >= SSIO_GPIO_BASE; +#else + return 0; +#endif +} + +static inline int ssio_gpio_to_bit(unsigned gpio) +{ + return 1 << (gpio - SSIO_GPIO_BASE); +} + +int ltq_gpio_init(void) +{ + ltq_writel(<q_ssio_regs->ar, 0); + ltq_writel(<q_ssio_regs->cpu0, CONFIG_LTQ_SSIO_INIT_VALUE); + ltq_writel(<q_ssio_regs->cpu1, 0); + ltq_writel(<q_ssio_regs->con0, SSIO_CON0_SWU); + + if (enable_ssio) { + ltq_writel(<q_ssio_regs->con0, SSIO_CON0_GPHY1_CONFIG | + SSIO_RZFL_CONFIG); + ltq_writel(<q_ssio_regs->con1, SSIO_CON1_US_FPI | + SSIO_CON1_FPID_8HZ | SSIO_CON1_GPHY2_CONFIG | + SSIO_CON1_GROUP_CONFIG); + } + + return 0; +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + unsigned port = gpio_to_port(gpio); + const void *gpio_od = <q_gpio_regs->ports[port].od; + const void *gpio_altsel0 = <q_gpio_regs->ports[port].altsel0; + const void *gpio_altsel1 = <q_gpio_regs->ports[port].altsel1; + const void *gpio_dir = <q_gpio_regs->ports[port].dir; + + if (is_gpio_ssio(gpio)) + return 0; + + if (is_gpio_bank3(port)) { + gpio_od = <q_gpio3_regs->od; + gpio_altsel0 = <q_gpio3_regs->altsel0; + gpio_altsel1 = <q_gpio3_regs->altsel1; + gpio_dir = <q_gpio3_regs->dir; + } + + /* + * Reset open drain and altsel configs to workaround improper + * reset values or unwanted modifications by BootROM + */ + ltq_clrbits(gpio_od, gpio_to_bit(gpio)); + ltq_clrbits(gpio_altsel0, gpio_to_bit(gpio)); + ltq_clrbits(gpio_altsel1, gpio_to_bit(gpio)); + + /* Switch to input */ + ltq_clrbits(gpio_dir, gpio_to_bit(gpio)); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned port = gpio_to_port(gpio); + const void *gpio_od = <q_gpio_regs->ports[port].od; + const void *gpio_altsel0 = <q_gpio_regs->ports[port].altsel0; + const void *gpio_altsel1 = <q_gpio_regs->ports[port].altsel1; + const void *gpio_dir = <q_gpio_regs->ports[port].dir; + const void *gpio_out = <q_gpio_regs->ports[port].out; + u32 data = gpio_to_bit(gpio); + + if (is_gpio_ssio(gpio)) { + data = ssio_gpio_to_bit(gpio); + if (value) + ltq_setbits(<q_ssio_regs->cpu0, data); + else + ltq_clrbits(<q_ssio_regs->cpu0, data); + + return 0; + } + + if (is_gpio_bank3(port)) { + gpio_od = <q_gpio3_regs->od; + gpio_altsel0 = <q_gpio3_regs->altsel0; + gpio_altsel1 = <q_gpio3_regs->altsel1; + gpio_dir = <q_gpio3_regs->dir; + gpio_out = <q_gpio3_regs->out; + } + + /* + * Reset open drain and altsel configs to workaround improper + * reset values or unwanted modifications by BootROM + */ + ltq_setbits(gpio_od, data); + ltq_clrbits(gpio_altsel0, data); + ltq_clrbits(gpio_altsel1, data); + + if (value) + ltq_setbits(gpio_out, data); + else + ltq_clrbits(gpio_out, data); + + /* Switch to output */ + ltq_setbits(gpio_dir, data); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + unsigned port = gpio_to_port(gpio); + const void *gpio_in = <q_gpio_regs->ports[port].in; + u32 data = gpio_to_bit(gpio); + u32 val; + + if (is_gpio_ssio(gpio)) { + gpio_in = <q_ssio_regs->cpu0; + data = ssio_gpio_to_bit(gpio); + } + + if (is_gpio_bank3(port)) + gpio_in = <q_gpio3_regs->in; + + val = ltq_readl(gpio_in); + + return !!(val & data); +} + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned port = gpio_to_port(gpio); + const void *gpio_out = <q_gpio_regs->ports[port].out; + u32 data = gpio_to_bit(gpio); + + if (is_gpio_ssio(gpio)) { + gpio_out = <q_ssio_regs->cpu0; + data = ssio_gpio_to_bit(gpio); + } + + if (is_gpio_bank3(port)) + gpio_out = <q_gpio3_regs->out; + + if (value) + ltq_setbits(gpio_out, data); + else + ltq_clrbits(gpio_out, data); + + return 0; +} + +int gpio_set_altfunc(unsigned gpio, int altsel0, int altsel1, int dir) +{ + unsigned port = gpio_to_port(gpio); + const void *gpio_od = <q_gpio_regs->ports[port].od; + const void *gpio_altsel0 = <q_gpio_regs->ports[port].altsel0; + const void *gpio_altsel1 = <q_gpio_regs->ports[port].altsel1; + const void *gpio_dir = <q_gpio_regs->ports[port].dir; + + if (is_gpio_ssio(gpio)) + return 0; + + if (is_gpio_bank3(port)) { + gpio_od = <q_gpio3_regs->od; + gpio_altsel0 = <q_gpio3_regs->altsel0; + gpio_altsel1 = <q_gpio3_regs->altsel1; + gpio_dir = <q_gpio3_regs->dir; + } + + if (altsel0) + ltq_setbits(gpio_altsel0, gpio_to_bit(gpio)); + else + ltq_clrbits(gpio_altsel0, gpio_to_bit(gpio)); + + if (altsel1) + ltq_setbits(gpio_altsel1, gpio_to_bit(gpio)); + else + ltq_clrbits(gpio_altsel1, gpio_to_bit(gpio)); + + if (dir) { + ltq_setbits(gpio_od, gpio_to_bit(gpio)); + ltq_setbits(gpio_dir, gpio_to_bit(gpio)); + } else { + ltq_clrbits(gpio_od, gpio_to_bit(gpio)); + ltq_clrbits(gpio_dir, gpio_to_bit(gpio)); + } + + return 0; +} + +int gpio_set_opendrain(unsigned gpio, int od) +{ + unsigned port = gpio_to_port(gpio); + const void *gpio_od = <q_gpio_regs->ports[port].od; + + if (is_gpio_ssio(gpio)) + return 0; + + if (is_gpio_bank3(port)) + gpio_od = <q_gpio3_regs->od; + + if (od) + ltq_setbits(gpio_od, gpio_to_bit(gpio)); + else + ltq_clrbits(gpio_od, gpio_to_bit(gpio)); + + return 0; +} --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -161,6 +161,18 @@ u64 flash_read64(void *addr)__attribute_ #define flash_read64 __flash_read64 #endif +static inline void *__flash_swap_addr(unsigned long addr) +{ + return (void *) addr; +} + +#ifdef CONFIG_CFI_FLASH_USE_WEAK_ADDR_SWAP +void *flash_swap_addr(unsigned long addr) + __attribute__((weak, alias("__flash_swap_addr"))); +#else +#define flash_swap_addr __flash_swap_addr +#endif + /*----------------------------------------------------------------------- */ #if defined(CONFIG_ENV_IS_IN_FLASH) || defined(CONFIG_ENV_ADDR_REDUND) || (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) @@ -196,7 +208,7 @@ flash_map (flash_info_t * info, flash_se { unsigned int byte_offset = offset * info->portwidth; - return (void *)(info->start[sect] + byte_offset); + return flash_swap_addr(info->start[sect] + byte_offset); } static inline void flash_unmap(flash_info_t *info, flash_sect_t sect, --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -53,6 +53,7 @@ COBJS-$(CONFIG_NAND_JZ4740) += jz4740_na COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o +COBJS-$(CONFIG_NAND_LANTIQ) += lantiq_nand.o COBJS-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o COBJS-$(CONFIG_NAND_MXC) += mxc_nand.o COBJS-$(CONFIG_NAND_MXS) += mxs_nand.o --- /dev/null +++ b/drivers/mtd/nand/lantiq_nand.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define NAND_CON_ECC_ON (1 << 31) +#define NAND_CON_LATCH_PRE (1 << 23) +#define NAND_CON_LATCH_WP (1 << 22) +#define NAND_CON_LATCH_SE (1 << 21) +#define NAND_CON_LATCH_CS (1 << 20) +#define NAND_CON_LATCH_CLE (1 << 19) +#define NAND_CON_LATCH_ALE (1 << 18) +#define NAND_CON_OUT_CS1 (1 << 10) +#define NAND_CON_IN_CS1 (1 << 8) +#define NAND_CON_PRE_P (1 << 7) +#define NAND_CON_WP_P (1 << 6) +#define NAND_CON_SE_P (1 << 5) +#define NAND_CON_CS_P (1 << 4) +#define NAND_CON_CLE_P (1 << 3) +#define NAND_CON_ALE_P (1 << 2) +#define NAND_CON_CSMUX (1 << 1) +#define NAND_CON_NANDM (1 << 0) + +#define NAND_WAIT_WR_C (1 << 3) +#define NAND_WAIT_RDBY (1 << 0) + +#define NAND_CMD_ALE (1 << 2) +#define NAND_CMD_CLE (1 << 3) +#define NAND_CMD_CS (1 << 4) +#define NAND_CMD_SE (1 << 5) +#define NAND_CMD_WP (1 << 6) +#define NAND_CMD_PRE (1 << 7) + +struct ltq_nand_regs { + __be32 con; /* NAND controller control */ + __be32 wait; /* NAND Flash Device RD/BY State */ + __be32 ecc0; /* NAND Flash ECC Register 0 */ + __be32 ecc_ac; /* NAND Flash ECC Register address counter */ + __be32 ecc_cr; /* NAND Flash ECC Comparison */ +}; + +static struct ltq_nand_regs *ltq_nand_regs = + (struct ltq_nand_regs *) CKSEG1ADDR(LTQ_EBU_NAND_BASE); + +static void ltq_nand_wait_ready(void) +{ + while ((ltq_readl(<q_nand_regs->wait) & NAND_WAIT_WR_C) == 0) + ; +} + +static int ltq_nand_dev_ready(struct mtd_info *mtd) +{ + u32 data = ltq_readl(<q_nand_regs->wait); + return data & NAND_WAIT_RDBY; +} + +static void ltq_nand_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip == 0) { + ltq_setbits(<q_nand_regs->con, NAND_CON_NANDM); + ltq_setbits(<q_nand_regs->con, NAND_CON_LATCH_CS); + } else { + ltq_clrbits(<q_nand_regs->con, NAND_CON_LATCH_CS); + ltq_clrbits(<q_nand_regs->con, NAND_CON_NANDM); + } +} + +static void ltq_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + unsigned long addr = (unsigned long) chip->IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + addr |= NAND_CMD_ALE; + else + addr &= ~NAND_CMD_ALE; + + if (ctrl & NAND_CLE) + addr |= NAND_CMD_CLE; + else + addr &= ~NAND_CMD_CLE; + + chip->IO_ADDR_W = (void __iomem *) addr; + } + + if (cmd != NAND_CMD_NONE) { + writeb(cmd, chip->IO_ADDR_W); + ltq_nand_wait_ready(); + } +} + +int ltq_nand_init(struct nand_chip *nand) +{ + /* Enable NAND, set NAND CS to EBU CS1, enable EBU CS mux */ + ltq_writel(<q_nand_regs->con, NAND_CON_OUT_CS1 | NAND_CON_IN_CS1 | + NAND_CON_PRE_P | NAND_CON_WP_P | NAND_CON_SE_P | + NAND_CON_CS_P | NAND_CON_CSMUX); + + nand->dev_ready = ltq_nand_dev_ready; + nand->select_chip = ltq_nand_select_chip; + nand->cmd_ctrl = ltq_nand_cmd_ctrl; + + nand->chip_delay = 30; + nand->options = 0; + nand->ecc.mode = NAND_ECC_SOFT; + + /* Enable CS bit in address offset */ + nand->IO_ADDR_R = nand->IO_ADDR_R + NAND_CMD_CS; + nand->IO_ADDR_W = nand->IO_ADDR_W + NAND_CMD_CS; + + return 0; +} + +__weak int board_nand_init(struct nand_chip *chip) +{ + return ltq_nand_init(chip); +} --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -37,6 +37,8 @@ COBJS-$(CONFIG_INCA_IP_SWITCH) += inca-i COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o COBJS-$(CONFIG_KS8851_MLL) += ks8851_mll.o COBJS-$(CONFIG_LAN91C96) += lan91c96.o +COBJS-$(CONFIG_LANTIQ_DANUBE_ETOP) += lantiq_danube_etop.o +COBJS-$(CONFIG_LANTIQ_VRX200_SWITCH) += lantiq_vrx200_switch.o COBJS-$(CONFIG_MACB) += macb.o COBJS-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o COBJS-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o --- /dev/null +++ b/drivers/net/lantiq_danube_etop.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTQ_PPE_ETOP_MDIO_ACC_RA (1 << 31) +#define LTQ_PPE_ETOP_MDIO_CFG_UMM1 (1 << 2) +#define LTQ_PPE_ETOP_MDIO_CFG_UMM0 (1 << 1) + +#define LTQ_PPE_ETOP_CFG_TCKINV1 (1 << 11) +#define LTQ_PPE_ETOP_CFG_TCKINV0 (1 << 10) +#define LTQ_PPE_ETOP_CFG_FEN1 (1 << 9) +#define LTQ_PPE_ETOP_CFG_FEN0 (1 << 8) +#define LTQ_PPE_ETOP_CFG_SEN1 (1 << 7) +#define LTQ_PPE_ETOP_CFG_SEN0 (1 << 6) +#define LTQ_PPE_ETOP_CFG_TURBO1 (1 << 5) +#define LTQ_PPE_ETOP_CFG_REMII1 (1 << 4) +#define LTQ_PPE_ETOP_CFG_OFF1 (1 << 3) +#define LTQ_PPE_ETOP_CFG_TURBO0 (1 << 2) +#define LTQ_PPE_ETOP_CFG_REMII0 (1 << 1) +#define LTQ_PPE_ETOP_CFG_OFF0 (1 << 0) + +#define LTQ_PPE_ENET0_MAC_CFG_CGEN (1 << 11) +#define LTQ_PPE_ENET0_MAC_CFG_DUPLEX (1 << 2) +#define LTQ_PPE_ENET0_MAC_CFG_SPEED (1 << 1) +#define LTQ_PPE_ENET0_MAC_CFG_LINK (1 << 0) + +#define LTQ_PPE_ENETS0_CFG_FTUC (1 << 28) + +#define LTQ_ETH_RX_BUFFER_CNT PKTBUFSRX +#define LTQ_ETH_TX_BUFFER_CNT 8 +#define LTQ_ETH_RX_DATA_SIZE PKTSIZE_ALIGN +#define LTQ_ETH_IP_ALIGN 2 + +#define LTQ_MDIO_DRV_NAME "ltq-mdio" +#define LTQ_ETH_DRV_NAME "ltq-eth" + +struct ltq_ppe_etop_regs { + u32 mdio_cfg; /* MDIO configuration */ + u32 mdio_acc; /* MDIO access */ + u32 cfg; /* ETOP configuration */ + u32 ig_vlan_cos; /* IG VLAN priority CoS mapping */ + u32 ig_dscp_cos3; /* IG DSCP CoS mapping 3 */ + u32 ig_dscp_cos2; /* IG DSCP CoS mapping 2 */ + u32 ig_dscp_cos1; /* IG DSCP CoS mapping 1 */ + u32 ig_dscp_cos0; /* IG DSCP CoS mapping 0 */ + u32 ig_plen_ctrl; /* IG frame length control */ + u32 rsvd0[3]; + u32 vpid; /* VLAN protocol ID */ +}; + +struct ltq_ppe_enet_regs { + u32 mac_cfg; /* MAC configuration */ + u32 rsvd0[3]; + u32 ig_cfg; /* Ingress configuration */ + u32 ig_pgcnt; /* Ingress buffer used page count */ + u32 rsvd1; + u32 ig_buf_ctrl; /* Ingress buffer backpressure ctrl */ + u32 cos_cfg; /* Classification configuration */ + u32 ig_drop; /* Total ingress drop frames */ + u32 ig_err; /* Total ingress error frames */ + u32 mac_da0; /* Ingress MAC address 0 */ + u32 mac_da1; /* Ingress MAC address 1 */ + u32 rsvd2[22]; + u32 pgcnt; /* Page counter */ + u32 rsvd3; + u32 hf_ctrl; /* Half duplex control */ + u32 tx_ctrl; /* Transmit control */ + u32 rsvd4; + u32 vlcos0; /* VLAN insertion config CoS 0 */ + u32 vlcos1; /* VLAN insertion config CoS 1 */ + u32 vlcos2; /* VLAN insertion config CoS 2 */ + u32 vlcos3; /* VLAN insertion config CoS 3 */ + u32 eg_col; /* Total egress collision frames */ + u32 eg_drop; /* Total egress drop frames */ +}; + +struct ltq_eth_priv { + struct ltq_dma_device dma_dev; + struct mii_dev *bus; + struct eth_device *dev; + int rx_num; + int tx_num; +}; + +struct ltq_mdio_access { + union { + struct { + unsigned ra:1; + unsigned rw:1; + unsigned rsvd:4; + unsigned phya:5; + unsigned rega:5; + unsigned phyd:16; + } reg; + u32 val; + }; +}; + +static struct ltq_ppe_etop_regs *ltq_ppe_etop_regs = + (struct ltq_ppe_etop_regs *) CKSEG1ADDR(LTQ_PPE_ETOP_BASE); + +static struct ltq_ppe_enet_regs *ltq_ppe_enet0_regs = + (struct ltq_ppe_enet_regs *) CKSEG1ADDR(LTQ_PPE_ENET0_BASE); + +static inline int ltq_mdio_poll(void) +{ + struct ltq_mdio_access acc; + unsigned cnt = 10000; + + while (likely(cnt--)) { + acc.val = ltq_readl(<q_ppe_etop_regs->mdio_acc); + if (!acc.reg.ra) + return 0; + } + + return 1; +} + +static int ltq_mdio_read(struct mii_dev *bus, int addr, int dev_addr, + int regnum) +{ + struct ltq_mdio_access acc; + int ret; + + acc.val = 0; + acc.reg.ra = 1; + acc.reg.rw = 1; + acc.reg.phya = addr; + acc.reg.rega = regnum; + + ret = ltq_mdio_poll(); + if (ret) + return ret; + + ltq_writel(<q_ppe_etop_regs->mdio_acc, acc.val); + + ret = ltq_mdio_poll(); + if (ret) + return ret; + + acc.val = ltq_readl(<q_ppe_etop_regs->mdio_acc); + + return acc.reg.phyd; +} + +static int ltq_mdio_write(struct mii_dev *bus, int addr, int dev_addr, + int regnum, u16 val) +{ + struct ltq_mdio_access acc; + int ret; + + acc.val = 0; + acc.reg.ra = 1; + acc.reg.rw = 0; + acc.reg.phya = addr; + acc.reg.rega = regnum; + acc.reg.phyd = val; + + ret = ltq_mdio_poll(); + if (ret) + return ret; + + ltq_writel(<q_ppe_etop_regs->mdio_acc, acc.val); + + return 0; +} + +static inline void ltq_eth_write_hwaddr(const struct eth_device *dev) +{ + u32 da0, da1; + + da0 = (dev->enetaddr[0] << 24) + (dev->enetaddr[1] << 16) + + (dev->enetaddr[2] << 8) + dev->enetaddr[3]; + da1 = (dev->enetaddr[4] << 24) + (dev->enetaddr[5] << 16); + + ltq_writel(<q_ppe_enet0_regs->mac_da0, da0); + ltq_writel(<q_ppe_enet0_regs->mac_da1, da1); +} + +static inline u8 *ltq_eth_rx_packet_align(int rx_num) +{ + u8 *packet = (u8 *) NetRxPackets[rx_num]; + + /* + * IP header needs + */ + return packet + LTQ_ETH_IP_ALIGN; +} + +static int ltq_eth_init(struct eth_device *dev, bd_t *bis) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + int i; + + ltq_eth_write_hwaddr(dev); + + for (i = 0; i < LTQ_ETH_RX_BUFFER_CNT; i++) + ltq_dma_rx_map(dma_dev, i, ltq_eth_rx_packet_align(i), + LTQ_ETH_RX_DATA_SIZE); + + ltq_dma_enable(dma_dev); + + priv->rx_num = 0; + priv->tx_num = 0; + + return 0; +} + +static void ltq_eth_halt(struct eth_device *dev) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + + ltq_dma_reset(dma_dev); +} + +static int ltq_eth_send(struct eth_device *dev, void *packet, int length) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + int err; + + /* Minimum payload length w/ CRC is 60 bytes */ + if (length < 60) + length = 60; + + err = ltq_dma_tx_map(dma_dev, priv->tx_num, packet, length, 10); + if (err) { + puts("NET: timeout on waiting for TX descriptor\n"); + return -1; + } + + priv->tx_num = (priv->tx_num + 1) % LTQ_ETH_TX_BUFFER_CNT; + + return err; +} + +static int ltq_eth_recv(struct eth_device *dev) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + u8 *packet; + int len; + + if (!ltq_dma_rx_poll(dma_dev, priv->rx_num)) + return 0; + +#if 0 + printf("%s: rx_num %d\n", __func__, priv->rx_num); +#endif + + len = ltq_dma_rx_length(dma_dev, priv->rx_num); + packet = ltq_eth_rx_packet_align(priv->rx_num); + +#if 0 + printf("%s: received: packet %p, len %u, rx_num %d\n", + __func__, packet, len, priv->rx_num); +#endif + + if (len) + NetReceive(packet, len); + + ltq_dma_rx_map(dma_dev, priv->rx_num, packet, + LTQ_ETH_RX_DATA_SIZE); + + priv->rx_num = (priv->rx_num + 1) % LTQ_ETH_RX_BUFFER_CNT; + + return 0; +} + +static void ltq_eth_hw_init(const struct ltq_eth_port_config *port) +{ + u32 data; + + /* Power up ethernet subsystems */ + ltq_pm_enable(LTQ_PM_ETH); + + /* Reset ethernet subsystems */ + ltq_reset_once(LTQ_RESET_ETH, 1); + + /* Disable MDIO auto-detection */ + ltq_clrbits(<q_ppe_etop_regs->mdio_cfg, LTQ_PPE_ETOP_MDIO_CFG_UMM1 | + LTQ_PPE_ETOP_MDIO_CFG_UMM0); + + /* Enable CRC generation, Full Duplex, 100Mbps, Link up */ + ltq_writel(<q_ppe_enet0_regs->mac_cfg, LTQ_PPE_ENET0_MAC_CFG_CGEN | + LTQ_PPE_ENET0_MAC_CFG_DUPLEX | + LTQ_PPE_ENET0_MAC_CFG_SPEED | + LTQ_PPE_ENET0_MAC_CFG_LINK); + + /* Reset ETOP cfg and disable all */ + data = LTQ_PPE_ETOP_CFG_OFF0 | LTQ_PPE_ETOP_CFG_OFF1; + + /* Enable ENET0, enable store and fetch */ + data &= ~LTQ_PPE_ETOP_CFG_OFF0; + data |= LTQ_PPE_ETOP_CFG_SEN0 | LTQ_PPE_ETOP_CFG_FEN0; + + if (port->phy_if == PHY_INTERFACE_MODE_RMII) + data |= LTQ_PPE_ETOP_CFG_REMII0; + else + data &= ~LTQ_PPE_ETOP_CFG_REMII0; + + ltq_writel(<q_ppe_etop_regs->cfg, data); + + /* Set allowed packet length from 64 bytes to 1518 bytes */ + ltq_writel(<q_ppe_etop_regs->ig_plen_ctrl, (64 << 16) | 1518); + + /* Enable filter for unicast packets */ + ltq_setbits(<q_ppe_enet0_regs->ig_cfg, LTQ_PPE_ENETS0_CFG_FTUC); +} + +int ltq_eth_initialize(const struct ltq_eth_board_config *board_config) +{ + struct eth_device *dev; + struct mii_dev *bus; + struct ltq_eth_priv *priv; + struct ltq_dma_device *dma_dev; + const struct ltq_eth_port_config *port = &board_config->ports[0]; + struct phy_device *phy; + struct switch_device *sw; + int ret; + + ltq_dma_init(); + ltq_eth_hw_init(port); + + dev = calloc(1, sizeof(*dev)); + if (!dev) + return -1; + + priv = calloc(1, sizeof(*priv)); + if (!priv) + return -1; + + bus = mdio_alloc(); + if (!bus) + return -1; + + sprintf(dev->name, LTQ_ETH_DRV_NAME); + dev->priv = priv; + dev->init = ltq_eth_init; + dev->halt = ltq_eth_halt; + dev->recv = ltq_eth_recv; + dev->send = ltq_eth_send; + + sprintf(bus->name, LTQ_MDIO_DRV_NAME); + bus->read = ltq_mdio_read; + bus->write = ltq_mdio_write; + bus->priv = priv; + + dma_dev = &priv->dma_dev; + dma_dev->port = 0; + dma_dev->rx_chan.chan_no = 6; + dma_dev->rx_chan.class = 3; + dma_dev->rx_chan.num_desc = LTQ_ETH_RX_BUFFER_CNT; + dma_dev->rx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0; + dma_dev->rx_burst_len = LTQ_DMA_BURST_2WORDS; + dma_dev->tx_chan.chan_no = 7; + dma_dev->tx_chan.class = 3; + dma_dev->tx_chan.num_desc = LTQ_ETH_TX_BUFFER_CNT; + dma_dev->tx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0; + dma_dev->tx_burst_len = LTQ_DMA_BURST_2WORDS; + + priv->bus = bus; + priv->dev = dev; + + ret = ltq_dma_register(dma_dev); + if (ret) + return ret; + + ret = mdio_register(bus); + if (ret) + return ret; + + ret = eth_register(dev); + if (ret) + return ret; + + if (port->flags & LTQ_ETH_PORT_SWITCH) { + sw = switch_connect(bus); + if (!sw) + return -1; + + switch_setup(sw); + } + + if (port->flags & LTQ_ETH_PORT_PHY) { + phy = phy_connect(bus, port->phy_addr, dev, port->phy_if); + if (!phy) + return -1; + + phy_config(phy); + } + + return 0; +} --- /dev/null +++ b/drivers/net/lantiq_vrx200_switch.c @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2010-2011 Lantiq Deutschland GmbH + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTQ_ETH_RX_BUFFER_CNT PKTBUFSRX +#define LTQ_ETH_TX_BUFFER_CNT 8 +#define LTQ_ETH_RX_DATA_SIZE PKTSIZE_ALIGN +#define LTQ_ETH_IP_ALIGN 2 + +#define LTQ_MDIO_DRV_NAME "ltq-mdio" +#define LTQ_ETH_DRV_NAME "ltq-eth" + +#define LTQ_ETHSW_MAX_GMAC 6 +#define LTQ_ETHSW_PMAC 6 + +struct ltq_mdio_phy_addr_reg { + union { + struct { + unsigned rsvd:1; + unsigned lnkst:2; /* Link status control */ + unsigned speed:2; /* Speed control */ + unsigned fdup:2; /* Full duplex control */ + unsigned fcontx:2; /* Flow control mode TX */ + unsigned fconrx:2; /* Flow control mode RX */ + unsigned addr:5; /* PHY address */ + } bits; + u16 val; + }; +}; + +enum ltq_mdio_phy_addr_lnkst { + LTQ_MDIO_PHY_ADDR_LNKST_AUTO = 0, + LTQ_MDIO_PHY_ADDR_LNKST_UP = 1, + LTQ_MDIO_PHY_ADDR_LNKST_DOWN = 2, +}; + +enum ltq_mdio_phy_addr_speed { + LTQ_MDIO_PHY_ADDR_SPEED_M10 = 0, + LTQ_MDIO_PHY_ADDR_SPEED_M100 = 1, + LTQ_MDIO_PHY_ADDR_SPEED_G1 = 2, + LTQ_MDIO_PHY_ADDR_SPEED_AUTO = 3, +}; + +enum ltq_mdio_phy_addr_fdup { + LTQ_MDIO_PHY_ADDR_FDUP_AUTO = 0, + LTQ_MDIO_PHY_ADDR_FDUP_ENABLE = 1, + LTQ_MDIO_PHY_ADDR_FDUP_DISABLE = 3, +}; + +enum ltq_mdio_phy_addr_fcon { + LTQ_MDIO_PHY_ADDR_FCON_AUTO = 0, + LTQ_MDIO_PHY_ADDR_FCON_ENABLE = 1, + LTQ_MDIO_PHY_ADDR_FCON_DISABLE = 3, +}; + +struct ltq_mii_mii_cfg_reg { + union { + struct { + unsigned res:1; /* Hardware reset */ + unsigned en:1; /* xMII interface enable */ + unsigned isol:1; /* xMII interface isolate */ + unsigned ldclkdis:1; /* Link down clock disable */ + unsigned rsvd:1; + unsigned crs:2; /* CRS sensitivity config */ + unsigned rgmii_ibs:1; /* RGMII In Band status */ + unsigned rmii:1; /* RMII ref clock direction */ + unsigned miirate:3; /* xMII interface clock rate */ + unsigned miimode:4; /* xMII interface mode */ + } bits; + u16 val; + }; +}; + +enum ltq_mii_mii_cfg_miirate { + LTQ_MII_MII_CFG_MIIRATE_M2P5 = 0, + LTQ_MII_MII_CFG_MIIRATE_M25 = 1, + LTQ_MII_MII_CFG_MIIRATE_M125 = 2, + LTQ_MII_MII_CFG_MIIRATE_M50 = 3, + LTQ_MII_MII_CFG_MIIRATE_AUTO = 4, +}; + +enum ltq_mii_mii_cfg_miimode { + LTQ_MII_MII_CFG_MIIMODE_MIIP = 0, + LTQ_MII_MII_CFG_MIIMODE_MIIM = 1, + LTQ_MII_MII_CFG_MIIMODE_RMIIP = 2, + LTQ_MII_MII_CFG_MIIMODE_RMIIM = 3, + LTQ_MII_MII_CFG_MIIMODE_RGMII = 4, +}; + +struct ltq_eth_priv { + struct ltq_dma_device dma_dev; + struct mii_dev *bus; + struct eth_device *dev; + struct phy_device *phymap[LTQ_ETHSW_MAX_GMAC]; + int rx_num; + int tx_num; +}; + +static struct vr9_switch_regs *switch_regs = + (struct vr9_switch_regs *) CKSEG1ADDR(LTQ_SWITCH_BASE); + +static inline void vr9_switch_sync(void) +{ + __asm__("sync"); +} + +static inline int vr9_switch_mdio_is_busy(void) +{ + u32 mdio_ctrl = ltq_readl(&switch_regs->mdio.mdio_ctrl); + + return mdio_ctrl & MDIO_CTRL_MBUSY; +} + +static inline void vr9_switch_mdio_poll(void) +{ + while (vr9_switch_mdio_is_busy()) + cpu_relax(); +} + +static int vr9_switch_mdio_read(struct mii_dev *bus, int phyad, int devad, + int regad) +{ + u32 mdio_ctrl; + int retval; + + mdio_ctrl = MDIO_CTRL_OP_READ | + ((phyad << MDIO_CTRL_PHYAD_SHIFT) & MDIO_CTRL_PHYAD_MASK) | + (regad & MDIO_CTRL_REGAD_MASK); + + vr9_switch_mdio_poll(); + ltq_writel(&switch_regs->mdio.mdio_ctrl, mdio_ctrl); + vr9_switch_mdio_poll(); + retval = ltq_readl(&switch_regs->mdio.mdio_read); + + return retval; +} + +static int vr9_switch_mdio_write(struct mii_dev *bus, int phyad, int devad, + int regad, u16 val) +{ + u32 mdio_ctrl; + + mdio_ctrl = MDIO_CTRL_OP_WRITE | + ((phyad << MDIO_CTRL_PHYAD_SHIFT) & MDIO_CTRL_PHYAD_MASK) | + (regad & MDIO_CTRL_REGAD_MASK); + + vr9_switch_mdio_poll(); + ltq_writel(&switch_regs->mdio.mdio_write, val); + ltq_writel(&switch_regs->mdio.mdio_ctrl, mdio_ctrl); + + return 0; +} + +static void ltq_eth_gmac_update(struct phy_device *phydev, int num) +{ + struct ltq_mdio_phy_addr_reg phy_addr_reg; + struct ltq_mii_mii_cfg_reg mii_cfg_reg; + + phy_addr_reg.val = ltq_readl(to_mdio_phyaddr(switch_regs, num)); + + switch (num) { + case 0: + case 1: + case 5: + mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs, num)); + break; + default: + mii_cfg_reg.val = 0; + break; + } + + phy_addr_reg.bits.addr = phydev->addr; + + if (phydev->link) + phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_UP; + else + phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_DOWN; + + switch (phydev->speed) { + case SPEED_1000: + phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_G1; + mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M125; + break; + case SPEED_100: + phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M100; + switch (mii_cfg_reg.bits.miimode) { + case LTQ_MII_MII_CFG_MIIMODE_RMIIM: + case LTQ_MII_MII_CFG_MIIMODE_RMIIP: + mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M50; + break; + default: + mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M25; + break; + } + break; + default: + phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M10; + mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M2P5; + break; + } + + if (phydev->duplex == DUPLEX_FULL) + phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_ENABLE; + else + phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_DISABLE; + + ltq_writel(to_mdio_phyaddr(switch_regs, num), phy_addr_reg.val); + + switch (num) { + case 0: + case 1: + case 5: + ltq_writel(to_mii_miicfg(switch_regs, num), mii_cfg_reg.val); + break; + default: + break; + } +} + +static inline u8 *ltq_eth_rx_packet_align(int rx_num) +{ + u8 *packet = (u8 *) NetRxPackets[rx_num]; + + /* + * IP header needs + */ + return packet + LTQ_ETH_IP_ALIGN; +} + +static int ltq_eth_init(struct eth_device *dev, bd_t *bis) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + struct phy_device *phydev; + int i; + + for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) { + phydev = priv->phymap[i]; + if (!phydev) + continue; + + phy_startup(phydev); + ltq_eth_gmac_update(phydev, i); + } + + for (i = 0; i < LTQ_ETH_RX_BUFFER_CNT; i++) + ltq_dma_rx_map(dma_dev, i, ltq_eth_rx_packet_align(i), + LTQ_ETH_RX_DATA_SIZE); + + ltq_dma_enable(dma_dev); + + priv->rx_num = 0; + priv->tx_num = 0; + + return 0; +} + +static void ltq_eth_halt(struct eth_device *dev) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + struct phy_device *phydev; + int i; + + ltq_dma_reset(dma_dev); + + for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) { + phydev = priv->phymap[i]; + if (!phydev) + continue; + + phy_shutdown(phydev); + phydev->link = 0; + ltq_eth_gmac_update(phydev, i); + } +} + +static int ltq_eth_send(struct eth_device *dev, void *packet, int length) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + +#if 0 + printf("%s: packet %p, len %d\n", __func__, packet, length); +#endif + + ltq_dma_tx_map(dma_dev, priv->tx_num, packet, length, 10); + priv->tx_num = (priv->tx_num + 1) % LTQ_ETH_TX_BUFFER_CNT; + + return 0; +} + +static int ltq_eth_recv(struct eth_device *dev) +{ + struct ltq_eth_priv *priv = dev->priv; + struct ltq_dma_device *dma_dev = &priv->dma_dev; + u8 *packet; + int len; + + if (!ltq_dma_rx_poll(dma_dev, priv->rx_num)) + return 0; + +#if 0 + printf("%s: rx_num %d\n", __func__, priv->rx_num); +#endif + + len = ltq_dma_rx_length(dma_dev, priv->rx_num); + packet = ltq_eth_rx_packet_align(priv->rx_num); + +#if 0 + printf("%s: received: packet %p, len %u, rx_num %d\n", + __func__, packet, len, priv->rx_num); +#endif + + if (len) + NetReceive(packet, len); + + ltq_dma_rx_map(dma_dev, priv->rx_num, packet, + LTQ_ETH_RX_DATA_SIZE); + + priv->rx_num = (priv->rx_num + 1) % LTQ_ETH_RX_BUFFER_CNT; + + return 0; +} + +static void ltq_eth_gmac_init(int num) +{ + struct ltq_mdio_phy_addr_reg phy_addr_reg; + struct ltq_mii_mii_cfg_reg mii_cfg_reg; + + /* Reset PHY status to link down */ + phy_addr_reg.val = ltq_readl(to_mdio_phyaddr(switch_regs, num)); + phy_addr_reg.bits.addr = num; + phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_DOWN; + phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M10; + phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_DISABLE; + ltq_writel(to_mdio_phyaddr(switch_regs, num), phy_addr_reg.val); + + /* Reset and disable MII interface */ + switch (num) { + case 0: + case 1: + case 5: + mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs, num)); + mii_cfg_reg.bits.en = 0; + mii_cfg_reg.bits.res = 1; + mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M2P5; + ltq_writel(to_mii_miicfg(switch_regs, num), mii_cfg_reg.val); + break; + default: + break; + } + + /* + * - enable frame checksum generation + * - enable padding of short frames + * - disable flow control + */ + ltq_writel(to_mac_ctrl(switch_regs, num, 0), + MAC_CTRL0_PADEN | MAC_CTRL0_FCS | MAC_CTRL0_FCON_NONE); + + vr9_switch_sync(); +} + +static void ltq_eth_pmac_init(void) +{ + /* + * WAR: buffer congestion: + * - shorten preambel to 1 byte + * - set TX IPG to 7 bytes + */ +#if 1 + ltq_writel(to_mac_ctrl(switch_regs, LTQ_ETHSW_PMAC, 1), + MAC_CTRL1_SHORTPRE | 7); +#endif + + /* + * WAR: systematical concept weakness ACM bug + * - set maximum number of used buffer segments to 254 + * - soft-reset BM FSQM + */ +#if 1 + ltq_writel(&switch_regs->bm.core.fsqm_gctrl, 253); + ltq_setbits(&switch_regs->bm.core.gctrl, BM_GCTRL_F_SRES); + ltq_clrbits(&switch_regs->bm.core.gctrl, BM_GCTRL_F_SRES); +#endif + + /* + * WAR: switch MAC drop bug + */ +#if 1 + ltq_writel(to_pce_tbl_key(switch_regs, 0), 0xf); + ltq_writel(to_pce_tbl_value(switch_regs, 0), 0x40); + ltq_writel(&switch_regs->pce.core.tbl_addr, 0x3); + ltq_writel(&switch_regs->pce.core.tbl_ctrl, 0x902f); +#endif + + /* + * Configure frame header control: + * - enable flow control + * - enable CRC check for packets from DMA to PMAC + * - remove special tag from packets from PMAC to DMA + * - add CRC for packets from DMA to PMAC + */ + ltq_writel(&switch_regs->pmac.hd_ctl, /*PMAC_HD_CTL_FC |*/ + PMAC_HD_CTL_CCRC | PMAC_HD_CTL_RST | PMAC_HD_CTL_AC | + PMAC_HD_CTL_RC); + +#if 1 + ltq_writel(&switch_regs->pmac.rx_ipg, 0x8b); +#endif + + /* + * - enable frame checksum generation + * - enable padding of short frames + * - disable flow control + */ + ltq_writel(to_mac_ctrl(switch_regs, LTQ_ETHSW_PMAC, 0), + MAC_CTRL0_PADEN | MAC_CTRL0_FCS | MAC_CTRL0_FCON_NONE); + + vr9_switch_sync(); +} + +static void ltq_eth_hw_init(void) +{ + int i; + + /* Power up ethernet and switch subsystems */ + ltq_pm_enable(LTQ_PM_ETH); + + /* Reset ethernet and switch subsystems */ +#if 0 + ltq_reset_once(LTQ_RESET_ETH, 10); +#endif + + /* Enable switch macro */ + ltq_setbits(&switch_regs->mdio.glob_ctrl, MDIO_GLOB_CTRL_SE); + + /* Disable MDIO auto-polling for all ports */ + ltq_writel(&switch_regs->mdio.mdc_cfg_0, 0); + + /* + * Enable and set MDIO management clock to 2.5 MHz. This is the + * maximum clock for FE PHYs. + * Formula for clock is: + * + * 50 MHz + * x = ----------- - 1 + * 2 * f_MDC + */ + ltq_writel(&switch_regs->mdio.mdc_cfg_1, MDIO_MDC_CFG1_RES | + MDIO_MDC_CFG1_MCEN | 5); + + vr9_switch_sync(); + + /* Init MAC connected to CPU */ + ltq_eth_pmac_init(); + + /* Init MACs connected to external MII interfaces */ + for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) + ltq_eth_gmac_init(i); +} + +static void ltq_eth_port_config(struct ltq_eth_priv *priv, + const struct ltq_eth_port_config *port) +{ + struct ltq_mii_mii_cfg_reg mii_cfg_reg; + struct phy_device *phydev; + int setup_gpio = 0; + + switch (port->num) { + case 0: /* xMII0 */ + case 1: /* xMII1 */ + mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs, + port->num)); + mii_cfg_reg.bits.en = port->flags ? 1 : 0; + + switch (port->phy_if) { + case PHY_INTERFACE_MODE_MII: + if (port->flags & LTQ_ETH_PORT_PHY) + /* MII MAC mode, connected to external PHY */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_MIIM; + else + /* MII PHY mode, connected to external MAC */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_MIIP; + setup_gpio = 1; + break; + case PHY_INTERFACE_MODE_RMII: + if (port->flags & LTQ_ETH_PORT_PHY) + /* RMII MAC mode, connected to external PHY */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_RMIIM; + else + /* RMII PHY mode, connected to external MAC */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_RMIIP; + setup_gpio = 1; + break; + case PHY_INTERFACE_MODE_RGMII: + /* RGMII MAC mode, connected to external PHY */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_RGMII; + setup_gpio = 1; + + /* RGMII clock delays */ + ltq_writel(to_mii_pcdu(switch_regs, port->num), + port->rgmii_rx_delay << PCDU_RXDLY_SHIFT | + port->rgmii_tx_delay); + break; + default: + break; + } + + ltq_writel(to_mii_miicfg(switch_regs, port->num), + mii_cfg_reg.val); + break; + case 2: /* internal GPHY0 */ + case 3: /* internal GPHY0 */ + case 4: /* internal GPHY1 */ + switch (port->phy_if) { + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_GMII: + setup_gpio = 1; + break; + default: + break; + } + break; + case 5: /* internal GPHY1 or xMII2 */ + mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs, + port->num)); + mii_cfg_reg.bits.en = port->flags ? 1 : 0; + + switch (port->phy_if) { + case PHY_INTERFACE_MODE_MII: + /* MII MAC mode, connected to internal GPHY */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_MIIM; + setup_gpio = 1; + break; + case PHY_INTERFACE_MODE_RGMII: + /* RGMII MAC mode, connected to external PHY */ + mii_cfg_reg.bits.miimode = + LTQ_MII_MII_CFG_MIIMODE_RGMII; + setup_gpio = 1; + + /* RGMII clock delays */ + ltq_writel(to_mii_pcdu(switch_regs, port->num), + port->rgmii_rx_delay << PCDU_RXDLY_SHIFT | + port->rgmii_tx_delay); + break; + default: + break; + } + + ltq_writel(to_mii_miicfg(switch_regs, port->num), + mii_cfg_reg.val); + break; + default: + break; + } + + /* Setup GPIOs for MII with external PHYs/MACs */ + if (setup_gpio) { + /* MII/MDIO */ + gpio_set_altfunc(42, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, + GPIO_DIR_OUT); + /* MII/MDC */ + gpio_set_altfunc(43, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, + GPIO_DIR_OUT); + } + + /* Connect to internal/external PHYs */ + if (port->flags & LTQ_ETH_PORT_PHY) { + phydev = phy_connect(priv->bus, port->phy_addr, priv->dev, + port->phy_if); + if (phydev) + phy_config(phydev); + + priv->phymap[port->num] = phydev; + } +} + +int ltq_eth_initialize(const struct ltq_eth_board_config *board_config) +{ + struct eth_device *dev; + struct mii_dev *bus; + struct ltq_eth_priv *priv; + struct ltq_dma_device *dma_dev; + int i, ret; + + build_check_vr9_registers(); + + ltq_dma_init(); + ltq_eth_hw_init(); + + dev = calloc(1, sizeof(struct eth_device)); + if (!dev) + return -1; + + priv = calloc(1, sizeof(struct ltq_eth_priv)); + if (!priv) + return -1; + + bus = mdio_alloc(); + if (!bus) + return -1; + + sprintf(dev->name, LTQ_ETH_DRV_NAME); + dev->priv = priv; + dev->init = ltq_eth_init; + dev->halt = ltq_eth_halt; + dev->recv = ltq_eth_recv; + dev->send = ltq_eth_send; + + sprintf(bus->name, LTQ_MDIO_DRV_NAME); + bus->read = vr9_switch_mdio_read; + bus->write = vr9_switch_mdio_write; + bus->priv = priv; + + dma_dev = &priv->dma_dev; + dma_dev->port = 0; + dma_dev->rx_chan.chan_no = 0; + dma_dev->rx_chan.class = 0; + dma_dev->rx_chan.num_desc = LTQ_ETH_RX_BUFFER_CNT; + dma_dev->rx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0; + dma_dev->rx_burst_len = LTQ_DMA_BURST_2WORDS; + dma_dev->tx_chan.chan_no = 1; + dma_dev->tx_chan.class = 0; + dma_dev->tx_chan.num_desc = LTQ_ETH_TX_BUFFER_CNT; + dma_dev->tx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0; + dma_dev->tx_burst_len = LTQ_DMA_BURST_2WORDS; + + priv->bus = bus; + priv->dev = dev; + + ret = ltq_dma_register(dma_dev); + if (ret) + return -1; + + ret = mdio_register(bus); + if (ret) + return -1; + + ret = eth_register(dev); + if (ret) + return -1; + + for (i = 0; i < board_config->num_ports; i++) + ltq_eth_port_config(priv, &board_config->ports[i]); + + return 0; +} --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -20,6 +20,7 @@ COBJS-$(CONFIG_PHY_BROADCOM) += broadcom COBJS-$(CONFIG_PHY_DAVICOM) += davicom.o COBJS-$(CONFIG_PHY_ET1011C) += et1011c.o COBJS-$(CONFIG_PHY_ICPLUS) += icplus.o +COBJS-$(CONFIG_PHY_LANTIQ) += lantiq.o COBJS-$(CONFIG_PHY_LXT) += lxt.o COBJS-$(CONFIG_PHY_MARVELL) += marvell.o COBJS-$(CONFIG_PHY_MICREL) += micrel.o --- /dev/null +++ b/drivers/net/phy/lantiq.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define DEBUG + +#include +#include + +#define ADVERTIZE_MPD (1 << 10) + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Update link status. + * + * Based on genphy_update_link in phylib.c + */ +static int ltq_phy_update_link(struct phy_device *phydev) +{ + unsigned int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + /* + * If we already saw the link up, and it hasn't gone down, then + * we don't need to wait for autoneg again + */ + if (phydev->link && mii_reg & BMSR_LSTATUS) + return 0; + + if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) { + phydev->link = 0; + return 0; + } else { + /* Read the link a second time to clear the latched state */ + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + if (mii_reg & BMSR_LSTATUS) + phydev->link = 1; + else + phydev->link = 0; + } + + return 0; +} + +/* + * Update speed and duplex. + * + * Based on genphy_parse_link in phylib.c + */ +static int ltq_phy_parse_link(struct phy_device *phydev) +{ + unsigned int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + /* We're using autonegotiation */ + if (mii_reg & BMSR_ANEGCAPABLE) { + u32 lpa = 0; + u32 gblpa = 0; + + /* Check for gigabit capability */ + if (mii_reg & BMSR_ERCAP) { + /* We want a list of states supported by + * both PHYs in the link + */ + gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); + gblpa &= phy_read(phydev, + MDIO_DEVAD_NONE, MII_CTRL1000) << 2; + } + + /* Set the baseline so we only have to set them + * if they're different + */ + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + /* Check the gigabit fields */ + if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) { + phydev->speed = SPEED_1000; + + if (gblpa & PHY_1000BTSR_1000FD) + phydev->duplex = DUPLEX_FULL; + + /* We're done! */ + return 0; + } + + lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + + if (lpa & (LPA_100FULL | LPA_100HALF)) { + phydev->speed = SPEED_100; + + if (lpa & LPA_100FULL) + phydev->duplex = DUPLEX_FULL; + + } else if (lpa & LPA_10FULL) + phydev->duplex = DUPLEX_FULL; + } else { + u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + } + + return 0; +} + +static int ltq_phy_config(struct phy_device *phydev) +{ + u16 val; + + /* Advertise as Multi-port device */ + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + val |= ADVERTIZE_MPD; + phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, val); + + genphy_config_aneg(phydev); + + return 0; +} + +static int ltq_phy_startup(struct phy_device *phydev) +{ + /* + * Update PHY status immediately without any delays as genphy_startup + * does because VRX200 switch needs to be configured dependent + * on this information. + */ + ltq_phy_update_link(phydev); + ltq_phy_parse_link(phydev); + + debug("ltq_phy: addr %d, link %d, speed %d, duplex %d\n", + phydev->addr, phydev->link, phydev->speed, phydev->duplex); + + return 0; +} + +static struct phy_driver xrx_11g_13_driver = { + .name = "Lantiq XWAY XRX PHY11G v1.3 and earlier", + .uid = 0x030260D0, + .mask = 0xFFFFFFF0, + .features = PHY_GBIT_FEATURES, + .config = ltq_phy_config, + .startup = ltq_phy_startup, + .shutdown = genphy_shutdown, +}; + +static struct phy_driver xrx_11g_14_driver = { + .name = "Lantiq XWAY XRX PHY11G v1.4 and later", + .uid = 0xd565a408, + .mask = 0xFFFFFFF8, + .features = PHY_GBIT_FEATURES, + .config = ltq_phy_config, + .startup = ltq_phy_startup, + .shutdown = genphy_shutdown, +}; + +static struct phy_driver xrx_22f_14_driver = { + .name = "Lantiq XWAY XRX PHY22F v1.4 and later", + .uid = 0xd565a418, + .mask = 0xFFFFFFF8, + .features = PHY_BASIC_FEATURES, + .config = ltq_phy_config, + .startup = ltq_phy_startup, + .shutdown = genphy_shutdown, +}; + +static struct phy_driver pef7071_driver = { + .name = "Lantiq XWAY PEF7071", + .uid = 0xd565a400, + .mask = 0xFFFFFFF8, + .features = PHY_GBIT_FEATURES, + .config = ltq_phy_config, + .startup = ltq_phy_startup, + .shutdown = genphy_shutdown, +}; + +static struct phy_driver xrx_genphy_driver = { + .name = "Generic PHY at Lantiq XWAY XRX switch", + .uid = 0, + .mask = 0, + .features = 0, + .config = genphy_config, + .startup = ltq_phy_startup, + .shutdown = genphy_shutdown, +}; + +int phy_lantiq_init(void) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + xrx_11g_13_driver.config = ltq_phy_config; + xrx_11g_13_driver.startup = ltq_phy_startup; + xrx_11g_13_driver.shutdown = genphy_shutdown; + xrx_11g_13_driver.name += gd->reloc_off; + + xrx_11g_14_driver.config = ltq_phy_config; + xrx_11g_14_driver.startup = ltq_phy_startup; + xrx_11g_14_driver.shutdown = genphy_shutdown; + xrx_11g_14_driver.name += gd->reloc_off; + + xrx_22f_14_driver.config = ltq_phy_config; + xrx_22f_14_driver.startup = ltq_phy_startup; + xrx_22f_14_driver.shutdown = genphy_shutdown; + xrx_22f_14_driver.name += gd->reloc_off; + + pef7071_driver.config = ltq_phy_config; + pef7071_driver.startup = ltq_phy_startup; + pef7071_driver.shutdown = genphy_shutdown; + pef7071_driver.name += gd->reloc_off; + + xrx_genphy_driver.config = genphy_config; + xrx_genphy_driver.startup = ltq_phy_startup; + xrx_genphy_driver.shutdown = genphy_shutdown; + xrx_genphy_driver.name += gd->reloc_off; +#endif + + phy_register(&xrx_11g_13_driver); + phy_register(&xrx_11g_14_driver); + phy_register(&xrx_22f_14_driver); + phy_register(&pef7071_driver); + phy_register(&xrx_genphy_driver); + + return 0; +} --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -16,9 +16,10 @@ #include #include #include -#include #include +DECLARE_GLOBAL_DATA_PTR; + /* Generic PHY support and helper functions */ /** @@ -440,6 +441,16 @@ static LIST_HEAD(phy_drivers); int phy_init(void) { +#ifdef CONFIG_NEEDS_MANUAL_RELOC + INIT_LIST_HEAD(&phy_drivers); + + genphy_driver.config = genphy_config; + genphy_driver.startup = genphy_startup; + genphy_driver.shutdown = genphy_shutdown; + + genphy_driver.name += gd->reloc_off; +#endif + #ifdef CONFIG_PHY_ATHEROS phy_atheros_init(); #endif @@ -455,6 +466,9 @@ int phy_init(void) #ifdef CONFIG_PHY_ICPLUS phy_icplus_init(); #endif +#ifdef CONFIG_PHY_LANTIQ + phy_lantiq_init(); +#endif #ifdef CONFIG_PHY_LXT phy_lxt_init(); #endif --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -24,6 +24,7 @@ COBJS-$(CONFIG_SYS_NS16550_SERIAL) += se COBJS-$(CONFIG_IMX_SERIAL) += serial_imx.o COBJS-$(CONFIG_IXP_SERIAL) += serial_ixp.o COBJS-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o +COBJS-$(CONFIG_LANTIQ_SERIAL) += serial_lantiq.o COBJS-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o COBJS-$(CONFIG_MXC_UART) += serial_mxc.o COBJS-$(CONFIG_PL010_SERIAL) += serial_pl01x.o --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -160,6 +160,7 @@ serial_initfunc(sa1100_serial_initialize serial_initfunc(sh_serial_initialize); serial_initfunc(arm_dcc_initialize); serial_initfunc(mxs_auart_initialize); +serial_initfunc(ltq_serial_initialize); /** * serial_register() - Register serial driver with serial driver core @@ -253,6 +254,7 @@ void serial_initialize(void) sh_serial_initialize(); arm_dcc_initialize(); mxs_auart_initialize(); + ltq_serial_initialize(); serial_assign(default_serial_console()->name); } --- /dev/null +++ b/drivers/serial/serial_lantiq.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2010 Thomas Langer + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#if CONFIG_CONSOLE_ASC == 0 +#define LTQ_ASC_BASE LTQ_ASC0_BASE +#else +#define LTQ_ASC_BASE LTQ_ASC1_BASE +#endif + +#define LTQ_ASC_ID_TXFS_SHIFT 24 +#define LTQ_ASC_ID_TXFS_MASK (0x3F << LTQ_ASC_ID_TXFS_SHIFT) +#define LTQ_ASC_ID_RXFS_SHIFT 16 +#define LTQ_ASC_ID_RXFS_MASK (0x3F << LTQ_ASC_ID_RXFS_SHIFT) + +#define LTQ_ASC_MCON_R (1 << 15) +#define LTQ_ASC_MCON_FDE (1 << 9) + +#define LTQ_ASC_WHBSTATE_SETREN (1 << 1) +#define LTQ_ASC_WHBSTATE_CLRREN (1 << 0) + +#define LTQ_ASC_RXFCON_RXFITL_SHIFT 8 +#define LTQ_ASC_RXFCON_RXFITL_MASK (0x3F << LTQ_ASC_RXFCON_RXFITL_SHIFT) +#define LTQ_ASC_RXFCON_RXFITL_RXFFLU (1 << 1) +#define LTQ_ASC_RXFCON_RXFITL_RXFEN (1 << 0) + +#define LTQ_ASC_TXFCON_TXFITL_SHIFT 8 +#define LTQ_ASC_TXFCON_TXFITL_MASK (0x3F << LTQ_ASC_TXFCON_TXFITL_SHIFT) +#define LTQ_ASC_TXFCON_TXFITL_TXFFLU (1 << 1) +#define LTQ_ASC_TXFCON_TXFITL_TXFEN (1 << 0) + +#define LTQ_ASC_FSTAT_TXFREE_SHIFT 24 +#define LTQ_ASC_FSTAT_TXFREE_MASK (0x3F << LTQ_ASC_FSTAT_TXFREE_SHIFT) +#define LTQ_ASC_FSTAT_RXFREE_SHIFT 16 +#define LTQ_ASC_FSTAT_RXFREE_MASK (0x3F << LTQ_ASC_FSTAT_RXFREE_SHIFT) +#define LTQ_ASC_FSTAT_TXFFL_SHIFT 8 +#define LTQ_ASC_FSTAT_TXFFL_MASK (0x3F << LTQ_ASC_FSTAT_TXFFL_SHIFT) +#define LTQ_ASC_FSTAT_RXFFL_MASK 0x3F + +#ifdef __BIG_ENDIAN +#define LTQ_ASC_RBUF_OFFSET 3 +#define LTQ_ASC_TBUF_OFFSET 3 +#else +#define LTQ_ASC_RBUF_OFFSET 0 +#define LTQ_ASC_TBUF_OFFSET 0 +#endif + +struct ltq_asc_regs { + u32 clc; + u32 pisel; + u32 id; + u32 rsvd0; + u32 mcon; + u32 state; + u32 whbstate; + u32 rsvd1; + u8 tbuf[4]; + u8 rbuf[4]; + u32 rsvd2[2]; + u32 abcon; + u32 abstat; + u32 whbabcon; + u32 whbabstat; + u32 rxfcon; + u32 txfcon; + u32 fstat; + u32 rsvd3; + u32 bg; + u32 bg_timer; + u32 fdv; + u32 pmw; + u32 modcon; + u32 modstat; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static struct ltq_asc_regs *ltq_asc_regs = + (struct ltq_asc_regs *) CKSEG1ADDR(LTQ_ASC_BASE); + +static int ltq_serial_init(void) +{ + /* Set clock divider for normal run mode to 1 and enable module */ + ltq_writel(<q_asc_regs->clc, 0x100); + + /* Reset MCON register */ + ltq_writel(<q_asc_regs->mcon, 0); + + /* Use Port A as receiver input */ + ltq_writel(<q_asc_regs->pisel, 0); + + /* Enable and flush RX/TX FIFOs */ + ltq_setbits(<q_asc_regs->rxfcon, + LTQ_ASC_RXFCON_RXFITL_RXFFLU | LTQ_ASC_RXFCON_RXFITL_RXFEN); + ltq_setbits(<q_asc_regs->txfcon, + LTQ_ASC_TXFCON_TXFITL_TXFFLU | LTQ_ASC_TXFCON_TXFITL_TXFEN); + + serial_setbrg(); + + /* Disable error flags, enable receiver */ + ltq_writel(<q_asc_regs->whbstate, LTQ_ASC_WHBSTATE_SETREN); + + return 0; +} + +/* + * fdv asc_clk + * Baudrate = ----- * ------------- + * 512 16 * (bg + 1) + */ +static void ltq_serial_calc_br_fdv(unsigned long asc_clk, + unsigned long baudrate, u16 *fdv, + u16 *bg) +{ + const u32 c = asc_clk / (16 * 512); + u32 diff1, diff2; + u32 bg_calc, br_calc, i; + + diff1 = baudrate; + for (i = 512; i > 0; i--) { + /* Calc bg for current fdv value */ + bg_calc = i * c / baudrate; + + /* Impossible baudrate */ + if (!bg_calc) + return; + + /* + * Calc diff to target baudrate dependent on current + * bg and fdv values + */ + br_calc = i * c / bg_calc; + if (br_calc > baudrate) + diff2 = br_calc - baudrate; + else + diff2 = baudrate - br_calc; + + /* Perfect values found */ + if (diff2 == 0) { + *fdv = i; + *bg = bg_calc - 1; + return; + } + + if (diff2 < diff1) { + *fdv = i; + *bg = bg_calc - 1; + diff1 = diff2; + } + } +} + +static void ltq_serial_setbrg(void) +{ + unsigned long asc_clk, baudrate; + u16 bg = 0; + u16 fdv = 511; + + /* ASC clock is same as FPI clock with CLC.RMS = 1 */ + asc_clk = ltq_get_bus_clock(); + baudrate = gd->baudrate; + + /* Calculate FDV and BG values */ + ltq_serial_calc_br_fdv(asc_clk, baudrate, &fdv, &bg); + + /* Disable baudrate generator */ + ltq_clrbits(<q_asc_regs->mcon, LTQ_ASC_MCON_R); + + /* Enable fractional divider */ + ltq_setbits(<q_asc_regs->mcon, LTQ_ASC_MCON_FDE); + + /* Set fdv and bg values */ + ltq_writel(<q_asc_regs->fdv, fdv); + ltq_writel(<q_asc_regs->bg, bg); + + /* Enable baudrate generator */ + ltq_setbits(<q_asc_regs->mcon, LTQ_ASC_MCON_R); +} + +static unsigned int ltq_serial_tx_free(void) +{ + unsigned int txfree; + + txfree = (ltq_readl(<q_asc_regs->fstat) & + LTQ_ASC_FSTAT_TXFREE_MASK) >> + LTQ_ASC_FSTAT_TXFREE_SHIFT; + + return txfree; +} + +static unsigned int ltq_serial_rx_fill(void) +{ + unsigned int rxffl; + + rxffl = ltq_readl(<q_asc_regs->fstat) & LTQ_ASC_FSTAT_RXFFL_MASK; + + return rxffl; +} + +static void ltq_serial_tx(const char c) +{ + ltq_writeb(<q_asc_regs->tbuf[LTQ_ASC_TBUF_OFFSET], c); +} + +static u8 ltq_serial_rx(void) +{ + return ltq_readb(<q_asc_regs->rbuf[LTQ_ASC_RBUF_OFFSET]); +} + +static void ltq_serial_putc(const char c) +{ + if (c == '\n') + ltq_serial_putc('\r'); + + while (!ltq_serial_tx_free()) + ; + + ltq_serial_tx(c); +} + +static int ltq_serial_getc(void) +{ + while (!ltq_serial_rx_fill()) + ; + + return ltq_serial_rx(); +} + +static int ltq_serial_tstc(void) +{ + return (0 != ltq_serial_rx_fill()); +} + +static struct serial_device ltq_serial_drv = { + .name = "ltq_serial", + .start = ltq_serial_init, + .stop = NULL, + .setbrg = ltq_serial_setbrg, + .putc = ltq_serial_putc, + .puts = default_serial_puts, + .getc = ltq_serial_getc, + .tstc = ltq_serial_tstc, +}; + +void ltq_serial_initialize(void) +{ + serial_register(<q_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return <q_serial_drv; +} --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -25,6 +25,7 @@ COBJS-$(CONFIG_DAVINCI_SPI) += davinci_s COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o COBJS-$(CONFIG_ICH_SPI) += ich.o COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o +COBJS-$(CONFIG_LANTIQ_SPI) += lantiq_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o --- /dev/null +++ b/drivers/spi/lantiq_spi.c @@ -0,0 +1,666 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTQ_SPI_CLC_RMC_SHIFT 8 +#define LTQ_SPI_CLC_RMC_MASK (0xFF << LTQ_SPI_CLC_RMC_SHIFT) +#define LTQ_SPI_CLC_DISS (1 << 1) +#define LTQ_SPI_CLC_DISR 1 + +#define LTQ_SPI_ID_TXFS_SHIFT 24 +#define LTQ_SPI_ID_TXFS_MASK (0x3F << LTQ_SPI_ID_TXFS_SHIFT) +#define LTQ_SPI_ID_RXFS_SHIFT 16 +#define LTQ_SPI_ID_RXFS_MASK (0x3F << LTQ_SPI_ID_RXFS_SHIFT) + +#define LTQ_SPI_CON_ENBV (1 << 22) +#define LTQ_SPI_CON_BM_SHIFT 16 +#define LTQ_SPI_CON_BM_MASK (0x1F << LTQ_SPI_CON_BM_SHIFT) +#define LTQ_SPI_CON_IDLE (1 << 23) +#define LTQ_SPI_CON_RUEN (1 << 12) +#define LTQ_SPI_CON_AEN (1 << 10) +#define LTQ_SPI_CON_REN (1 << 9) +#define LTQ_SPI_CON_TEN (1 << 8) +#define LTQ_SPI_CON_LB (1 << 7) +#define LTQ_SPI_CON_PO (1 << 6) +#define LTQ_SPI_CON_PH (1 << 5) +#define LTQ_SPI_CON_HB (1 << 4) +#define LTQ_SPI_CON_RXOFF (1 << 1) +#define LTQ_SPI_CON_TXOFF 1 + +#define LTQ_SPI_STAT_RXBV_SHIFT 28 +#define LTQ_SPI_STAT_RXBV_MASK (0x7 << LTQ_SPI_STAT_RXBV_SHIFT) +#define LTQ_SPI_STAT_BSY (1 << 13) + +#define LTQ_SPI_WHBSTATE_SETMS (1 << 3) +#define LTQ_SPI_WHBSTATE_CLRMS (1 << 2) +#define LTQ_SPI_WHBSTATE_SETEN (1 << 1) +#define LTQ_SPI_WHBSTATE_CLREN 1 +#define LTQ_SPI_WHBSTATE_CLR_ERRORS 0x0F50 + +#define LTQ_SPI_TXFCON_TXFLU (1 << 1) +#define LTQ_SPI_TXFCON_TXFEN 1 + +#define LTQ_SPI_RXFCON_RXFLU (1 << 1) +#define LTQ_SPI_RXFCON_RXFEN 1 + +#define LTQ_SPI_FSTAT_RXFFL_MASK 0x3f +#define LTQ_SPI_FSTAT_TXFFL_SHIFT 8 +#define LTQ_SPI_FSTAT_TXFFL_MASK (0x3f << LTQ_SPI_FSTAT_TXFFL_SHIFT) + +#define LTQ_SPI_RXREQ_RXCNT_MASK 0xFFFF +#define LTQ_SPI_RXCNT_TODO_MASK 0xFFFF + +#define LTQ_SPI_GPIO_DIN 16 +#define LTQ_SPI_GPIO_DOUT 17 +#define LTQ_SPI_GPIO_CLK 18 + +struct ltq_spi_regs { + __be32 clc; /* Clock control */ + __be32 pisel; /* Port input select */ + __be32 id; /* Identification */ + __be32 rsvd0; + __be32 con; /* Control */ + __be32 stat; /* Status */ + __be32 whbstate; /* Write HW modified state */ + __be32 rsvd1; + __be32 tb; /* Transmit buffer */ + __be32 rb; /* Receive buffer */ + __be32 rsvd2[2]; + __be32 rxfcon; /* Recevie FIFO control */ + __be32 txfcon; /* Transmit FIFO control */ + __be32 fstat; /* FIFO status */ + __be32 rsvd3; + __be32 brt; /* Baudrate timer */ + __be32 brstat; /* Baudrate timer status */ + __be32 rsvd4[6]; + __be32 sfcon; /* Serial frame control */ + __be32 sfstat; /* Serial frame status */ + __be32 rsvd5[2]; + __be32 gpocon; /* General purpose output control */ + __be32 gpostat; /* General purpose output status */ + __be32 fgpo; /* Force general purpose output */ + __be32 rsvd6; + __be32 rxreq; /* Receive request */ + __be32 rxcnt; /* Receive count */ + __be32 rsvd7[25]; + __be32 dmacon; /* DMA control */ + __be32 rsvd8; + __be32 irnen; /* Interrupt node enable */ + __be32 irnicr; /* Interrupt node interrupt capture */ + __be32 irncr; /* Interrupt node control */ +}; + +struct ltq_spi_drv_data { + struct ltq_spi_regs __iomem *regs; + + struct spi_slave slave; + unsigned int max_hz; + unsigned int mode; + unsigned int tx_todo; + unsigned int rx_todo; + unsigned int rx_req; + unsigned int bits_per_word; + unsigned int speed_hz; + const u8 *tx; + u8 *rx; + int status; +}; + +static struct ltq_spi_drv_data *to_ltq_spi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct ltq_spi_drv_data, slave); +} + +#ifdef CONFIG_SPL_BUILD +/* + * We do not have or want malloc in a SPI flash SPL. + * Neither we have to support multiple SPI slaves. Thus we put the + * SPI slave context in BSS for SPL builds. + */ +static struct ltq_spi_drv_data ltq_spi_slave; + +static struct ltq_spi_drv_data *ltq_spi_slave_alloc(unsigned int bus, + unsigned int cs) +{ + ltq_spi_slave.slave.bus = bus; + ltq_spi_slave.slave.cs = cs; + + return <q_spi_slave; +} + +static void ltq_spi_slave_free(struct spi_slave *slave) +{ +} +#else +static struct ltq_spi_drv_data *ltq_spi_slave_alloc(unsigned int bus, + unsigned int cs) +{ + return spi_alloc_slave(struct ltq_spi_drv_data, bus, cs); +} + +static void ltq_spi_slave_free(struct spi_slave *slave) +{ + struct ltq_spi_drv_data *drv; + + if (slave) { + drv = to_ltq_spi_slave(slave); + free(drv); + } +} +#endif + +static unsigned int tx_fifo_size(struct ltq_spi_drv_data *drv) +{ + u32 id = ltq_readl(&drv->regs->id); + + return (id & LTQ_SPI_ID_TXFS_MASK) >> LTQ_SPI_ID_TXFS_SHIFT; +} + +static unsigned int rx_fifo_size(struct ltq_spi_drv_data *drv) +{ + u32 id = ltq_readl(&drv->regs->id); + + return (id & LTQ_SPI_ID_RXFS_MASK) >> LTQ_SPI_ID_RXFS_SHIFT; +} + +static unsigned int tx_fifo_level(struct ltq_spi_drv_data *drv) +{ + u32 fstat = ltq_readl(&drv->regs->fstat); + + return (fstat & LTQ_SPI_FSTAT_TXFFL_MASK) >> LTQ_SPI_FSTAT_TXFFL_SHIFT; +} + +static unsigned int rx_fifo_level(struct ltq_spi_drv_data *drv) +{ + u32 fstat = ltq_readl(&drv->regs->fstat); + + return fstat & LTQ_SPI_FSTAT_RXFFL_MASK; +} + +static unsigned int tx_fifo_free(struct ltq_spi_drv_data *drv) +{ + return tx_fifo_size(drv) - tx_fifo_level(drv); +} + +static void hw_power_on(struct ltq_spi_drv_data *drv) +{ + u32 clc; + + /* Power-up mdule */ + ltq_pm_enable(LTQ_PM_SPI); + + /* + * Set clock divider for run mode to 1 to + * run at same frequency as FPI bus + */ + clc = (1 << LTQ_SPI_CLC_RMC_SHIFT); + ltq_writel(&drv->regs->clc, clc); +} + +static void hw_reset_fifos(struct ltq_spi_drv_data *drv) +{ + u32 val; + + val = LTQ_SPI_TXFCON_TXFEN | LTQ_SPI_TXFCON_TXFLU; + ltq_writel(&drv->regs->txfcon, val); + + val = LTQ_SPI_RXFCON_RXFEN | LTQ_SPI_RXFCON_RXFLU; + ltq_writel(&drv->regs->rxfcon, val); +} + +static int hw_is_busy(struct ltq_spi_drv_data *drv) +{ + u32 stat = ltq_readl(&drv->regs->stat); + + return stat & LTQ_SPI_STAT_BSY; +} + +static void hw_enter_config_mode(struct ltq_spi_drv_data *drv) +{ + ltq_writel(&drv->regs->whbstate, LTQ_SPI_WHBSTATE_CLREN); +} + +static void hw_enter_active_mode(struct ltq_spi_drv_data *drv) +{ + ltq_writel(&drv->regs->whbstate, LTQ_SPI_WHBSTATE_SETEN); +} + +static void hw_setup_speed_hz(struct ltq_spi_drv_data *drv, + unsigned int max_speed_hz) +{ + unsigned int spi_hz, speed_hz, brt; + + /* + * SPI module clock is derived from FPI bus clock dependent on + * divider value in CLC.RMS which is always set to 1. + * + * f_SPI + * baudrate = -------------- + * 2 * (BR + 1) + */ + spi_hz = ltq_get_bus_clock() / 2; + + /* TODO: optimize baudrate calculation */ + for (brt = 0; brt < 0xFFFF; brt++) { + speed_hz = spi_hz / (brt + 1); + if (speed_hz <= max_speed_hz) + break; + } + + ltq_writel(&drv->regs->brt, brt); +} + +static void hw_setup_bits_per_word(struct ltq_spi_drv_data *drv, + unsigned int bits_per_word) +{ + u32 bm; + + /* CON.BM value = bits_per_word - 1 */ + bm = (bits_per_word - 1) << LTQ_SPI_CON_BM_SHIFT; + + ltq_clrsetbits(&drv->regs->con, LTQ_SPI_CON_BM_MASK, bm); +} + +static void hw_setup_clock_mode(struct ltq_spi_drv_data *drv, unsigned int mode) +{ + u32 con_set = 0, con_clr = 0; + + /* + * SPI mode mapping in CON register: + * Mode CPOL CPHA CON.PO CON.PH + * 0 0 0 0 1 + * 1 0 1 0 0 + * 2 1 0 1 1 + * 3 1 1 1 0 + */ + if (mode & SPI_CPHA) + con_clr |= LTQ_SPI_CON_PH; + else + con_set |= LTQ_SPI_CON_PH; + + if (mode & SPI_CPOL) + con_set |= LTQ_SPI_CON_PO | LTQ_SPI_CON_IDLE; + else + con_clr |= LTQ_SPI_CON_PO | LTQ_SPI_CON_IDLE; + + /* Set heading control */ + if (mode & SPI_LSB_FIRST) + con_clr |= LTQ_SPI_CON_HB; + else + con_set |= LTQ_SPI_CON_HB; + + /* Set loopback mode */ + if (mode & SPI_LOOP) + con_set |= LTQ_SPI_CON_LB; + else + con_clr |= LTQ_SPI_CON_LB; + + ltq_clrsetbits(&drv->regs->con, con_clr, con_set); +} + +static void hw_set_rxtx(struct ltq_spi_drv_data *drv) +{ + u32 con; + + /* Configure transmitter and receiver */ + con = ltq_readl(&drv->regs->con); + if (drv->tx) + con &= ~LTQ_SPI_CON_TXOFF; + else + con |= LTQ_SPI_CON_TXOFF; + + if (drv->rx) + con &= ~LTQ_SPI_CON_RXOFF; + else + con |= LTQ_SPI_CON_RXOFF; + + ltq_writel(&drv->regs->con, con); +} + +static void hw_init(struct ltq_spi_drv_data *drv) +{ + hw_power_on(drv); + + /* Put controller into config mode */ + hw_enter_config_mode(drv); + + /* Disable all interrupts */ + ltq_writel(&drv->regs->irnen, 0); + + /* Clear error flags */ + ltq_clrsetbits(&drv->regs->whbstate, 0, LTQ_SPI_WHBSTATE_CLR_ERRORS); + + /* Enable error checking, disable TX/RX */ + ltq_writel(&drv->regs->con, LTQ_SPI_CON_RUEN | LTQ_SPI_CON_AEN | + LTQ_SPI_CON_TEN | LTQ_SPI_CON_REN | LTQ_SPI_CON_TXOFF | + LTQ_SPI_CON_RXOFF); + + /* Setup default SPI mode */ + drv->bits_per_word = 8; + drv->speed_hz = 0; + hw_setup_bits_per_word(drv, drv->bits_per_word); + hw_setup_clock_mode(drv, SPI_MODE_0); + + /* Enable master mode and clear error flags */ + ltq_writel(&drv->regs->whbstate, LTQ_SPI_WHBSTATE_SETMS | + LTQ_SPI_WHBSTATE_CLR_ERRORS); + + /* Reset GPIO/CS registers */ + ltq_writel(&drv->regs->gpocon, 0); + ltq_writel(&drv->regs->fgpo, 0xFF00); + + /* Enable and flush FIFOs */ + hw_reset_fifos(drv); + + /* SPI/DIN input */ + gpio_set_altfunc(16, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_IN); + /* SPI/DOUT output */ + gpio_set_altfunc(17, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_OUT); + /* SPI/CLK output */ + gpio_set_altfunc(18, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_OUT); +} + +static void tx_fifo_write(struct ltq_spi_drv_data *drv) +{ + const u8 *tx8; + const u16 *tx16; + const u32 *tx32; + u32 data; + unsigned int tx_free = tx_fifo_free(drv); + + while (drv->tx_todo && tx_free) { + switch (drv->bits_per_word) { + case 8: + tx8 = drv->tx; + data = *tx8; + drv->tx_todo--; + drv->tx++; + break; + case 16: + tx16 = (u16 *) drv->tx; + data = *tx16; + drv->tx_todo -= 2; + drv->tx += 2; + break; + case 32: + tx32 = (u32 *) drv->tx; + data = *tx32; + drv->tx_todo -= 4; + drv->tx += 4; + break; + default: + return; + } + + ltq_writel(&drv->regs->tb, data); + tx_free--; + } +} + +static void rx_fifo_read_full_duplex(struct ltq_spi_drv_data *drv) +{ + u8 *rx8; + u16 *rx16; + u32 *rx32; + u32 data; + unsigned int rx_fill = rx_fifo_level(drv); + + while (rx_fill) { + data = ltq_readl(&drv->regs->rb); + + switch (drv->bits_per_word) { + case 8: + rx8 = drv->rx; + *rx8 = data; + drv->rx_todo--; + drv->rx++; + break; + case 16: + rx16 = (u16 *) drv->rx; + *rx16 = data; + drv->rx_todo -= 2; + drv->rx += 2; + break; + case 32: + rx32 = (u32 *) drv->rx; + *rx32 = data; + drv->rx_todo -= 4; + drv->rx += 4; + break; + default: + return; + } + + rx_fill--; + } +} + +static void rx_fifo_read_half_duplex(struct ltq_spi_drv_data *drv) +{ + u32 data, *rx32; + u8 *rx8; + unsigned int rxbv, shift; + unsigned int rx_fill = rx_fifo_level(drv); + + /* + * In RX-only mode the bits per word value is ignored by HW. A value + * of 32 is used instead. Thus all 4 bytes per FIFO must be read. + * If remaining RX bytes are less than 4, the FIFO must be read + * differently. The amount of received and valid bytes is indicated + * by STAT.RXBV register value. + */ + while (rx_fill) { + if (drv->rx_todo < 4) { + rxbv = (ltq_readl(&drv->regs->stat) & + LTQ_SPI_STAT_RXBV_MASK) >> + LTQ_SPI_STAT_RXBV_SHIFT; + data = ltq_readl(&drv->regs->rb); + + shift = (rxbv - 1) * 8; + rx8 = drv->rx; + + while (rxbv) { + *rx8++ = (data >> shift) & 0xFF; + rxbv--; + shift -= 8; + drv->rx_todo--; + drv->rx++; + + if (drv->rx_req) + drv->rx_req --; + } + } else { + data = ltq_readl(&drv->regs->rb); + rx32 = (u32 *) drv->rx; + + *rx32++ = data; + drv->rx_todo -= 4; + drv->rx += 4; + + if (drv->rx_req >= 4) + drv->rx_req -= 4; + } + rx_fill--; + } +} + +static void rx_request(struct ltq_spi_drv_data *drv) +{ + unsigned int rxreq, rxreq_max; + + if (drv->rx_req) + return; + + /* + * To avoid receive overflows at high clocks it is better to request + * only the amount of bytes that fits into all FIFOs. This value + * depends on the FIFO size implemented in hardware. + */ + rxreq = drv->rx_todo; + rxreq_max = rx_fifo_size(drv) * 4; + if (rxreq > rxreq_max) + rxreq = rxreq_max; + + drv->rx_req = rxreq; + ltq_writel(&drv->regs->rxreq, rxreq); +} + +void spi_init(void) +{ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct ltq_spi_drv_data *drv; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + drv = ltq_spi_slave_alloc(bus, cs); + if (!drv) + return NULL; + + drv->regs = (struct ltq_spi_regs *) CKSEG1ADDR(LTQ_SPI_BASE); + + hw_init(drv); + + drv->max_hz = max_hz; + drv->mode = mode; + + return &drv->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + ltq_spi_slave_free(slave); +} + +static int ltq_spi_wait_ready(struct ltq_spi_drv_data *drv) +{ + const unsigned long timeout = 20000; + unsigned long timebase; + + timebase = get_timer(0); + + do { + WATCHDOG_RESET(); + + if (!hw_is_busy(drv)) + return 0; + } while (get_timer(timebase) < timeout); + + return 1; +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct ltq_spi_drv_data *drv = to_ltq_spi_slave(slave); + int ret; + + ret = ltq_spi_wait_ready(drv); + if (ret) { + debug("cannot claim bus\n"); + return ret; + } + + hw_enter_config_mode(drv); + hw_setup_clock_mode(drv, drv->mode); + hw_setup_speed_hz(drv, drv->max_hz); + hw_setup_bits_per_word(drv, drv->bits_per_word); + hw_enter_active_mode(drv); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct ltq_spi_drv_data *drv = to_ltq_spi_slave(slave); + + hw_enter_config_mode(drv); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + + struct ltq_spi_drv_data *drv = to_ltq_spi_slave(slave); + int ret = 0; + + if (bitlen % 8) + return 1; + + if (!bitlen) { + ret = 0; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + drv->tx = dout; + drv->tx_todo = 0; + drv->rx = din; + drv->rx_todo = 0; + hw_set_rxtx(drv); + + if (drv->tx) { + drv->tx_todo = bitlen / 8; + + tx_fifo_write(drv); + } + + if (drv->rx) { + drv->rx_todo = bitlen / 8; + + if (!drv->tx) + rx_request(drv); + } + + for (;;) { + if (drv->tx) { + if (drv->rx && drv->rx_todo) + rx_fifo_read_full_duplex(drv); + + if (drv->tx_todo) + tx_fifo_write(drv); + else + goto done; + } else if (drv->rx) { + if (drv->rx_todo) { + rx_fifo_read_half_duplex(drv); + + if (drv->rx_todo) + rx_request(drv); + else + goto done; + } else { + goto done; + } + } + } + +done: + ret = ltq_spi_wait_ready(drv); + + drv->rx = NULL; + drv->tx = NULL; + hw_set_rxtx(drv); + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return ret; +} --- a/include/phy.h +++ b/include/phy.h @@ -214,6 +214,7 @@ int phy_atheros_init(void); int phy_broadcom_init(void); int phy_davicom_init(void); int phy_et1011c_init(void); +int phy_lantiq_init(void); int phy_lxt_init(void); int phy_marvell_init(void); int phy_micrel_init(void); --- a/spl/Makefile +++ b/spl/Makefile @@ -100,6 +100,8 @@ LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += dri LIBS-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/libusb_musb-new.o LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/libusb_gadget.o LIBS-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/libwatchdog.o +LIBS-$(CONFIG_SPL_LZMA_SUPPORT) += lib/lzma/liblzma.o +LIBS-$(CONFIG_SPL_LZO_SUPPORT) += lib/lzo/liblzo.o ifneq ($(CONFIG_OMAP_COMMON),) LIBS-y += $(CPUDIR)/omap-common/libomap-common.o --- a/tools/.gitignore +++ b/tools/.gitignore @@ -2,6 +2,7 @@ /envcrc /gen_eth_addr /img2srec +/ltq-boot-image /kwboot /mkenvimage /mkimage --- a/tools/Makefile +++ b/tools/Makefile @@ -49,6 +49,7 @@ BIN_FILES-$(CONFIG_VIDEO_LOGO) += bmp_lo BIN_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc$(SFX) BIN_FILES-$(CONFIG_CMD_NET) += gen_eth_addr$(SFX) BIN_FILES-$(CONFIG_CMD_LOADS) += img2srec$(SFX) +BIN_FILES-$(CONFIG_SOC_LANTIQ) += ltq-boot-image$(SFX) BIN_FILES-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes$(SFX) BIN_FILES-y += mkenvimage$(SFX) BIN_FILES-y += mkimage$(SFX) @@ -95,6 +96,7 @@ OBJ_FILES-$(CONFIG_MX28) += mxsboot.o OBJ_FILES-$(CONFIG_NETCONSOLE) += ncb.o OBJ_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1.o OBJ_FILES-$(CONFIG_SMDK5250) += mkexynosspl.o +OBJ_FILES-$(CONFIG_SOC_LANTIQ) += ltq-boot-image.o OBJ_FILES-$(CONFIG_VIDEO_LOGO) += bmp_logo.o OBJ_FILES-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes.o @@ -195,6 +197,10 @@ $(obj)img2srec$(SFX): $(obj)img2srec.o $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTSTRIP) $@ +$(obj)ltq-boot-image$(SFX): $(obj)ltq-boot-image.o + $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ + $(HOSTSTRIP) $@ + $(obj)xway-swap-bytes$(SFX): $(obj)xway-swap-bytes.o $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTSTRIP) $@ --- /dev/null +++ b/tools/ltq-boot-image.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +enum image_types { + IMAGE_NONE, + IMAGE_SFSPL +}; + +/* Lantiq non-volatile bootstrap command IDs */ +enum nvb_cmd_ids { + NVB_CMD_DEBUG = 0x11, + NVB_CMD_REGCFG = 0x22, + NVB_CMD_IDWNLD = 0x33, + NVB_CMD_CDWNLD = 0x44, + NVB_CMD_DWNLD = 0x55, + NVB_CMD_IFCFG = 0x66, + NVB_CMD_START = 0x77 +}; + +/* Lantiq non-volatile bootstrap command flags */ +enum nvb_cmd_flags { + NVB_FLAG_START = 1, + NVB_FLAG_DEC = (1 << 1), + NVB_FLAG_DBG = (1 << 2), + NVB_FLAG_SDBG = (1 << 3), + NVB_FLAG_CFG0 = (1 << 4), + NVB_FLAG_CFG1 = (1 << 5), + NVB_FLAG_CFG2 = (1 << 6), + NVB_FLAG_RST = (1 << 7) +}; + +struct args { + enum image_types type; + __u32 entry_addr; + const char *uboot_bin; + const char *spl_bin; + const char *out_bin; +}; + +static void usage_msg(const char *name) +{ + fprintf(stderr, "%s: [-h] -t type -e entry-addr -u uboot-bin [-s spl-bin] -o out-bin\n", + name); + fprintf(stderr, " Image types:\n" + " sfspl - SPL + [compressed] U-Boot for SPI flash\n"); +} + +static enum image_types parse_image_type(const char *type) +{ + if (!type) + return IMAGE_NONE; + + if (!strncmp(type, "sfspl", 6)) + return IMAGE_SFSPL; + + return IMAGE_NONE; +} + +static int parse_args(int argc, char *argv[], struct args *arg) +{ + int opt; + + memset(arg, 0, sizeof(*arg)); + + while ((opt = getopt(argc, argv, "ht:e:u:s:o:")) != -1) { + switch (opt) { + case 'h': + usage_msg(argv[0]); + return 1; + case 't': + arg->type = parse_image_type(optarg); + break; + case 'e': + arg->entry_addr = strtoul(optarg, NULL, 16); + break; + case 'u': + arg->uboot_bin = optarg; + break; + case 's': + arg->spl_bin = optarg; + break; + case 'o': + arg->out_bin = optarg; + break; + default: + fprintf(stderr, "Invalid option -%c\n", opt); + goto parse_error; + } + } + + if (arg->type == IMAGE_NONE) { + fprintf(stderr, "Invalid image type\n"); + goto parse_error; + } + + if (!arg->uboot_bin) { + fprintf(stderr, "Missing U-Boot binary\n"); + goto parse_error; + } + + if (!arg->out_bin) { + fprintf(stderr, "Missing output binary\n"); + goto parse_error; + } + + if (arg->type == IMAGE_SFSPL && !arg->spl_bin) { + fprintf(stderr, "Missing SPL binary\n"); + goto parse_error; + } + + return 0; + +parse_error: + usage_msg(argv[0]); + return -1; +} + +static __u32 build_nvb_command(unsigned cmdid, unsigned cmdflags) +{ + __u32 cmd; + __u16 tag; + + tag = (cmdid << 8) | cmdflags; + cmd = (tag << 16) | (0xFFFF - tag); + + return cpu_to_be32(cmd); +} + +static int write_header(int fd, const void *hdr, size_t size) +{ + ssize_t n; + + n = write(fd, hdr, size); + if (n != size) { + fprintf(stderr, "Cannot write header: %s\n", + strerror(errno)); + return -1; + } + + return 0; +} + +static int write_nvb_dwnld_header(int fd, size_t size, __u32 addr) +{ + __u32 hdr[3]; + + hdr[0] = build_nvb_command(NVB_CMD_DWNLD, NVB_FLAG_START | + NVB_FLAG_SDBG); + hdr[1] = cpu_to_be32(size + 4); + hdr[2] = cpu_to_be32(addr); + + return write_header(fd, hdr, sizeof(hdr)); +} + +static int write_nvb_start_header(int fd, __u32 addr) +{ + __u32 hdr[3]; + + hdr[0] = build_nvb_command(NVB_CMD_START, NVB_FLAG_SDBG); + hdr[1] = cpu_to_be32(4); + hdr[2] = cpu_to_be32(addr); + + return write_header(fd, hdr, sizeof(hdr)); +} + +static int open_input_bin(const char *name, void **ptr, size_t *size) +{ + struct stat sbuf; + int ret, fd; + + fd = open(name, O_RDONLY | O_BINARY); + if (0 > fd) { + fprintf(stderr, "Cannot open %s: %s\n", name, + strerror(errno)); + return -1; + } + + ret = fstat(fd, &sbuf); + if (0 > ret) { + fprintf(stderr, "Cannot fstat %s: %s\n", name, + strerror(errno)); + return -1; + } + + *ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (*ptr == MAP_FAILED) { + fprintf(stderr, "Cannot mmap %s: %s\n", name, + strerror(errno)); + return -1; + } + + *size = sbuf.st_size; + + return fd; +} + +static void close_input_bin(int fd, void *ptr, size_t size) +{ + munmap(ptr, size); + close(fd); +} + +static int copy_bin(int fd, void *ptr, size_t size) +{ + ssize_t n; + + n = write(fd, ptr, size); + if (n != size) { + fprintf(stderr, "Cannot copy binary: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int open_output_bin(const char *name) +{ + int fd; + + fd = open(name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (0 > fd) { + fprintf(stderr, "Cannot open %s: %s\n", name, + strerror(errno)); + return -1; + } + + return fd; +} + +static int create_sfspl(const struct args *arg) +{ + int out_fd, uboot_fd, spl_fd, ret; + void *uboot_ptr, *spl_ptr; + size_t uboot_size, spl_size; + + out_fd = open_output_bin(arg->out_bin); + if (0 > out_fd) + goto err; + + spl_fd = open_input_bin(arg->spl_bin, &spl_ptr, &spl_size); + if (0 > spl_fd) + goto err_spl; + + uboot_fd = open_input_bin(arg->uboot_bin, &uboot_ptr, &uboot_size); + if (0 > uboot_fd) + goto err_uboot; + + ret = write_nvb_dwnld_header(out_fd, spl_size, arg->entry_addr); + if (ret) + goto err_write; + + ret = copy_bin(out_fd, spl_ptr, spl_size); + if (ret) + goto err_write; + + ret = write_nvb_start_header(out_fd, arg->entry_addr); + if (ret) + goto err_write; + + ret = copy_bin(out_fd, uboot_ptr, uboot_size); + if (ret) + goto err_write; + + close_input_bin(uboot_fd, uboot_ptr, uboot_size); + close_input_bin(spl_fd, spl_ptr, spl_size); + close(out_fd); + + return 0; + +err_write: + close_input_bin(uboot_fd, uboot_ptr, uboot_size); +err_uboot: + close_input_bin(spl_fd, spl_ptr, spl_size); +err_spl: + close(out_fd); +err: + return -1; +} + +int main(int argc, char *argv[]) +{ + int ret; + struct args arg; + + ret = parse_args(argc, argv, &arg); + if (ret) + goto done; + + switch (arg.type) { + case IMAGE_SFSPL: + ret = create_sfspl(&arg); + break; + default: + fprintf(stderr, "Image type not implemented\n"); + ret = -1; + break; + } + +done: + if (ret >= 0) + return EXIT_SUCCESS; + + return EXIT_FAILURE; +}