1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

[lantiq] adds new lantiq kernel. once the codebase is fully tested and know to be working on all the devices previously supported by ifxmips, we will drop ifxmips support.

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@24526 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
blogic
2010-12-12 22:57:16 +00:00
parent 8d8d887cb0
commit 18a076fccd
52 changed files with 40307 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -1496,7 +1496,18 @@ void __cpuinit per_cpu_trap_init(void)
if (cpu_has_mips_r2) {
cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP;
cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7;
+
+ if (!cp0_compare_irq)
+ cp0_compare_irq = CP0_LEGACY_COMPARE_IRQ;
+
cp0_perfcount_irq = (read_c0_intctl() >> INTCTLB_IPPCI) & 7;
+
+ if (!cp0_perfcount_irq)
+ cp0_perfcount_irq = CP0_LEGACY_PERFCNT_IRQ;
+
+ if (arch_fixup_c0_irqs)
+ arch_fixup_c0_irqs();
+
if (cp0_perfcount_irq == cp0_compare_irq)
cp0_perfcount_irq = -1;
} else {
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -133,9 +133,11 @@ extern void free_irqno(unsigned int irq)
* IE7. Since R2 their number has to be read from the c0_intctl register.
*/
#define CP0_LEGACY_COMPARE_IRQ 7
+#define CP0_LEGACY_PERFCNT_IRQ 7
extern int cp0_compare_irq;
extern int cp0_compare_irq_shift;
extern int cp0_perfcount_irq;
+extern void __weak arch_fixup_c0_irqs(void);
#endif /* _ASM_IRQ_H */

View File

@@ -0,0 +1,33 @@
--- a/arch/mips/kernel/cevt-r4k.c
+++ b/arch/mips/kernel/cevt-r4k.c
@@ -22,6 +22,22 @@
#ifndef CONFIG_MIPS_MT_SMTC
+/*
+ * Compare interrupt can be routed and latched outside the core,
+ * so a single execution hazard barrier may not be enough to give
+ * it time to clear as seen in the Cause register. 4 time the
+ * pipeline depth seems reasonably conservative, and empirically
+ * works better in configurations with high CPU/bus clock ratios.
+ */
+
+#define compare_change_hazard() \
+ do { \
+ irq_disable_hazard(); \
+ irq_disable_hazard(); \
+ irq_disable_hazard(); \
+ irq_disable_hazard(); \
+ } while (0)
+
static int mips_next_event(unsigned long delta,
struct clock_event_device *evt)
{
@@ -31,6 +47,7 @@ static int mips_next_event(unsigned long
cnt = read_c0_count();
cnt += delta;
write_c0_compare(cnt);
+ compare_change_hazard();
res = ((int)(read_c0_count() - cnt) > 0) ? -ETIME : 0;
return res;
}

View File

@@ -0,0 +1,12 @@
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -650,6 +650,9 @@ handle_percpu_irq(unsigned int irq, stru
kstat_incr_irqs_this_cpu(irq, desc);
+ if (unlikely(!desc->action || (desc->status & IRQ_DISABLED)))
+ return;
+
if (desc->chip->ack)
desc->chip->ack(irq);

View File

@@ -0,0 +1,641 @@
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -139,6 +139,9 @@ config MACH_DECSTATION
otherwise choose R3000.
+config LANTIQ
+ bool "Lantiq MIPS"
+
config MACH_JAZZ
bool "Jazz family of machines"
select ARC
@@ -693,6 +696,7 @@ source "arch/mips/txx9/Kconfig"
source "arch/mips/vr41xx/Kconfig"
source "arch/mips/cavium-octeon/Kconfig"
source "arch/mips/loongson/Kconfig"
+source "arch/mips/lantiq/Kconfig"
endmenu
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -317,6 +317,17 @@ cflags-$(CONFIG_MIPS_COBALT) += -I$(srct
load-$(CONFIG_MIPS_COBALT) += 0xffffffff80080000
#
+# Lantiq
+#
+load-$(CONFIG_LANTIQ) += 0xffffffff80002000
+core-$(CONFIG_LANTIQ) += arch/mips/lantiq/
+cflags-$(CONFIG_LANTIQ) += -I$(srctree)/arch/mips/include/asm/mach-lantiq
+core-$(CONFIG_SOC_LANTIQ_FALCON) += arch/mips/lantiq/falcon/
+cflags-$(CONFIG_SOC_LANTIQ_FALCON) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/falcon
+core-$(CONFIG_SOC_LANTIQ_XWAY) += arch/mips/lantiq/xway/
+cflags-$(CONFIG_SOC_LANTIQ_XWAY) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/xway
+
+#
# DECstation family
#
core-$(CONFIG_MACH_DECSTATION) += arch/mips/dec/
--- /dev/null
+++ b/arch/mips/lantiq/Kconfig
@@ -0,0 +1,36 @@
+if LANTIQ
+
+config SOC_LANTIQ
+ bool
+ select DMA_NONCOHERENT
+ select IRQ_CPU
+ select CEVT_R4K
+ select CSRC_R4K
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_HAS_CPU_MIPS32_R2
+ select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_MULTITHREADING
+ select SYS_HAS_EARLY_PRINTK
+ select HW_HAS_PCI
+ select ARCH_REQUIRE_GPIOLIB
+ select SWAP_IO_SPACE
+ select MIPS_MACHINE
+
+choice
+ prompt "SoC Type"
+ default SOC_LANTIQ_XWAY
+
+#config SOC_LANTIQ_FALCON
+# bool "FALCON"
+# select SOC_LANTIQ
+
+config SOC_LANTIQ_XWAY
+ bool "XWAY"
+ select SOC_LANTIQ
+endchoice
+
+#source "arch/mips/lantiq/falcon/Kconfig"
+source "arch/mips/lantiq/xway/Kconfig"
+
+endif
--- /dev/null
+++ b/arch/mips/lantiq/Makefile
@@ -0,0 +1,2 @@
+obj-y := irq.o setup.o clk.o prom.o
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
--- /dev/null
+++ b/arch/mips/lantiq/irq.c
@@ -0,0 +1,212 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include <asm/bootinfo.h>
+#include <asm/irq_cpu.h>
+
+#include <lantiq.h>
+#include <irq.h>
+
+#define LQ_ICU_BASE_ADDR (KSEG1 | 0x1F880200)
+
+#define LQ_ICU_IM0_ISR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0000))
+#define LQ_ICU_IM0_IER ((u32 *)(LQ_ICU_BASE_ADDR + 0x0008))
+#define LQ_ICU_IM0_IOSR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0010))
+#define LQ_ICU_IM0_IRSR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0018))
+#define LQ_ICU_IM0_IMR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0020))
+
+#define LQ_ICU_IM1_ISR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0028))
+#define LQ_ICU_IM2_ISR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0050))
+#define LQ_ICU_IM3_ISR ((u32 *)(LQ_ICU_BASE_ADDR + 0x0078))
+#define LQ_ICU_IM4_ISR ((u32 *)(LQ_ICU_BASE_ADDR + 0x00A0))
+
+#define LQ_ICU_OFFSET (LQ_ICU_IM1_ISR - LQ_ICU_IM0_ISR)
+
+void
+lq_disable_irq(unsigned int irq_nr)
+{
+ u32 *ier = LQ_ICU_IM0_IER;
+ irq_nr -= INT_NUM_IRQ0;
+ ier += LQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
+ irq_nr %= INT_NUM_IM_OFFSET;
+ lq_w32(lq_r32(ier) & ~(1 << irq_nr), ier);
+}
+EXPORT_SYMBOL(lq_disable_irq);
+
+void
+lq_mask_and_ack_irq(unsigned int irq_nr)
+{
+ u32 *ier = LQ_ICU_IM0_IER;
+ u32 *isr = LQ_ICU_IM0_ISR;
+ irq_nr -= INT_NUM_IRQ0;
+ ier += LQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
+ isr += LQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
+ irq_nr %= INT_NUM_IM_OFFSET;
+ lq_w32(lq_r32(ier) & ~(1 << irq_nr), ier);
+ lq_w32((1 << irq_nr), isr);
+}
+EXPORT_SYMBOL(lq_mask_and_ack_irq);
+
+static void
+lq_ack_irq(unsigned int irq_nr)
+{
+ u32 *isr = LQ_ICU_IM0_ISR;
+ irq_nr -= INT_NUM_IRQ0;
+ isr += LQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
+ irq_nr %= INT_NUM_IM_OFFSET;
+ lq_w32((1 << irq_nr), isr);
+}
+
+void
+lq_enable_irq(unsigned int irq_nr)
+{
+ u32 *ier = LQ_ICU_IM0_IER;
+ irq_nr -= INT_NUM_IRQ0;
+ ier += LQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
+ irq_nr %= INT_NUM_IM_OFFSET;
+ lq_w32(lq_r32(ier) | (1 << irq_nr), ier);
+}
+EXPORT_SYMBOL(lq_enable_irq);
+
+static unsigned int
+lq_startup_irq(unsigned int irq)
+{
+ lq_enable_irq(irq);
+ return 0;
+}
+
+static void
+lq_end_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ lq_enable_irq(irq);
+}
+
+static struct irq_chip
+lq_irq_type = {
+ "lq_irq",
+ .startup = lq_startup_irq,
+ .enable = lq_enable_irq,
+ .disable = lq_disable_irq,
+ .unmask = lq_enable_irq,
+ .ack = lq_ack_irq,
+ .mask = lq_disable_irq,
+ .mask_ack = lq_mask_and_ack_irq,
+ .end = lq_end_irq,
+};
+
+static void
+lq_hw_irqdispatch(int module)
+{
+ u32 irq;
+
+ irq = lq_r32(LQ_ICU_IM0_IOSR + (module * LQ_ICU_OFFSET));
+ if (irq == 0)
+ return;
+
+ /* silicon bug causes only the msb set to 1 to be valid. all
+ other bits might be bogus */
+ irq = __fls(irq);
+ do_IRQ((int)irq + INT_NUM_IM0_IRL0 + (INT_NUM_IM_OFFSET * module));
+}
+
+#define DEFINE_HWx_IRQDISPATCH(x) \
+static void lq_hw ## x ## _irqdispatch(void)\
+{\
+ lq_hw_irqdispatch(x); \
+}
+static void lq_hw5_irqdispatch(void)
+{
+ do_IRQ(MIPS_CPU_TIMER_IRQ);
+}
+DEFINE_HWx_IRQDISPATCH(0)
+DEFINE_HWx_IRQDISPATCH(1)
+DEFINE_HWx_IRQDISPATCH(2)
+DEFINE_HWx_IRQDISPATCH(3)
+DEFINE_HWx_IRQDISPATCH(4)
+/*DEFINE_HWx_IRQDISPATCH(5)*/
+
+asmlinkage void
+plat_irq_dispatch(void)
+{
+ unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
+ unsigned int i;
+
+ if (pending & CAUSEF_IP7)
+ {
+ do_IRQ(MIPS_CPU_TIMER_IRQ);
+ goto out;
+ } else {
+ for (i = 0; i < 5; i++)
+ {
+ if (pending & (CAUSEF_IP2 << i))
+ {
+ lq_hw_irqdispatch(i);
+ goto out;
+ }
+ }
+ }
+ printk(KERN_ALERT "Spurious IRQ: CAUSE=0x%08x\n", read_c0_status());
+
+out:
+ return;
+}
+
+static struct irqaction
+cascade = {
+ .handler = no_action,
+ .flags = IRQF_DISABLED,
+ .name = "cascade",
+};
+
+void __init
+arch_init_irq(void)
+{
+ int i;
+
+ for (i = 0; i < 5; i++)
+ lq_w32(0, LQ_ICU_IM0_IER + (i * LQ_ICU_OFFSET));
+
+ mips_cpu_irq_init();
+
+ for (i = 2; i <= 6; i++)
+ setup_irq(i, &cascade);
+
+ if (cpu_has_vint) {
+ printk(KERN_INFO "Setting up vectored interrupts\n");
+ set_vi_handler(2, lq_hw0_irqdispatch);
+ set_vi_handler(3, lq_hw1_irqdispatch);
+ set_vi_handler(4, lq_hw2_irqdispatch);
+ set_vi_handler(5, lq_hw3_irqdispatch);
+ set_vi_handler(6, lq_hw4_irqdispatch);
+ set_vi_handler(7, lq_hw5_irqdispatch);
+ }
+
+ for (i = INT_NUM_IRQ0; i <= (INT_NUM_IRQ0 + (5 * INT_NUM_IM_OFFSET)); i++)
+ set_irq_chip_and_handler(i, &lq_irq_type,
+ handle_level_irq);
+
+ #if !defined(CONFIG_MIPS_MT_SMP) && !defined(CONFIG_MIPS_MT_SMTC)
+ set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 |
+ IE_IRQ3 | IE_IRQ4 | IE_IRQ5);
+ #else
+ set_c0_status(IE_SW0 | IE_SW1 | IE_IRQ0 | IE_IRQ1 |
+ IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5);
+ #endif
+}
+
+void __cpuinit
+arch_fixup_c0_irqs(void)
+{
+ /* FIXME: check for CPUID and only do fix for specific chips/versions */
+ cp0_compare_irq = CP0_LEGACY_COMPARE_IRQ;
+ cp0_perfcount_irq = CP0_LEGACY_PERFCNT_IRQ;
+}
--- /dev/null
+++ b/arch/mips/lantiq/setup.c
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include <lantiq.h>
+#include <lantiq_regs.h>
+
+void __init
+plat_mem_setup(void)
+{
+ /* assume 16M as default */
+ int memsize = 16;
+ char **envp = (char **) KSEG1ADDR(fw_arg2);
+ u32 status;
+
+ /* make sure to have no "reverse endian" for user mode! */
+ status = read_c0_status();
+ status &= (~(1<<25));
+ write_c0_status(status);
+
+ ioport_resource.start = IOPORT_RESOURCE_START;
+ ioport_resource.end = IOPORT_RESOURCE_END;
+ iomem_resource.start = IOMEM_RESOURCE_START;
+ iomem_resource.end = IOMEM_RESOURCE_END;
+
+ while (*envp)
+ {
+ char *e = (char *)KSEG1ADDR(*envp);
+ if (!strncmp(e, "memsize=", 8))
+ {
+ e += 8;
+ memsize = simple_strtoul(e, NULL, 10);
+ }
+ envp++;
+ }
+ memsize *= 1024 * 1024;
+ add_memory_region(0x00000000, memsize, BOOT_MEM_RAM);
+}
--- /dev/null
+++ b/arch/mips/lantiq/clk.c
@@ -0,0 +1,141 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 Thomas Langer, Lantiq Deutschland
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/list.h>
+
+#include <asm/time.h>
+#include <asm/irq.h>
+#include <asm/div64.h>
+
+#include <lantiq.h>
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+#include <xway.h>
+#endif
+
+extern unsigned long lq_get_cpu_hz(void);
+extern unsigned long lq_get_fpi_hz(void);
+extern unsigned long lq_get_io_region_clock(void);
+
+struct clk {
+ const char *name;
+ unsigned long rate;
+ unsigned long (*get_rate) (void);
+};
+
+static struct clk *cpu_clk = 0;
+static int cpu_clk_cnt = 0;
+
+static unsigned int r4k_offset;
+static unsigned int r4k_cur;
+
+static struct clk cpu_clk_generic[] = {
+ {
+ .name = "cpu",
+ .get_rate = lq_get_cpu_hz,
+ }, {
+ .name = "fpi",
+ .get_rate = lq_get_fpi_hz,
+ }, {
+ .name = "io",
+ .get_rate = lq_get_io_region_clock,
+ },
+};
+
+void
+clk_init(void)
+{
+ int i;
+ cpu_clk = cpu_clk_generic;
+ cpu_clk_cnt = ARRAY_SIZE(cpu_clk_generic);
+ for(i = 0; i < cpu_clk_cnt; i++)
+ printk("%s: %ld\n", cpu_clk[i].name, clk_get_rate(&cpu_clk[i]));
+}
+
+static inline int
+clk_good(struct clk *clk)
+{
+ return clk && !IS_ERR(clk);
+}
+
+unsigned long
+clk_get_rate(struct clk *clk)
+{
+ if (unlikely(!clk_good(clk)))
+ return 0;
+
+ if (clk->rate != 0)
+ return clk->rate;
+
+ if (clk->get_rate != NULL)
+ return clk->get_rate();
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+struct clk*
+clk_get(struct device *dev, const char *id)
+{
+ int i;
+ for(i = 0; i < cpu_clk_cnt; i++)
+ if (!strcmp(id, cpu_clk[i].name))
+ return &cpu_clk[i];
+ BUG();
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get);
+
+void
+clk_put(struct clk *clk)
+{
+ /* not used */
+}
+EXPORT_SYMBOL(clk_put);
+
+static inline u32
+lq_get_counter_resolution(void)
+{
+ u32 res;
+ __asm__ __volatile__(
+ ".set push\n"
+ ".set mips32r2\n"
+ ".set noreorder\n"
+ "rdhwr %0, $3\n"
+ "ehb\n"
+ ".set pop\n"
+ : "=&r" (res)
+ : /* no input */
+ : "memory");
+ instruction_hazard();
+ return res;
+}
+
+void __init
+plat_time_init(void)
+{
+ struct clk *clk = clk_get(0, "cpu");
+ mips_hpt_frequency = clk_get_rate(clk) / lq_get_counter_resolution();
+ r4k_cur = (read_c0_count() + r4k_offset);
+ write_c0_compare(r4k_cur);
+
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+#define LQ_GPTU_GPT_CLC ((u32 *)(LQ_GPTU_BASE_ADDR + 0x0000))
+ lq_pmu_enable(PMU_GPT);
+ lq_pmu_enable(PMU_FPI);
+
+ lq_w32(0x100, LQ_GPTU_GPT_CLC);
+#endif
+}
--- /dev/null
+++ b/arch/mips/lantiq/prom.c
@@ -0,0 +1,118 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+
+#include <lantiq.h>
+
+#include "prom.h"
+
+static struct lq_soc_info soc_info;
+
+/* for Multithreading (APRP) on MIPS34K */
+unsigned long physical_memsize;
+
+/* all access to the ebu must be locked */
+DEFINE_SPINLOCK(ebu_lock);
+EXPORT_SYMBOL_GPL(ebu_lock);
+
+extern void clk_init(void);
+
+unsigned int
+lq_get_cpu_ver(void)
+{
+ return soc_info.rev;
+}
+EXPORT_SYMBOL(lq_get_cpu_ver);
+
+unsigned int
+lq_get_soc_type(void)
+{
+ return soc_info.type;
+}
+EXPORT_SYMBOL(lq_get_soc_type);
+
+const char*
+get_system_type(void)
+{
+ return soc_info.sys_type;
+}
+
+void
+prom_free_prom_memory(void)
+{
+}
+
+#ifdef CONFIG_IMAGE_CMDLINE_HACK
+extern char __image_cmdline[];
+
+static void __init
+prom_init_image_cmdline(void)
+{
+ char *p = __image_cmdline;
+ int replace = 0;
+
+ if (*p == '-') {
+ replace = 1;
+ p++;
+ }
+
+ if (*p == '\0')
+ return;
+
+ if (replace) {
+ strlcpy(arcs_cmdline, p, sizeof(arcs_cmdline));
+ } else {
+ strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline));
+ strlcat(arcs_cmdline, p, sizeof(arcs_cmdline));
+ }
+}
+#else
+static void __init prom_init_image_cmdline(void) { return; }
+#endif
+
+static void __init
+prom_init_cmdline(void)
+{
+ int argc = fw_arg0;
+ char **argv = (char**)KSEG1ADDR(fw_arg1);
+ int i;
+
+ arcs_cmdline[0] = '\0';
+ if(argc)
+ for (i = 1; i < argc; i++)
+ {
+ strlcat(arcs_cmdline, (char*)KSEG1ADDR(argv[i]), COMMAND_LINE_SIZE);
+ if(i + 1 != argc)
+ strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE);
+ }
+
+ if (!*arcs_cmdline)
+ strcpy(&(arcs_cmdline[0]),
+ "console=ttyS1,115200 rootfstype=squashfs,jffs2");
+ prom_init_image_cmdline();
+}
+
+void __init
+prom_init(void)
+{
+ struct clk *clk;
+ lq_soc_detect(&soc_info);
+
+ clk_init();
+ clk = clk_get(0, "cpu");
+ snprintf(soc_info.sys_type, LQ_SYS_TYPE_LEN - 1, "%s rev1.%d %ldMhz",
+ soc_info.name, soc_info.rev, clk_get_rate(clk) / 1000000);
+ soc_info.sys_type[LQ_SYS_TYPE_LEN - 1] = '\0';
+ printk("SoC: %s\n", soc_info.sys_type);
+
+ prom_init_cmdline();
+}
--- /dev/null
+++ b/arch/mips/lantiq/prom.h
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _LQ_PROM_H__
+#define _LQ_PROM_H__
+
+#define LQ_SYS_TYPE_LEN 0x100
+
+struct lq_soc_info {
+ unsigned char *name;
+ unsigned int rev;
+ unsigned int partnum;
+ unsigned int type;
+ unsigned char sys_type[LQ_SYS_TYPE_LEN];
+};
+
+void lq_soc_detect(struct lq_soc_info *i);
+
+#endif

View File

@@ -0,0 +1,136 @@
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/war.h
@@ -0,0 +1,24 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+#ifndef __ASM_MIPS_MACH_LANTIQ_WAR_H
+#define __ASM_MIPS_MACH_LANTIQ_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR 0
+#define R4600_V1_HIT_CACHEOP_WAR 0
+#define R4600_V2_HIT_CACHEOP_WAR 0
+#define R5432_CP0_INTERRUPT_WAR 0
+#define BCM1250_M3_WAR 0
+#define SIBYTE_1956_WAR 0
+#define MIPS4K_ICACHE_REFILL_WAR 0
+#define MIPS_CACHE_SYNC_WAR 0
+#define TX49XX_ICACHE_INDEX_INV_WAR 0
+#define RM9000_CDEX_SMP_WAR 0
+#define ICACHE_REFILLS_WORKAROUND_WAR 0
+#define R10000_LLSC_WAR 0
+#define MIPS34K_MISSED_ITLB_WAR 0
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/lantiq.h
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _LANTIQ_H__
+#define _LANTIQ_H__
+
+/* generic reg access functions */
+#define lq_r32(reg) __raw_readl(reg)
+#define lq_w32(val, reg) __raw_writel(val, reg)
+#define lq_w32_mask(clear, set, reg) lq_w32((lq_r32(reg) & ~clear) | set, reg)
+
+extern unsigned int lq_get_cpu_ver(void);
+extern unsigned int lq_get_soc_type(void);
+
+/* clock speeds */
+#define CLOCK_60M 60000000
+#define CLOCK_83M 83333333
+#define CLOCK_111M 111111111
+#define CLOCK_111M 111111111
+#define CLOCK_133M 133333333
+#define CLOCK_167M 166666667
+#define CLOCK_200M 200000000
+#define CLOCK_333M 333333333
+#define CLOCK_400M 400000000
+
+/* spinlock all ebu i/o */
+extern spinlock_t ebu_lock;
+
+/* some irq helpers */
+extern void lq_disable_irq(unsigned int irq_nr);
+extern void lq_mask_and_ack_irq(unsigned int irq_nr);
+extern void lq_enable_irq(unsigned int irq_nr);
+
+#define IOPORT_RESOURCE_START 0x10000000
+#define IOPORT_RESOURCE_END 0xffffffff
+#define IOMEM_RESOURCE_START 0x10000000
+#define IOMEM_RESOURCE_END 0xffffffff
+
+#define LQ_FLASH_START 0x10000000
+#define LQ_FLASH_MAX 0x04000000
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/lantiq_regs.h
@@ -0,0 +1,17 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _LANTIQ_REGS_H__
+#define _LANTIQ_REGS_H__
+
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+#include <xway.h>
+#include <xway_irq.h>
+#endif
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/lantiq_platform.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _LANTIQ_PLATFORM_H__
+#define _LANTIQ_PLATFORM_H__
+
+#include <linux/mtd/partitions.h>
+
+/* struct used to pass info to network drivers */
+enum {
+ MII_MODE,
+ REV_MII_MODE,
+};
+
+struct lq_eth_data {
+ unsigned char *mac;
+ int mii_mode;
+};
+
+/* struct used to pass info to the pci core */
+enum {
+ PCI_CLOCK_INT = 0,
+ PCI_CLOCK_EXT
+};
+
+struct lq_pci_data {
+ int clock;
+ int req_mask;
+};
+
+#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,565 @@
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/xway/irq.h
@@ -0,0 +1,18 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef __LANTIQ_IRQ_H
+#define __LANTIQ_IRQ_H
+
+#include <xway_irq.h>
+
+#define NR_IRQS 256
+
+#include_next <irq.h>
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/lantiq_timer.h
@@ -0,0 +1,155 @@
+#ifndef __DANUBE_GPTU_DEV_H__2005_07_26__10_19__
+#define __DANUBE_GPTU_DEV_H__2005_07_26__10_19__
+
+
+/******************************************************************************
+ Copyright (c) 2002, Infineon Technologies. All rights reserved.
+
+ No Warranty
+ Because the program is licensed free of charge, there is no warranty for
+ the program, to the extent permitted by applicable law. Except when
+ otherwise stated in writing the copyright holders and/or other parties
+ provide the program "as is" without warranty of any kind, either
+ expressed or implied, including, but not limited to, the implied
+ warranties of merchantability and fitness for a particular purpose. The
+ entire risk as to the quality and performance of the program is with
+ you. should the program prove defective, you assume the cost of all
+ necessary servicing, repair or correction.
+
+ In no event unless required by applicable law or agreed to in writing
+ will any copyright holder, or any other party who may modify and/or
+ redistribute the program as permitted above, be liable to you for
+ damages, including any general, special, incidental or consequential
+ damages arising out of the use or inability to use the program
+ (including but not limited to loss of data or data being rendered
+ inaccurate or losses sustained by you or third parties or a failure of
+ the program to operate with any other programs), even if such holder or
+ other party has been advised of the possibility of such damages.
+******************************************************************************/
+
+
+/*
+ * ####################################
+ * Definition
+ * ####################################
+ */
+
+/*
+ * Available Timer/Counter Index
+ */
+#define TIMER(n, X) (n * 2 + (X ? 1 : 0))
+#define TIMER_ANY 0x00
+#define TIMER1A TIMER(1, 0)
+#define TIMER1B TIMER(1, 1)
+#define TIMER2A TIMER(2, 0)
+#define TIMER2B TIMER(2, 1)
+#define TIMER3A TIMER(3, 0)
+#define TIMER3B TIMER(3, 1)
+
+/*
+ * Flag of Timer/Counter
+ * These flags specify the way in which timer is configured.
+ */
+/* Bit size of timer/counter. */
+#define TIMER_FLAG_16BIT 0x0000
+#define TIMER_FLAG_32BIT 0x0001
+/* Switch between timer and counter. */
+#define TIMER_FLAG_TIMER 0x0000
+#define TIMER_FLAG_COUNTER 0x0002
+/* Stop or continue when overflowing/underflowing. */
+#define TIMER_FLAG_ONCE 0x0000
+#define TIMER_FLAG_CYCLIC 0x0004
+/* Count up or counter down. */
+#define TIMER_FLAG_UP 0x0000
+#define TIMER_FLAG_DOWN 0x0008
+/* Count on specific level or edge. */
+#define TIMER_FLAG_HIGH_LEVEL_SENSITIVE 0x0000
+#define TIMER_FLAG_LOW_LEVEL_SENSITIVE 0x0040
+#define TIMER_FLAG_RISE_EDGE 0x0010
+#define TIMER_FLAG_FALL_EDGE 0x0020
+#define TIMER_FLAG_ANY_EDGE 0x0030
+/* Signal is syncronous to module clock or not. */
+#define TIMER_FLAG_UNSYNC 0x0000
+#define TIMER_FLAG_SYNC 0x0080
+/* Different interrupt handle type. */
+#define TIMER_FLAG_NO_HANDLE 0x0000
+#if defined(__KERNEL__)
+ #define TIMER_FLAG_CALLBACK_IN_IRQ 0x0100
+#endif // defined(__KERNEL__)
+#define TIMER_FLAG_SIGNAL 0x0300
+/* Internal clock source or external clock source */
+#define TIMER_FLAG_INT_SRC 0x0000
+#define TIMER_FLAG_EXT_SRC 0x1000
+
+
+/*
+ * ioctl Command
+ */
+#define GPTU_REQUEST_TIMER 0x01 /* General method to setup timer/counter. */
+#define GPTU_FREE_TIMER 0x02 /* Free timer/counter. */
+#define GPTU_START_TIMER 0x03 /* Start or resume timer/counter. */
+#define GPTU_STOP_TIMER 0x04 /* Suspend timer/counter. */
+#define GPTU_GET_COUNT_VALUE 0x05 /* Get current count value. */
+#define GPTU_CALCULATE_DIVIDER 0x06 /* Calculate timer divider from given freq.*/
+#define GPTU_SET_TIMER 0x07 /* Simplified method to setup timer. */
+#define GPTU_SET_COUNTER 0x08 /* Simplified method to setup counter. */
+
+/*
+ * Data Type Used to Call ioctl
+ */
+struct gptu_ioctl_param {
+ unsigned int timer; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and *
+ * GPTU_SET_COUNTER, this field is ID of expected *
+ * timer/counter. If it's zero, a timer/counter would *
+ * be dynamically allocated and ID would be stored in *
+ * this field. *
+ * In command GPTU_GET_COUNT_VALUE, this field is *
+ * ignored. *
+ * In other command, this field is ID of timer/counter *
+ * allocated. */
+ unsigned int flag; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and *
+ * GPTU_SET_COUNTER, this field contains flags to *
+ * specify how to configure timer/counter. *
+ * In command GPTU_START_TIMER, zero indicate start *
+ * and non-zero indicate resume timer/counter. *
+ * In other command, this field is ignored. */
+ unsigned long value; /* In command GPTU_REQUEST_TIMER, this field contains *
+ * init/reload value. *
+ * In command GPTU_SET_TIMER, this field contains *
+ * frequency (0.001Hz) of timer. *
+ * In command GPTU_GET_COUNT_VALUE, current count *
+ * value would be stored in this field. *
+ * In command GPTU_CALCULATE_DIVIDER, this field *
+ * contains frequency wanted, and after calculation, *
+ * divider would be stored in this field to overwrite *
+ * the frequency. *
+ * In other command, this field is ignored. */
+ int pid; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, *
+ * if signal is required, this field contains process *
+ * ID to which signal would be sent. *
+ * In other command, this field is ignored. */
+ int sig; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, *
+ * if signal is required, this field contains signal *
+ * number which would be sent. *
+ * In other command, this field is ignored. */
+};
+
+/*
+ * ####################################
+ * Data Type
+ * ####################################
+ */
+typedef void (*timer_callback)(unsigned long arg);
+
+extern int ifxmips_request_timer(unsigned int, unsigned int, unsigned long, unsigned long, unsigned long);
+extern int ifxmips_free_timer(unsigned int);
+extern int ifxmips_start_timer(unsigned int, int);
+extern int ifxmips_stop_timer(unsigned int);
+extern int ifxmips_reset_counter_flags(u32 timer, u32 flags);
+extern int ifxmips_get_count_value(unsigned int, unsigned long *);
+extern u32 ifxmips_cal_divider(unsigned long);
+extern int ifxmips_set_timer(unsigned int, unsigned int, int, int, unsigned int, unsigned long, unsigned long);
+extern int ifxmips_set_counter(unsigned int timer, unsigned int flag,
+ u32 reload, unsigned long arg1, unsigned long arg2);
+
+#endif /* __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ */
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/xway/xway.h
@@ -0,0 +1,121 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2005 infineon
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+
+#ifndef _LQ_XWAY_H__
+#define _LQ_XWAY_H__
+
+#include <lantiq.h>
+
+/* request a non-gpio and set the PIO config */
+extern int lq_gpio_request(unsigned int pin, unsigned int alt0,
+ unsigned int alt1, unsigned int dir, const char *name);
+extern int lq_gpio_setconfig(unsigned int pin, unsigned int reg, unsigned int val);
+
+extern void lq_pmu_enable(unsigned int module);
+extern void lq_pmu_disable(unsigned int module);
+
+extern unsigned int lq_get_fpi_bus_clock(int bus);
+
+#define BOARD_SYSTEM_TYPE "LANTIQ"
+
+/*------------ Chip IDs */
+#define SOC_ID_DANUBE1 0x129
+#define SOC_ID_DANUBE2 0x12B
+#define SOC_ID_TWINPASS 0x12D
+#define SOC_ID_ARX188 0x16C
+#define SOC_ID_ARX168 0x16D
+#define SOC_ID_ARX182 0x16F
+
+/*------------ SoC Types */
+#define SOC_TYPE_DANUBE 0x01
+#define SOC_TYPE_TWINPASS 0x02
+#define SOC_TYPE_AR9 0x03
+
+/*------------ ASC0/1 */
+#define LQ_ASC0_BASE 0x1E100400
+#define LQ_ASC1_BASE 0x1E100C00
+#define LQ_ASC_SIZE 0x400
+
+/*------------ RCU */
+#define LQ_RCU_BASE_ADDR 0xBF203000
+
+/*------------ GPTU */
+#define LQ_GPTU_BASE_ADDR 0xB8000300
+
+/*------------ EBU */
+#define LQ_EBU_GPIO_START 0x14000000
+#define LQ_EBU_GPIO_SIZE 0x1000
+
+#define LQ_EBU_BASE_ADDR 0xBE105300
+
+#define LQ_EBU_BUSCON0 ((u32 *)(LQ_EBU_BASE_ADDR + 0x0060))
+#define LQ_EBU_PCC_CON ((u32 *)(LQ_EBU_BASE_ADDR + 0x0090))
+#define LQ_EBU_PCC_IEN ((u32 *)(LQ_EBU_BASE_ADDR + 0x00A4))
+#define LQ_EBU_PCC_ISTAT ((u32 *)(LQ_EBU_BASE_ADDR + 0x00A0))
+#define LQ_EBU_BUSCON1 ((u32 *)(LQ_EBU_BASE_ADDR + 0x0064))
+#define LQ_EBU_ADDRSEL1 ((u32 *)(LQ_EBU_BASE_ADDR + 0x0024))
+
+#define EBU_WRDIS 0x80000000
+
+/*------------ CGU */
+#define LQ_CGU_BASE_ADDR (KSEG1 + 0x1F103000)
+
+/*------------ PMU */
+#define LQ_PMU_BASE_ADDR (KSEG1 + 0x1F102000)
+
+#define PMU_DMA 0x0020
+#define PMU_USB 0x8041
+#define PMU_LED 0x0800
+#define PMU_GPT 0x1000
+#define PMU_PPE 0x2000
+#define PMU_FPI 0x4000
+#define PMU_SWITCH 0x10000000
+
+/*------------ ETOP */
+#define LQ_PPE32_BASE_ADDR 0xBE180000
+#define LQ_PPE32_SIZE 0x40000
+
+/*------------ DMA */
+#define LQ_DMA_BASE_ADDR 0xBE104100
+
+/*------------ PCI */
+#define PCI_CR_PR_BASE_ADDR (KSEG1 + 0x1E105400)
+#define PCI_CS_PR_BASE_ADDR (KSEG1 + 0x17000000)
+
+/*------------ WDT */
+#define LQ_WDT_BASE 0x1F880000
+#define LQ_WDT_SIZE 0x400
+
+/*------------ Serial To Parallel conversion */
+#define LQ_STP_BASE 0x1E100BB0
+#define LQ_STP_SIZE 0x40
+
+/*------------ GPIO */
+#define LQ_GPIO0_BASE_ADDR 0x1E100B10
+#define LQ_GPIO1_BASE_ADDR 0x1E100B40
+#define LQ_GPIO_SIZE 0x30
+
+/*------------ SSC */
+#define LQ_SSC_BASE_ADDR (KSEG1 + 0x1e100800)
+
+/*------------ MEI */
+#define LQ_MEI_BASE_ADDR (KSEG1 + 0x1E116000)
+
+/*------------ DEU */
+#define LQ_DEU_BASE (KSEG1 + 0x1E103100)
+
+/*------------ MPS */
+#define LQ_MPS_BASE_ADDR (KSEG1 + 0x1F107000)
+#define LQ_MPS_CHIPID ((u32 *)(LQ_MPS_BASE_ADDR + 0x0344))
+
+#endif
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h
@@ -0,0 +1,144 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2005 infineon
+ * Copyright (C) 2007 John Crispin <blogic@openwrt.org>
+ *
+ */
+#ifndef _LQ_DMA_H__
+#define _LQ_DMA_H__
+
+#define RCV_INT 1
+#define TX_BUF_FULL_INT 2
+#define TRANSMIT_CPT_INT 4
+#define LQ_DMA_CH_ON 1
+#define LQ_DMA_CH_OFF 0
+#define LQ_DMA_CH_DEFAULT_WEIGHT 100
+
+enum attr_t{
+ TX = 0,
+ RX = 1,
+ RESERVED = 2,
+ DEFAULT = 3,
+};
+
+#define DMA_OWN 1
+#define CPU_OWN 0
+#define DMA_MAJOR 250
+
+#define DMA_DESC_OWN_CPU 0x0
+#define DMA_DESC_OWN_DMA 0x80000000
+#define DMA_DESC_CPT_SET 0x40000000
+#define DMA_DESC_SOP_SET 0x20000000
+#define DMA_DESC_EOP_SET 0x10000000
+
+#define MISCFG_MASK 0x40
+#define RDERR_MASK 0x20
+#define CHOFF_MASK 0x10
+#define DESCPT_MASK 0x8
+#define DUR_MASK 0x4
+#define EOP_MASK 0x2
+
+#define DMA_DROP_MASK (1<<31)
+
+#define LQ_DMA_RX -1
+#define LQ_DMA_TX 1
+
+struct dma_chan_map {
+ const char *dev_name;
+ enum attr_t dir;
+ int pri;
+ int irq;
+ int rel_chan_no;
+};
+
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+struct rx_desc {
+ u32 data_length:16;
+ volatile u32 reserved:7;
+ volatile u32 byte_offset:2;
+ volatile u32 Burst_length_offset:3;
+ volatile u32 EoP:1;
+ volatile u32 Res:1;
+ volatile u32 C:1;
+ volatile u32 OWN:1;
+ volatile u32 Data_Pointer; /* fixme: should be 28 bits here */
+};
+
+struct tx_desc {
+ volatile u32 data_length:16;
+ volatile u32 reserved1:7;
+ volatile u32 byte_offset:5;
+ volatile u32 EoP:1;
+ volatile u32 SoP:1;
+ volatile u32 C:1;
+ volatile u32 OWN:1;
+ volatile u32 Data_Pointer; /* fixme: should be 28 bits here */
+};
+#else /* BIG */
+struct rx_desc {
+ union {
+ struct {
+ volatile u32 OWN:1;
+ volatile u32 C:1;
+ volatile u32 SoP:1;
+ volatile u32 EoP:1;
+ volatile u32 Burst_length_offset:3;
+ volatile u32 byte_offset:2;
+ volatile u32 reserve:7;
+ volatile u32 data_length:16;
+ } field;
+ volatile u32 word;
+ } status;
+ volatile u32 Data_Pointer;
+};
+
+struct tx_desc {
+ union {
+ struct {
+ volatile u32 OWN:1;
+ volatile u32 C:1;
+ volatile u32 SoP:1;
+ volatile u32 EoP:1;
+ volatile u32 byte_offset:5;
+ volatile u32 reserved:7;
+ volatile u32 data_length:16;
+ } field;
+ volatile u32 word;
+ } status;
+ volatile u32 Data_Pointer;
+};
+#endif /* ENDIAN */
+
+struct dma_channel_info {
+ /* relative channel number */
+ int rel_chan_no;
+ /* class for this channel for QoS */
+ int pri;
+ /* specify byte_offset */
+ int byte_offset;
+ /* direction */
+ int dir;
+ /* irq number */
+ int irq;
+ /* descriptor parameter */
+ int desc_base;
+ int desc_len;
+ int curr_desc;
+ int prev_desc; /* only used if it is a tx channel*/
+ /* weight setting for WFQ algorithm*/
+ int weight;
+ int default_weight;
+ int packet_size;
+ int burst_len;
+ /* on or off of this channel */
+ int control;
+ /* optional information for the upper layer devices */
+#if defined(CONFIG_LQ_ETHERNET_D2) || defined(CONFIG_LQ_PPA)
+ void *opt[64];
+#else
+ void *opt[25];
+#endif
+ /* Pointer to the peripheral device who is using this channel */
+ void *dma_dev;
+ /* channel operations */
+ void (*open)(struct dma_channel_info *pCh);
+ void (*close)(struct dma_channel_info *pCh);
+ void (*reset)(struct dma_channel_info *pCh);
+ void (*enable_irq)(struct dma_channel_info *pCh);
+ void (*disable_irq)(struct dma_channel_info *pCh);
+};
+
+struct dma_device_info {
+ /* device name of this peripheral */
+ char device_name[15];
+ int reserved;
+ int tx_burst_len;
+ int rx_burst_len;
+ int default_weight;
+ int current_tx_chan;
+ int current_rx_chan;
+ int num_tx_chan;
+ int num_rx_chan;
+ int max_rx_chan_num;
+ int max_tx_chan_num;
+ struct dma_channel_info *tx_chan[20];
+ struct dma_channel_info *rx_chan[20];
+ /*functions, optional*/
+ u8 *(*buffer_alloc)(int len, int *offset, void **opt);
+ void (*buffer_free)(u8 *dataptr, void *opt);
+ int (*intr_handler)(struct dma_device_info *info, int status);
+ void *priv; /* used by peripheral driver only */
+};
+
+struct dma_device_info *dma_device_reserve(char *dev_name);
+void dma_device_release(struct dma_device_info *dev);
+void dma_device_register(struct dma_device_info *info);
+void dma_device_unregister(struct dma_device_info *info);
+int dma_device_read(struct dma_device_info *info, u8 **dataptr, void **opt);
+int dma_device_write(struct dma_device_info *info, u8 *dataptr, int len,
+ void *opt);
+
+#endif
+
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/xway/xway_irq.h
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _LANTIQ_XWAY_IRQ_H__
+#define _LANTIQ_XWAY_IRQ_H__
+
+#define INT_NUM_IRQ0 8
+#define INT_NUM_IM0_IRL0 (INT_NUM_IRQ0 + 0)
+#define INT_NUM_IM1_IRL0 (INT_NUM_IRQ0 + 32)
+#define INT_NUM_IM2_IRL0 (INT_NUM_IRQ0 + 64)
+#define INT_NUM_IM3_IRL0 (INT_NUM_IRQ0 + 96)
+#define INT_NUM_IM4_IRL0 (INT_NUM_IRQ0 + 128)
+#define INT_NUM_IM_OFFSET (INT_NUM_IM1_IRL0 - INT_NUM_IM0_IRL0)
+
+#define LQ_ASC_TIR(x) (INT_NUM_IM3_IRL0 + (x * 7))
+#define LQ_ASC_RIR(x) (INT_NUM_IM3_IRL0 + (x * 7) + 2)
+#define LQ_ASC_EIR(x) (INT_NUM_IM3_IRL0 + (x * 7) + 3)
+
+#define LQ_SSC_TIR (INT_NUM_IM0_IRL0 + 15)
+#define LQ_SSC_RIR (INT_NUM_IM0_IRL0 + 14)
+#define LQ_SSC_EIR (INT_NUM_IM0_IRL0 + 16)
+
+#define LQ_MEI_DYING_GASP_INT (INT_NUM_IM1_IRL0 + 21)
+#define LQ_MEI_INT (INT_NUM_IM1_IRL0 + 23)
+
+#define LQ_TIMER6_INT (INT_NUM_IM1_IRL0 + 23)
+#define LQ_USB_INT (INT_NUM_IM1_IRL0 + 22)
+#define LQ_USB_OC_INT (INT_NUM_IM4_IRL0 + 23)
+
+#define MIPS_CPU_TIMER_IRQ 7
+
+#define LQ_DMA_CH0_INT (INT_NUM_IM2_IRL0)
+#define LQ_DMA_CH1_INT (INT_NUM_IM2_IRL0 + 1)
+#define LQ_DMA_CH2_INT (INT_NUM_IM2_IRL0 + 2)
+#define LQ_DMA_CH3_INT (INT_NUM_IM2_IRL0 + 3)
+#define LQ_DMA_CH4_INT (INT_NUM_IM2_IRL0 + 4)
+#define LQ_DMA_CH5_INT (INT_NUM_IM2_IRL0 + 5)
+#define LQ_DMA_CH6_INT (INT_NUM_IM2_IRL0 + 6)
+#define LQ_DMA_CH7_INT (INT_NUM_IM2_IRL0 + 7)
+#define LQ_DMA_CH8_INT (INT_NUM_IM2_IRL0 + 8)
+#define LQ_DMA_CH9_INT (INT_NUM_IM2_IRL0 + 9)
+#define LQ_DMA_CH10_INT (INT_NUM_IM2_IRL0 + 10)
+#define LQ_DMA_CH11_INT (INT_NUM_IM2_IRL0 + 11)
+#define LQ_DMA_CH12_INT (INT_NUM_IM2_IRL0 + 25)
+#define LQ_DMA_CH13_INT (INT_NUM_IM2_IRL0 + 26)
+#define LQ_DMA_CH14_INT (INT_NUM_IM2_IRL0 + 27)
+#define LQ_DMA_CH15_INT (INT_NUM_IM2_IRL0 + 28)
+#define LQ_DMA_CH16_INT (INT_NUM_IM2_IRL0 + 29)
+#define LQ_DMA_CH17_INT (INT_NUM_IM2_IRL0 + 30)
+#define LQ_DMA_CH18_INT (INT_NUM_IM2_IRL0 + 16)
+#define LQ_DMA_CH19_INT (INT_NUM_IM2_IRL0 + 21)
+
+#define LQ_PPE_MBOX_INT (INT_NUM_IM2_IRL0 + 24)
+
+#define INT_NUM_IM4_IRL14 (INT_NUM_IM4_IRL0 + 14)
+
+#endif

View File

@@ -0,0 +1,93 @@
--- a/arch/mips/lantiq/Kconfig
+++ b/arch/mips/lantiq/Kconfig
@@ -33,4 +33,19 @@ endchoice
source "arch/mips/lantiq/falcon/Kconfig"
source "arch/mips/lantiq/xway/Kconfig"
+if EARLY_PRINTK
+choice
+ prompt "Early printk port"
+ default LANTIQ_PROM_ASC1
+ help
+ Choose which serial port is used, until the console driver is loaded
+
+config LANTIQ_PROM_ASC0
+ bool "ASC0"
+
+config LANTIQ_PROM_ASC1
+ bool "ASC1"
+endchoice
+endif
+
endif
--- /dev/null
+++ b/arch/mips/lantiq/early_printk.c
@@ -0,0 +1,68 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/cpu.h>
+
+#include <lantiq.h>
+
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+#include <xway.h>
+#ifdef CONFIG_LANTIQ_PROM_ASC0
+#define LQ_ASC_BASE KSEG1ADDR(LQ_ASC0_BASE)
+#else
+#define LQ_ASC_BASE KSEG1ADDR(LQ_ASC1_BASE)
+#endif
+
+#elif CONFIG_SOC_LANTIQ_FALCON
+#include <falcon/gpon_reg_base.h>
+#ifdef CONFIG_LANTIQ_PROM_ASC0
+#define LQ_ASC_BASE GPON_ASC0_BASE
+#else
+#define LQ_ASC_BASE GPON_ASC1_BASE
+#endif
+
+#endif
+
+#define ASC_BUF 1024
+#define LQ_ASC_FSTAT 0x0048
+#define LQ_ASC_TBUF 0x0020
+#define TXMASK 0x3F00
+#define TXOFFSET 8
+
+static char buf[ASC_BUF];
+
+void
+prom_putchar(char c)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ while ((lq_r32((u32 *)(LQ_ASC_BASE + LQ_ASC_FSTAT)) & TXMASK) >> TXOFFSET);
+
+ if (c == '\n')
+ lq_w32('\r', (u32 *)(LQ_ASC_BASE + LQ_ASC_TBUF));
+ lq_w32(c, (u32 *)(LQ_ASC_BASE + LQ_ASC_TBUF));
+ local_irq_restore(flags);
+}
+
+void
+early_printf(const char *fmt, ...)
+{
+ va_list args;
+ int l;
+ char *p, *buf_end;
+
+ va_start(args, fmt);
+ l = vsnprintf(buf, ASC_BUF, fmt, args);
+ va_end(args);
+ buf_end = buf + l;
+
+ for (p = buf; p < buf_end; p++)
+ prom_putchar(*p);
+}

View File

@@ -0,0 +1,55 @@
--- a/arch/mips/lantiq/setup.c
+++ b/arch/mips/lantiq/setup.c
@@ -13,7 +13,8 @@
#include <linux/ioport.h>
#include <lantiq.h>
-#include <lantiq_regs.h>
+
+#include <machine.h>
void __init
plat_mem_setup(void)
@@ -46,3 +47,25 @@ plat_mem_setup(void)
memsize *= 1024 * 1024;
add_memory_region(0x00000000, memsize, BOOT_MEM_RAM);
}
+
+static int __init
+lq_machine_setup(void)
+{
+ mips_machine_setup();
+ return 0;
+}
+
+static void __init
+mach_generic_init(void)
+{
+}
+
+MIPS_MACHINE(LANTIQ_MACH_GENERIC,
+ "Generic",
+ "Generic",
+ mach_generic_init);
+
+arch_initcall(lq_machine_setup);
+
+/* for backward compatibility, define "board=" as alias for "machtype=" */
+__setup("board=", mips_machtype_setup);
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/machine.h
@@ -0,0 +1,14 @@
+#include <asm/mips_machine.h>
+
+enum lantiq_mach_type {
+ LANTIQ_MACH_GENERIC,
+
+ /* FALCON */
+ LANTIQ_MACH_EASY98000, /* Falcon Eval Board, NOR Flash */
+ LANTIQ_MACH_EASY98020, /* Falcon Reference Board */
+
+ /* XWAY */
+ LANTIQ_MACH_EASY4010, /* Twinpass evalkit */
+ LANTIQ_MACH_EASY50712, /* Danube evalkit */
+ LANTIQ_MACH_EASY50812, /* AR9 eval board */
+};

View File

@@ -0,0 +1,799 @@
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1397,6 +1397,14 @@
help
Support for Console on the NWP serial ports.
+config SERIAL_LANTIQ
+ bool "Lantiq serial driver"
+ depends on LANTIQ
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ help
+ Driver for the Lantiq SoC ASC hardware
+
config SERIAL_QE
tristate "Freescale QUICC Engine serial port support"
depends on QUICC_ENGINE
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -84,3 +84,4 @@
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o
--- /dev/null
+++ b/drivers/serial/lantiq.c
@@ -0,0 +1,772 @@
+/*
+ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2004 Infineon IFAP DC COM CPE
+ * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2007 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2010 Thomas Langer, Lantiq Deutschland
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#define lq_r32(reg) __raw_readl(reg)
+#define lq_r8(reg) __raw_readb(reg)
+#define lq_w32(val, reg) __raw_writel(val, reg)
+#define lq_w8(val, reg) __raw_writeb(val, reg)
+#define lq_w32_mask(clear, set, reg) lq_w32((lq_r32(reg) & ~(clear)) | (set), reg)
+
+#define PORT_IFXMIPSASC 111
+#define MAXPORTS 2
+
+#define UART_DUMMY_UER_RX 1
+
+#define DRVNAME "lq_asc"
+
+#ifdef __BIG_ENDIAN
+#define IFXMIPS_ASC_TBUF (0x0020 + 3)
+#define IFXMIPS_ASC_RBUF (0x0024 + 3)
+#else
+#define IFXMIPS_ASC_TBUF 0x0020
+#define IFXMIPS_ASC_RBUF 0x0024
+#endif
+
+#define IFXMIPS_ASC_FSTAT 0x0048
+#define IFXMIPS_ASC_WHBSTATE 0x0018
+#define IFXMIPS_ASC_STATE 0x0014
+#define IFXMIPS_ASC_IRNCR 0x00F8
+#define IFXMIPS_ASC_CLC 0x0000
+#define IFXMIPS_ASC_ID 0x0008
+#define IFXMIPS_ASC_PISEL 0x0004
+#define IFXMIPS_ASC_TXFCON 0x0044
+#define IFXMIPS_ASC_RXFCON 0x0040
+#define IFXMIPS_ASC_CON 0x0010
+#define IFXMIPS_ASC_BG 0x0050
+#define IFXMIPS_ASC_IRNREN 0x00F4
+
+#define ASC_IRNREN_TX 0x1
+#define ASC_IRNREN_RX 0x2
+#define ASC_IRNREN_ERR 0x4
+#define ASC_IRNREN_TX_BUF 0x8
+#define ASC_IRNCR_TIR 0x1
+#define ASC_IRNCR_RIR 0x2
+#define ASC_IRNCR_EIR 0x4
+
+#define ASCOPT_CSIZE 0x3
+#define ASCOPT_CS7 0x1
+#define ASCOPT_CS8 0x2
+#define ASCOPT_PARENB 0x4
+#define ASCOPT_STOPB 0x8
+#define ASCOPT_PARODD 0x0
+#define ASCOPT_CREAD 0x20
+#define TXFIFO_FL 1
+#define RXFIFO_FL 1
+#define ASCCLC_DISS 0x2
+#define ASCCLC_RMCMASK 0x0000FF00
+#define ASCCLC_RMCOFFSET 8
+#define ASCCON_M_8ASYNC 0x0
+#define ASCCON_M_7ASYNC 0x2
+#define ASCCON_ODD 0x00000020
+#define ASCCON_STP 0x00000080
+#define ASCCON_BRS 0x00000100
+#define ASCCON_FDE 0x00000200
+#define ASCCON_R 0x00008000
+#define ASCCON_FEN 0x00020000
+#define ASCCON_ROEN 0x00080000
+#define ASCCON_TOEN 0x00100000
+#define ASCSTATE_PE 0x00010000
+#define ASCSTATE_FE 0x00020000
+#define ASCSTATE_ROE 0x00080000
+#define ASCSTATE_ANY (ASCSTATE_ROE|ASCSTATE_PE|ASCSTATE_FE)
+#define ASCWHBSTATE_CLRREN 0x00000001
+#define ASCWHBSTATE_SETREN 0x00000002
+#define ASCWHBSTATE_CLRPE 0x00000004
+#define ASCWHBSTATE_CLRFE 0x00000008
+#define ASCWHBSTATE_CLRROE 0x00000020
+#define ASCTXFCON_TXFEN 0x0001
+#define ASCTXFCON_TXFFLU 0x0002
+#define ASCTXFCON_TXFITLMASK 0x3F00
+#define ASCTXFCON_TXFITLOFF 8
+#define ASCRXFCON_RXFEN 0x0001
+#define ASCRXFCON_RXFFLU 0x0002
+#define ASCRXFCON_RXFITLMASK 0x3F00
+#define ASCRXFCON_RXFITLOFF 8
+#define ASCFSTAT_RXFFLMASK 0x003F
+#define ASCFSTAT_TXFFLMASK 0x3F00
+#define ASCFSTAT_TXFFLOFF 8
+#define ASCFSTAT_RXFREEMASK 0x003F0000
+#define ASCFSTAT_RXFREEOFF 16
+#define ASCFSTAT_TXFREEMASK 0x3F000000
+#define ASCFSTAT_TXFREEOFF 24
+
+static void lqasc_tx_chars(struct uart_port *port);
+extern void prom_printf(const char *fmt, ...);
+static struct lq_uart_port *lqasc_port[2];
+static struct uart_driver lqasc_reg;
+
+struct lq_uart_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int tx_irq;
+ unsigned int rx_irq;
+ unsigned int err_irq;
+};
+
+static inline struct
+lq_uart_port *to_lq_uart_port(struct uart_port *port)
+{
+ return container_of(port, struct lq_uart_port, port);
+}
+
+static void
+lqasc_stop_tx(struct uart_port *port)
+{
+ return;
+}
+
+static void
+lqasc_start_tx(struct uart_port *port)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ lqasc_tx_chars(port);
+ local_irq_restore(flags);
+ return;
+}
+
+static void
+lqasc_stop_rx(struct uart_port *port)
+{
+ lq_w32(ASCWHBSTATE_CLRREN, port->membase + IFXMIPS_ASC_WHBSTATE);
+}
+
+static void
+lqasc_enable_ms(struct uart_port *port)
+{
+}
+
+static void
+lqasc_rx_chars(struct uart_port *port)
+{
+ struct tty_struct *tty = port->state->port.tty;
+ unsigned int ch = 0, rsr = 0, fifocnt;
+
+ fifocnt = lq_r32(port->membase + IFXMIPS_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
+ while (fifocnt--) {
+ u8 flag = TTY_NORMAL;
+ ch = lq_r8(port->membase + IFXMIPS_ASC_RBUF);
+ rsr = (lq_r32(port->membase + IFXMIPS_ASC_STATE)
+ & ASCSTATE_ANY) | UART_DUMMY_UER_RX;
+ tty_flip_buffer_push(tty);
+ port->icount.rx++;
+
+ /*
+ * Note that the error handling code is
+ * out of the main execution path
+ */
+ if (rsr & ASCSTATE_ANY) {
+ if (rsr & ASCSTATE_PE) {
+ port->icount.parity++;
+ lq_w32_mask(0, ASCWHBSTATE_CLRPE,
+ port->membase + IFXMIPS_ASC_WHBSTATE);
+ } else if (rsr & ASCSTATE_FE) {
+ port->icount.frame++;
+ lq_w32_mask(0, ASCWHBSTATE_CLRFE,
+ port->membase + IFXMIPS_ASC_WHBSTATE);
+ }
+ if (rsr & ASCSTATE_ROE) {
+ port->icount.overrun++;
+ lq_w32_mask(0, ASCWHBSTATE_CLRROE,
+ port->membase + IFXMIPS_ASC_WHBSTATE);
+ }
+
+ rsr &= port->read_status_mask;
+
+ if (rsr & ASCSTATE_PE)
+ flag = TTY_PARITY;
+ else if (rsr & ASCSTATE_FE)
+ flag = TTY_FRAME;
+ }
+
+ if ((rsr & port->ignore_status_mask) == 0)
+ tty_insert_flip_char(tty, ch, flag);
+
+ if (rsr & ASCSTATE_ROE)
+ /*
+ * Overrun is special, since it's reported
+ * immediately, and doesn't affect the current
+ * character
+ */
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ }
+ if (ch != 0)
+ tty_flip_buffer_push(tty);
+ return;
+}
+
+static void
+lqasc_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ if (uart_tx_stopped(port)) {
+ lqasc_stop_tx(port);
+ return;
+ }
+
+ while (((lq_r32(port->membase + IFXMIPS_ASC_FSTAT) &
+ ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) {
+ if (port->x_char) {
+ lq_w8(port->x_char, port->membase + IFXMIPS_ASC_TBUF);
+ port->icount.tx++;
+ port->x_char = 0;
+ continue;
+ }
+
+ if (uart_circ_empty(xmit))
+ break;
+
+ lq_w8(port->state->xmit.buf[port->state->xmit.tail],
+ port->membase + IFXMIPS_ASC_TBUF);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+}
+
+static irqreturn_t
+lqasc_tx_int(int irq, void *_port)
+{
+ struct uart_port *port = (struct uart_port *)_port;
+ lq_w32(ASC_IRNCR_TIR, port->membase + IFXMIPS_ASC_IRNCR);
+ lqasc_start_tx(port);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+lqasc_err_int(int irq, void *_port)
+{
+ struct uart_port *port = (struct uart_port *)_port;
+ /* clear any pending interrupts */
+ lq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE | ASCWHBSTATE_CLRROE,
+ port->membase + IFXMIPS_ASC_WHBSTATE);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+lqasc_rx_int(int irq, void *_port)
+{
+ struct uart_port *port = (struct uart_port *)_port;
+ lq_w32(ASC_IRNCR_RIR, port->membase + IFXMIPS_ASC_IRNCR);
+ lqasc_rx_chars(port);
+ return IRQ_HANDLED;
+}
+
+static unsigned int
+lqasc_tx_empty(struct uart_port *port)
+{
+ int status;
+ status = lq_r32(port->membase + IFXMIPS_ASC_FSTAT) & ASCFSTAT_TXFFLMASK;
+ return status ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int
+lqasc_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void
+lqasc_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+}
+
+static void
+lqasc_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int
+lqasc_startup(struct uart_port *port)
+{
+ struct lq_uart_port *ifx_port = to_lq_uart_port(port);
+ int retval;
+
+ port->uartclk = clk_get_rate(ifx_port->clk);
+
+ lq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
+ port->membase + IFXMIPS_ASC_CLC);
+
+ lq_w32(0, port->membase + IFXMIPS_ASC_PISEL);
+ lq_w32(
+ ((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) |
+ ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU,
+ port->membase + IFXMIPS_ASC_TXFCON);
+ lq_w32(
+ ((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK)
+ | ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU,
+ port->membase + IFXMIPS_ASC_RXFCON);
+ /* make sure other settings are written to hardware before setting enable bits */
+ wmb();
+ lq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
+ ASCCON_ROEN, port->membase + IFXMIPS_ASC_CON);
+
+ retval = request_irq(ifx_port->tx_irq, lqasc_tx_int,
+ IRQF_DISABLED, "asc_tx", port);
+ if (retval) {
+ pr_err("failed to request lqasc_tx_int\n");
+ return retval;
+ }
+
+ retval = request_irq(ifx_port->rx_irq, lqasc_rx_int,
+ IRQF_DISABLED, "asc_rx", port);
+ if (retval) {
+ pr_err("failed to request lqasc_rx_int\n");
+ goto err1;
+ }
+
+ retval = request_irq(ifx_port->err_irq, lqasc_err_int,
+ IRQF_DISABLED, "asc_err", port);
+ if (retval) {
+ pr_err("failed to request lqasc_err_int\n");
+ goto err2;
+ }
+
+ lq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
+ port->membase + IFXMIPS_ASC_IRNREN);
+ return 0;
+
+err2:
+ free_irq(ifx_port->rx_irq, port);
+err1:
+ free_irq(ifx_port->tx_irq, port);
+ return retval;
+}
+
+static void
+lqasc_shutdown(struct uart_port *port)
+{
+ struct lq_uart_port *ifx_port = to_lq_uart_port(port);
+ free_irq(ifx_port->tx_irq, port);
+ free_irq(ifx_port->rx_irq, port);
+ free_irq(ifx_port->err_irq, port);
+
+ lq_w32(0, port->membase + IFXMIPS_ASC_CON);
+ lq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
+ port->membase + IFXMIPS_ASC_RXFCON);
+ lq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
+ port->membase + IFXMIPS_ASC_TXFCON);
+}
+
+static void
+lqasc_set_termios(struct uart_port *port,
+ struct ktermios *new, struct ktermios *old)
+{
+ unsigned int cflag;
+ unsigned int iflag;
+ unsigned int quot;
+ unsigned int baud;
+ unsigned int con = 0;
+ unsigned long flags;
+
+ cflag = new->c_cflag;
+ iflag = new->c_iflag;
+
+ switch (cflag & CSIZE) {
+ case CS7:
+ con = ASCCON_M_7ASYNC;
+ break;
+
+ case CS5:
+ case CS6:
+ default:
+ con = ASCCON_M_8ASYNC;
+ break;
+ }
+
+ if (cflag & CSTOPB)
+ con |= ASCCON_STP;
+
+ if (cflag & PARENB) {
+ if (!(cflag & PARODD))
+ con &= ~ASCCON_ODD;
+ else
+ con |= ASCCON_ODD;
+ }
+
+ port->read_status_mask = ASCSTATE_ROE;
+ if (iflag & INPCK)
+ port->read_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
+
+ port->ignore_status_mask = 0;
+ if (iflag & IGNPAR)
+ port->ignore_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
+
+ if (iflag & IGNBRK) {
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (iflag & IGNPAR)
+ port->ignore_status_mask |= ASCSTATE_ROE;
+ }
+
+ if ((cflag & CREAD) == 0)
+ port->ignore_status_mask |= UART_DUMMY_UER_RX;
+
+ /* set error signals - framing, parity and overrun, enable receiver */
+ con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN;
+
+ local_irq_save(flags);
+
+ /* set up CON */
+ lq_w32_mask(0, con, port->membase + IFXMIPS_ASC_CON);
+
+ /* Set baud rate - take a divider of 2 into account */
+ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+ quot = uart_get_divisor(port, baud);
+ quot = quot / 2 - 1;
+
+ /* disable the baudrate generator */
+ lq_w32_mask(ASCCON_R, 0, port->membase + IFXMIPS_ASC_CON);
+
+ /* make sure the fractional divider is off */
+ lq_w32_mask(ASCCON_FDE, 0, port->membase + IFXMIPS_ASC_CON);
+
+ /* set up to use divisor of 2 */
+ lq_w32_mask(ASCCON_BRS, 0, port->membase + IFXMIPS_ASC_CON);
+
+ /* now we can write the new baudrate into the register */
+ lq_w32(quot, port->membase + IFXMIPS_ASC_BG);
+
+ /* turn the baudrate generator back on */
+ lq_w32_mask(0, ASCCON_R, port->membase + IFXMIPS_ASC_CON);
+
+ /* enable rx */
+ lq_w32(ASCWHBSTATE_SETREN, port->membase + IFXMIPS_ASC_WHBSTATE);
+
+ local_irq_restore(flags);
+}
+
+static const char*
+lqasc_type(struct uart_port *port)
+{
+ if (port->type == PORT_IFXMIPSASC)
+ return DRVNAME;
+ else
+ return NULL;
+}
+
+static void
+lqasc_release_port(struct uart_port *port)
+{
+ if (port->flags & UPF_IOREMAP) {
+ iounmap(port->membase);
+ port->membase = NULL;
+ }
+}
+
+static int
+lqasc_request_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *mmres;
+ int size;
+
+ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mmres)
+ return -ENODEV;
+ size = resource_size(mmres);
+
+ if (port->flags & UPF_IOREMAP) {
+ port->membase = ioremap_nocache(port->mapbase, size);
+ if (port->membase == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void
+lqasc_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_IFXMIPSASC;
+ lqasc_request_port(port);
+ }
+}
+
+static int
+lqasc_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_IFXMIPSASC)
+ ret = -EINVAL;
+ if (ser->irq < 0 || ser->irq >= NR_IRQS)
+ ret = -EINVAL;
+ if (ser->baud_base < 9600)
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops lqasc_pops = {
+ .tx_empty = lqasc_tx_empty,
+ .set_mctrl = lqasc_set_mctrl,
+ .get_mctrl = lqasc_get_mctrl,
+ .stop_tx = lqasc_stop_tx,
+ .start_tx = lqasc_start_tx,
+ .stop_rx = lqasc_stop_rx,
+ .enable_ms = lqasc_enable_ms,
+ .break_ctl = lqasc_break_ctl,
+ .startup = lqasc_startup,
+ .shutdown = lqasc_shutdown,
+ .set_termios = lqasc_set_termios,
+ .type = lqasc_type,
+ .release_port = lqasc_release_port,
+ .request_port = lqasc_request_port,
+ .config_port = lqasc_config_port,
+ .verify_port = lqasc_verify_port,
+};
+
+static void
+lqasc_console_putchar(struct uart_port *port, int ch)
+{
+ int fifofree;
+
+ if (!port->membase)
+ return;
+
+ do {
+ fifofree = (lq_r32(port->membase + IFXMIPS_ASC_FSTAT)
+ & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF;
+ } while (fifofree == 0);
+ lq_w8(ch, port->membase + IFXMIPS_ASC_TBUF);
+}
+
+
+static void
+lqasc_console_write(struct console *co, const char *s, u_int count)
+{
+ struct lq_uart_port *ifx_port;
+ struct uart_port *port;
+ unsigned long flags;
+
+ if (co->index >= MAXPORTS)
+ return;
+
+ ifx_port = lqasc_port[co->index];
+ if (!ifx_port)
+ return;
+
+ port = &ifx_port->port;
+
+ local_irq_save(flags);
+ uart_console_write(port, s, count, lqasc_console_putchar);
+ local_irq_restore(flags);
+}
+
+static int __init
+lqasc_console_setup(struct console *co, char *options)
+{
+ struct lq_uart_port *ifx_port;
+ struct uart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= MAXPORTS)
+ return -ENODEV;
+
+ ifx_port = lqasc_port[co->index];
+ if (!ifx_port)
+ return -ENODEV;
+
+ port = &ifx_port->port;
+
+ port->uartclk = clk_get_rate(ifx_port->clk);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console lqasc_console = {
+ .name = "ttyS",
+ .write = lqasc_console_write,
+ .device = uart_console_device,
+ .setup = lqasc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &lqasc_reg,
+};
+
+static int __init
+lqasc_console_init(void)
+{
+ register_console(&lqasc_console);
+ return 0;
+}
+console_initcall(lqasc_console_init);
+
+static struct uart_driver lqasc_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRVNAME,
+ .dev_name = "ttyS",
+ .major = TTY_MAJOR,
+ .minor = 64,
+ .nr = MAXPORTS,
+ .cons = &lqasc_console,
+};
+
+static int __devinit
+lqasc_probe(struct platform_device *pdev)
+{
+ struct lq_uart_port *ifx_port;
+ struct uart_port *port;
+ struct resource *mmres, *irqres;
+ int tx_irq, rx_irq, err_irq;
+ struct clk *clk;
+ int ret;
+
+ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mmres || !irqres)
+ return -ENODEV;
+
+ if (pdev->id >= MAXPORTS)
+ return -EBUSY;
+
+ if (lqasc_port[pdev->id] != NULL)
+ return -EBUSY;
+
+ clk = clk_get(&pdev->dev, "fpi");
+ if (IS_ERR(clk)) {
+ pr_err("failed to get fpi clk\n");
+ return -ENOENT;
+ }
+
+ tx_irq = platform_get_irq_byname(pdev, "tx");
+ if (tx_irq < 0) {
+ /* without named resources: assume standard irq scheme */
+ tx_irq = irqres->start;
+ rx_irq = irqres->start+2;
+ err_irq = irqres->start+3;
+ } else {
+ /* other irqs must be named also! */
+ rx_irq = platform_get_irq_byname(pdev, "rx");
+ err_irq = platform_get_irq_byname(pdev, "err");
+ if ((rx_irq < 0) | (err_irq < 0))
+ return -ENODEV;
+ }
+
+ ifx_port = kzalloc(sizeof(struct lq_uart_port), GFP_KERNEL);
+ if (!ifx_port)
+ return -ENOMEM;
+
+ port = &ifx_port->port;
+
+ port->iotype = SERIAL_IO_MEM;
+ port->flags = ASYNC_BOOT_AUTOCONF | UPF_IOREMAP;
+ port->ops = &lqasc_pops;
+ port->fifosize = 16;
+ port->type = PORT_IFXMIPSASC,
+ port->line = pdev->id;
+ port->dev = &pdev->dev;
+
+ port->irq = tx_irq; /* unused, just to be backward-compatibe */
+ port->mapbase = mmres->start;
+
+ ifx_port->clk = clk;
+
+ ifx_port->tx_irq = tx_irq;
+ ifx_port->rx_irq = rx_irq;
+ ifx_port->err_irq = err_irq;
+
+ lqasc_port[pdev->id] = ifx_port;
+ platform_set_drvdata(pdev, ifx_port);
+
+ ret = uart_add_one_port(&lqasc_reg, port);
+
+ return ret;
+}
+
+static int __devexit
+lqasc_remove(struct platform_device *pdev)
+{
+ struct lq_uart_port *ifx_port = platform_get_drvdata(pdev);
+ int ret;
+
+ clk_put(ifx_port->clk);
+ platform_set_drvdata(pdev, NULL);
+ lqasc_port[pdev->id] = NULL;
+ ret = uart_remove_one_port(&lqasc_reg, &ifx_port->port);
+ kfree(ifx_port);
+
+ return 0;
+}
+
+static struct platform_driver lqasc_driver = {
+ .probe = lqasc_probe,
+ .remove = __devexit_p(lqasc_remove),
+
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init
+init_lqasc(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&lqasc_reg);
+ if (ret != 0)
+ return ret;
+
+ ret = platform_driver_register(&lqasc_driver);
+ if (ret != 0)
+ uart_unregister_driver(&lqasc_reg);
+
+ return ret;
+}
+
+void __exit
+exit_lqasc(void)
+{
+ platform_driver_unregister(&lqasc_driver);
+ uart_unregister_driver(&lqasc_reg);
+}
+
+module_init(init_lqasc);
+module_exit(exit_lqasc);
+
+MODULE_DESCRIPTION("Lantiq serial port driver");
+MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,245 @@
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -251,6 +251,12 @@
help
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
+config MTD_LANTIQ
+ bool "Lantiq SoC NOR support"
+ depends on LANTIQ && MTD_PARTITIONS
+ help
+ Support for NOR flsh chips on Lantiq SoC
+
config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -59,3 +59,4 @@
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
+obj-$(CONFIG_MTD_LANTIQ) += lantiq.o
--- /dev/null
+++ b/drivers/mtd/maps/lantiq.c
@@ -0,0 +1,169 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2004 Liu Peng Infineon IFAP DC COM CPE
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/cfi.h>
+#include <linux/magic.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+
+#include <lantiq.h>
+#include <lantiq_platform.h>
+
+static map_word
+lq_read16(struct map_info *map, unsigned long adr)
+{
+ unsigned long flags;
+ map_word temp;
+ spin_lock_irqsave(&ebu_lock, flags);
+ adr ^= 2;
+ temp.x[0] = *((__u16 *)(map->virt + adr));
+ spin_unlock_irqrestore(&ebu_lock, flags);
+ return temp;
+}
+
+static void
+lq_write16(struct map_info *map, map_word d, unsigned long adr)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ebu_lock, flags);
+ adr ^= 2;
+ *((__u16 *)(map->virt + adr)) = d.x[0];
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+void
+lq_copy_from(struct map_info *map, void *to,
+ unsigned long from, ssize_t len)
+{
+ unsigned char *p;
+ unsigned char *to_8;
+ unsigned long flags;
+ spin_lock_irqsave(&ebu_lock, flags);
+ from = (unsigned long)(from + map->virt);
+ p = (unsigned char *) from;
+ to_8 = (unsigned char *) to;
+ while (len--)
+ *to_8++ = *p++;
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+void
+lq_copy_to(struct map_info *map, unsigned long to,
+ const void *from, ssize_t len)
+{
+ unsigned char *p = (unsigned char *)from;
+ unsigned char *to_8;
+ unsigned long flags;
+ spin_lock_irqsave(&ebu_lock, flags);
+ to += (unsigned long) map->virt;
+ to_8 = (unsigned char *)to;
+ while (len--)
+ *p++ = *to_8++;
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+static const char *part_probe_types[] = { "cmdlinepart", NULL };
+
+static struct map_info lq_map = {
+ .name = "lq_nor",
+ .bankwidth = 2,
+ .read = lq_read16,
+ .write = lq_write16,
+ .copy_from = lq_copy_from,
+ .copy_to = lq_copy_to,
+};
+
+static int
+lq_mtd_probe(struct platform_device *pdev)
+{
+ struct physmap_flash_data *lq_mtd_data =
+ (struct physmap_flash_data*) dev_get_platdata(&pdev->dev);
+ struct mtd_info *lq_mtd = NULL;
+ struct mtd_partition *parts = NULL;
+ struct resource *res = 0;
+ int nr_parts = 0;
+
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+ lq_w32(lq_r32(LQ_EBU_BUSCON0) & ~EBU_WRDIS, LQ_EBU_BUSCON0);
+#endif
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if(!res)
+ {
+ dev_err(&pdev->dev, "failed to get memory resource");
+ return -ENOENT;
+ }
+ res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if(!res)
+ {
+ dev_err(&pdev->dev, "failed to request mem resource");
+ return -EBUSY;
+ }
+
+ lq_map.phys = res->start;
+ lq_map.size = resource_size(res);
+ lq_map.virt = ioremap_nocache(lq_map.phys, lq_map.size);
+
+ if (!lq_map.virt ) {
+ dev_err(&pdev->dev, "failed to ioremap!\n");
+ return -EIO;
+ }
+
+ lq_mtd = (struct mtd_info *) do_map_probe("cfi_probe", &lq_map);
+ if (!lq_mtd) {
+ iounmap(lq_map.virt);
+ dev_err(&pdev->dev, "probing failed\n");
+ return -ENXIO;
+ }
+
+ lq_mtd->owner = THIS_MODULE;
+
+ nr_parts = parse_mtd_partitions(lq_mtd, part_probe_types, &parts, 0);
+ if (nr_parts > 0) {
+ dev_info(&pdev->dev, "using %d partitions from cmdline", nr_parts);
+ } else {
+ nr_parts = lq_mtd_data->nr_parts;
+ parts = lq_mtd_data->parts;
+ }
+
+ add_mtd_partitions(lq_mtd, parts, nr_parts);
+ return 0;
+}
+
+static struct platform_driver lq_mtd_driver = {
+ .probe = lq_mtd_probe,
+ .driver = {
+ .name = "lq_nor",
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init
+init_lq_mtd(void)
+{
+ int ret = platform_driver_register(&lq_mtd_driver);
+ if (ret)
+ printk(KERN_INFO "lq_nor: error registering platfom driver");
+ return ret;
+}
+
+module_init(init_lq_mtd);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Lantiq SoC NOR");
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -41,7 +41,11 @@
/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */
// debugging, turns off buffer write mode if set to 1
-#define FORCE_WORD_WRITE 0
+#ifdef CONFIG_LANTIQ
+# define FORCE_WORD_WRITE 1
+#else
+# define FORCE_WORD_WRITE 0
+#endif
/* Intel chips */
#define I82802AB 0x00ad
@@ -1491,6 +1495,9 @@
int ret=0;
adr += chip->start;
+#ifdef CONFIG_LANTIQ
+ adr ^= 2;
+#endif
switch (mode) {
case FL_WRITING:
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -40,7 +40,11 @@
#include <linux/mtd/xip.h>
#define AMD_BOOTLOC_BUG
-#define FORCE_WORD_WRITE 0
+#ifdef CONFIG_LANTIQ
+# define FORCE_WORD_WRITE 1
+#else
+# define FORCE_WORD_WRITE 0
+#endif
#define MAX_WORD_RETRIES 3
@@ -1156,6 +1160,10 @@
adr += chip->start;
+#ifdef CONFIG_LANTIQ
+ adr ^= 2;
+#endif
+
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) {

View File

@@ -0,0 +1,99 @@
--- a/drivers/mtd/maps/lantiq.c
+++ b/drivers/mtd/maps/lantiq.c
@@ -24,6 +24,10 @@
#include <lantiq.h>
#include <lantiq_platform.h>
+#ifdef CONFIG_SOC_LANTIQ_XWAY
+#include <xway.h>
+#endif
+
static map_word
lq_read16(struct map_info *map, unsigned long adr)
{
@@ -77,6 +81,75 @@ lq_copy_to(struct map_info *map, unsigne
spin_unlock_irqrestore(&ebu_lock, flags);
}
+static unsigned long
+find_uImage_size(struct map_info *map, unsigned long offset)
+{
+#define UBOOT_MAGIC 0x56190527
+ unsigned long magic;
+ unsigned long temp;
+ map->copy_from(map, &magic, offset, 4);
+ if (le32_to_cpu(magic) != UBOOT_MAGIC)
+ return 0;
+ map->copy_from(map, &temp, offset + 12, 4);
+ return temp + 0x40;
+}
+
+static int
+detect_squashfs_partition(struct map_info *map, unsigned long offset)
+{
+ unsigned long temp;
+ map->copy_from(map, &temp, offset, 4);
+ return le32_to_cpu(temp) == SQUASHFS_MAGIC;
+}
+
+static struct mtd_partition split_partitions[] = {
+ {
+ .name = "kernel",
+ .offset = 0x0,
+ .size = 0x0,
+ }, {
+ .name = "rootfs",
+ .offset = 0x0,
+ .size = 0x0,
+ },
+};
+
+static int
+mtd_split_linux(struct map_info *map, struct mtd_info *mtd,
+ struct mtd_partition *parts, int nr_parts)
+{
+ int base_part = 0;
+ int i;
+ for (i = 0; i < nr_parts && !base_part; i++) {
+ if(!strcmp("linux", parts[i].name))
+ base_part = i;
+ }
+ if (!base_part)
+ return 0;
+ split_partitions[0].size = find_uImage_size(map, parts[base_part].offset);
+ if (!split_partitions[0].size) {
+ printk(KERN_INFO "lq_nor: no uImage found in linux partition");
+ return -1;
+ }
+ if (!detect_squashfs_partition(map,
+ parts[base_part].offset + split_partitions[0].size)) {
+ split_partitions[0].size &= ~(mtd->erasesize - 1);
+ split_partitions[0].size += mtd->erasesize;
+ }
+ split_partitions[0].offset = parts[base_part].offset;
+ split_partitions[1].offset =
+ parts[base_part].offset + split_partitions[0].size;
+ split_partitions[1].size =
+ parts[base_part].size - split_partitions[0].size;
+
+ base_part++;
+ add_mtd_partitions(mtd, parts, base_part);
+ add_mtd_partitions(mtd, split_partitions, 2);
+ if(nr_parts != base_part)
+ add_mtd_partitions(mtd, &parts[base_part], nr_parts - base_part);
+ return nr_parts + 2;
+}
+
static const char *part_probe_types[] = { "cmdlinepart", NULL };
static struct map_info lq_map = {
@@ -142,7 +215,8 @@ lq_mtd_probe(struct platform_device *pde
parts = lq_mtd_data->parts;
}
- add_mtd_partitions(lq_mtd, parts, nr_parts);
+ if (!mtd_split_linux(&lq_map, lq_mtd, parts, nr_parts))
+ add_mtd_partitions(lq_mtd, parts, nr_parts);
return 0;
}

View File

@@ -0,0 +1,580 @@
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -343,6 +343,12 @@ config MACB
source "drivers/net/arm/Kconfig"
+config LANTIQ_ETOP
+ tristate "Lantiq SoC ETOP driver"
+ depends on SOC_LANTIQ_XWAY
+ help
+ Support for the MII0 inside the Lantiq SoC
+
config AX88796
tristate "ASIX AX88796 NE2000 clone support"
depends on ARM || MIPS || SUPERH
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -204,6 +204,7 @@ obj-$(CONFIG_SNI_82596) += sni_82596.o
obj-$(CONFIG_MVME16x_NET) += 82596.o
obj-$(CONFIG_BVME6000_NET) += 82596.o
obj-$(CONFIG_SC92031) += sc92031.o
+obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
# This is also a 82596 and should probably be merged
obj-$(CONFIG_LP486E) += lp486e.o
--- /dev/null
+++ b/drivers/net/lantiq_etop.c
@@ -0,0 +1,552 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2005 Wu Qi Ming <Qi-Ming.Wu@infineon.com>
+ * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/phy.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/checksum.h>
+
+#include <xway.h>
+#include <xway_dma.h>
+#include <lantiq_platform.h>
+
+#define ETHERNET_PACKET_DMA_BUFFER_SIZE 0x600
+#define LQ_PPE32_MEM_MAP ((u32 *)(LQ_PPE32_BASE_ADDR + 0x10000))
+#define LQ_PPE32_SRST ((u32 *)(LQ_PPE32_BASE_ADDR + 0x10080))
+
+/* mdio access */
+#define LQ_PPE32_MDIO_CFG ((u32 *)(LQ_PPE32_BASE_ADDR + 0x11800))
+#define LQ_PPE32_MDIO_ACC ((u32 *)(LQ_PPE32_BASE_ADDR + 0x11804))
+
+#define MDIO_ACC_REQUEST 0x80000000
+#define MDIO_ACC_READ 0x40000000
+#define MDIO_ACC_ADDR_MASK 0x1f
+#define MDIO_ACC_ADDR_OFFSET 0x15
+#define MDIO_ACC_REG_MASK 0x1f
+#define MDIO_ACC_REG_OFFSET 0x10
+#define MDIO_ACC_VAL_MASK 0xffff
+
+/* configuration */
+#define LQ_PPE32_CFG ((u32 *)(LQ_PPE32_MEM_MAP + 0x1808))
+
+#define PPE32_MII_MASK 0xfffffffc
+#define PPE32_MII_NORMAL 0x8
+#define PPE32_MII_REVERSE 0xe
+
+/* packet length */
+#define LQ_PPE32_IG_PLEN_CTRL ((u32 *)(LQ_PPE32_MEM_MAP + 0x1820))
+
+#define PPE32_PLEN_OVER 0x5ee
+#define PPE32_PLEN_UNDER 0x400000
+
+/* enet */
+#define LQ_PPE32_ENET_MAC_CFG ((u32 *)(LQ_PPE32_MEM_MAP + 0x1840))
+
+#define PPE32_CGEN 0x800
+
+struct lq_mii_priv {
+ struct net_device_stats stats;
+ struct dma_device_info *dma_device;
+ struct sk_buff *skb;
+
+ struct mii_bus *mii_bus;
+ struct phy_device *phydev;
+ int oldlink, oldspeed, oldduplex;
+};
+
+static struct net_device *lq_etop_dev;
+static unsigned char mac_addr[MAX_ADDR_LEN];
+
+static int lq_mdiobus_write(struct mii_bus *bus, int phy_addr,
+ int phy_reg, u16 phy_data)
+{
+ u32 val = MDIO_ACC_REQUEST |
+ ((phy_addr & MDIO_ACC_ADDR_MASK) << MDIO_ACC_ADDR_OFFSET) |
+ ((phy_reg & MDIO_ACC_REG_MASK) << MDIO_ACC_REG_OFFSET) |
+ phy_data;
+
+ while (lq_r32(LQ_PPE32_MDIO_ACC) & MDIO_ACC_REQUEST)
+ ;
+ lq_w32(val, LQ_PPE32_MDIO_ACC);
+
+ return 0;
+}
+
+static int lq_mdiobus_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ u32 val = MDIO_ACC_REQUEST | MDIO_ACC_READ |
+ ((phy_addr & MDIO_ACC_ADDR_MASK) << MDIO_ACC_ADDR_OFFSET) |
+ ((phy_reg & MDIO_ACC_REG_MASK) << MDIO_ACC_REG_OFFSET);
+
+ while (lq_r32(LQ_PPE32_MDIO_ACC) & MDIO_ACC_REQUEST)
+ ;
+ lq_w32(val, LQ_PPE32_MDIO_ACC);
+ while (lq_r32(LQ_PPE32_MDIO_ACC) & MDIO_ACC_REQUEST)
+ ;
+ val = lq_r32(LQ_PPE32_MDIO_ACC) & MDIO_ACC_VAL_MASK;
+ return val;
+}
+
+int lq_mii_open(struct net_device *dev)
+{
+ struct lq_mii_priv *priv = (struct lq_mii_priv *)netdev_priv(dev);
+ struct dma_device_info *dma_dev = priv->dma_device;
+ int i;
+
+ for (i = 0; i < dma_dev->max_rx_chan_num; i++) {
+ if ((dma_dev->rx_chan[i])->control == LQ_DMA_CH_ON)
+ (dma_dev->rx_chan[i])->open(dma_dev->rx_chan[i]);
+ }
+ netif_start_queue(dev);
+ return 0;
+}
+
+int lq_mii_release(struct net_device *dev)
+{
+ struct lq_mii_priv *priv = (struct lq_mii_priv *)netdev_priv(dev);
+ struct dma_device_info *dma_dev = priv->dma_device;
+ int i;
+
+ for (i = 0; i < dma_dev->max_rx_chan_num; i++)
+ dma_dev->rx_chan[i]->close(dma_dev->rx_chan[i]);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+int lq_mii_hw_receive(struct net_device *dev, struct dma_device_info *dma_dev)
+{
+ struct lq_mii_priv *priv = (struct lq_mii_priv *)netdev_priv(dev);
+ unsigned char *buf = NULL;
+ struct sk_buff *skb = NULL;
+ int len = 0;
+
+ len = dma_device_read(dma_dev, &buf, (void **)&skb);
+
+ if (len >= ETHERNET_PACKET_DMA_BUFFER_SIZE) {
+ printk(KERN_INFO "lq_etop: packet too large %d\n", len);
+ goto lq_mii_hw_receive_err_exit;
+ }
+
+ /* remove CRC */
+ len -= 4;
+ if (skb == NULL) {
+ printk(KERN_INFO "lq_etop: cannot restore pointer\n");
+ goto lq_mii_hw_receive_err_exit;
+ }
+
+ if (len > (skb->end - skb->tail)) {
+ printk(KERN_INFO "lq_etop: BUG, len:%d end:%p tail:%p\n",
+ (len+4), skb->end, skb->tail);
+ goto lq_mii_hw_receive_err_exit;
+ }
+
+ skb_put(skb, len);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ return 0;
+
+lq_mii_hw_receive_err_exit:
+ if (len == 0) {
+ if (skb)
+ dev_kfree_skb_any(skb);
+ priv->stats.rx_errors++;
+ priv->stats.rx_dropped++;
+ return -EIO;
+ } else {
+ return len;
+ }
+}
+
+int lq_mii_hw_tx(char *buf, int len, struct net_device *dev)
+{
+ int ret = 0;
+ struct lq_mii_priv *priv = netdev_priv(dev);
+ struct dma_device_info *dma_dev = priv->dma_device;
+ ret = dma_device_write(dma_dev, buf, len, priv->skb);
+ return ret;
+}
+
+int lq_mii_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int len;
+ char *data;
+ struct lq_mii_priv *priv = netdev_priv(dev);
+ struct dma_device_info *dma_dev = priv->dma_device;
+
+ len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
+ data = skb->data;
+ priv->skb = skb;
+ dev->trans_start = jiffies;
+ /* TODO: we got more than 1 dma channel,
+ so we should do something intelligent here to select one */
+ dma_dev->current_tx_chan = 0;
+
+ wmb();
+
+ if (lq_mii_hw_tx(data, len, dev) != len) {
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_errors++;
+ priv->stats.tx_dropped++;
+ } else {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += len;
+ }
+
+ return 0;
+}
+
+void lq_mii_tx_timeout(struct net_device *dev)
+{
+ int i;
+ struct lq_mii_priv *priv = (struct lq_mii_priv *)netdev_priv(dev);
+
+ priv->stats.tx_errors++;
+ for (i = 0; i < priv->dma_device->max_tx_chan_num; i++)
+ priv->dma_device->tx_chan[i]->disable_irq(priv->dma_device->tx_chan[i]);
+ netif_wake_queue(dev);
+ return;
+}
+
+int dma_intr_handler(struct dma_device_info *dma_dev, int status)
+{
+ int i;
+
+ switch (status) {
+ case RCV_INT:
+ lq_mii_hw_receive(lq_etop_dev, dma_dev);
+ break;
+
+ case TX_BUF_FULL_INT:
+ printk(KERN_INFO "lq_etop: tx buffer full\n");
+ netif_stop_queue(lq_etop_dev);
+ for (i = 0; i < dma_dev->max_tx_chan_num; i++) {
+ if ((dma_dev->tx_chan[i])->control == LQ_DMA_CH_ON)
+ dma_dev->tx_chan[i]->enable_irq(dma_dev->tx_chan[i]);
+ }
+ break;
+
+ case TRANSMIT_CPT_INT:
+ for (i = 0; i < dma_dev->max_tx_chan_num; i++)
+ dma_dev->tx_chan[i]->disable_irq(dma_dev->tx_chan[i]);
+
+ netif_wake_queue(lq_etop_dev);
+ break;
+ }
+
+ return 0;
+}
+
+unsigned char *lq_etop_dma_buffer_alloc(int len, int *byte_offset, void **opt)
+{
+ unsigned char *buffer = NULL;
+ struct sk_buff *skb = NULL;
+
+ skb = dev_alloc_skb(ETHERNET_PACKET_DMA_BUFFER_SIZE);
+ if (skb == NULL)
+ return NULL;
+
+ buffer = (unsigned char *)(skb->data);
+ skb_reserve(skb, 2);
+ *(int *)opt = (int)skb;
+ *byte_offset = 2;
+
+ return buffer;
+}
+
+void lq_etop_dma_buffer_free(unsigned char *dataptr, void *opt)
+{
+ struct sk_buff *skb = NULL;
+
+ if (opt == NULL) {
+ kfree(dataptr);
+ } else {
+ skb = (struct sk_buff *)opt;
+ dev_kfree_skb_any(skb);
+ }
+}
+
+static void
+lq_adjust_link(struct net_device *dev)
+{
+ struct lq_mii_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ int new_state = 0;
+
+ /* Did anything change? */
+ if (priv->oldlink != phydev->link ||
+ priv->oldduplex != phydev->duplex ||
+ priv->oldspeed != phydev->speed) {
+ /* Yes, so update status and mark as changed */
+ new_state = 1;
+ priv->oldduplex = phydev->duplex;
+ priv->oldspeed = phydev->speed;
+ priv->oldlink = phydev->link;
+ }
+
+ /* If link status changed, show new status */
+ if (new_state)
+ phy_print_status(phydev);
+}
+
+static int mii_probe(struct net_device *dev)
+{
+ struct lq_mii_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ int phy_addr;
+
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+
+ /* find the first (lowest address) PHY on the current MAC's MII bus */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ if (priv->mii_bus->phy_map[phy_addr]) {
+ phydev = priv->mii_bus->phy_map[phy_addr];
+ break; /* break out with first one found */
+ }
+ }
+
+ if (!phydev) {
+ printk (KERN_ERR "%s: no PHY found\n", dev->name);
+ return -ENODEV;
+ }
+
+ /* now we are supposed to have a proper phydev, to attach to... */
+ BUG_ON(!phydev);
+ BUG_ON(phydev->attached_dev);
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev), &lq_adjust_link,
+ 0, PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(phydev)) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(phydev);
+ }
+
+ /* mask with MAC supported features */
+ phydev->supported &= (SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg
+ /* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */
+ | SUPPORTED_MII
+ | SUPPORTED_TP);
+
+ phydev->advertising = phydev->supported;
+
+ priv->phydev = phydev;
+
+ printk(KERN_INFO "%s: attached PHY driver [%s] "
+ "(mii_bus:phy_addr=%s, irq=%d)\n",
+ dev->name, phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+ return 0;
+}
+
+
+static int lq_mii_dev_init(struct net_device *dev)
+{
+ int i;
+ struct lq_mii_priv *priv = (struct lq_mii_priv *)netdev_priv(dev);
+ ether_setup(dev);
+ dev->watchdog_timeo = 10 * HZ;
+ dev->mtu = 1500;
+ memset(priv, 0, sizeof(struct lq_mii_priv));
+ priv->dma_device = dma_device_reserve("PPE");
+ if (!priv->dma_device) {
+ BUG();
+ return -ENODEV;
+ }
+ priv->dma_device->buffer_alloc = &lq_etop_dma_buffer_alloc;
+ priv->dma_device->buffer_free = &lq_etop_dma_buffer_free;
+ priv->dma_device->intr_handler = &dma_intr_handler;
+ priv->dma_device->max_rx_chan_num = 4;
+
+ for (i = 0; i < priv->dma_device->max_rx_chan_num; i++) {
+ priv->dma_device->rx_chan[i]->packet_size = ETHERNET_PACKET_DMA_BUFFER_SIZE;
+ priv->dma_device->rx_chan[i]->control = LQ_DMA_CH_ON;
+ }
+
+ for (i = 0; i < priv->dma_device->max_tx_chan_num; i++)
+ if (i == 0)
+ priv->dma_device->tx_chan[i]->control = LQ_DMA_CH_ON;
+ else
+ priv->dma_device->tx_chan[i]->control = LQ_DMA_CH_OFF;
+
+ dma_device_register(priv->dma_device);
+
+ printk(KERN_INFO "%s: using mac=", dev->name);
+ for (i = 0; i < 6; i++) {
+ dev->dev_addr[i] = mac_addr[i];
+ printk("%02X%c", dev->dev_addr[i], (i == 5) ? ('\n') : (':'));
+ }
+
+ priv->mii_bus = mdiobus_alloc();
+ if (priv->mii_bus == NULL)
+ return -ENOMEM;
+
+ priv->mii_bus->priv = dev;
+ priv->mii_bus->read = lq_mdiobus_read;
+ priv->mii_bus->write = lq_mdiobus_write;
+ priv->mii_bus->name = "lq_mii";
+ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0);
+ priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ for(i = 0; i < PHY_MAX_ADDR; ++i)
+ priv->mii_bus->irq[i] = PHY_POLL;
+
+ mdiobus_register(priv->mii_bus);
+
+ return mii_probe(dev);
+}
+
+static void lq_mii_chip_init(int mode)
+{
+ lq_pmu_enable(PMU_DMA);
+ lq_pmu_enable(PMU_PPE);
+
+ if (mode == REV_MII_MODE)
+ lq_w32_mask(PPE32_MII_MASK, PPE32_MII_REVERSE, LQ_PPE32_CFG);
+ else if (mode == MII_MODE)
+ lq_w32_mask(PPE32_MII_MASK, PPE32_MII_NORMAL, LQ_PPE32_CFG);
+ lq_w32(PPE32_PLEN_UNDER | PPE32_PLEN_OVER, LQ_PPE32_IG_PLEN_CTRL);
+ lq_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG);
+ wmb();
+}
+
+static int lq_mii_eth_mac_addr(struct net_device *dev, void *p)
+{
+ int retcode;
+
+ retcode = eth_mac_addr(dev, p);
+
+ if (retcode)
+ return retcode;
+
+ // set rx_addr for unicast filter
+ lq_w32(((dev->dev_addr[0]<<24)|(dev->dev_addr[1]<<16)|(dev->dev_addr[2]<< 8)|dev->dev_addr[3]), (u32*)(LQ_PPE32_BASE_ADDR|(0x461b<<2)));
+ lq_w32(((dev->dev_addr[4]<<24)|(dev->dev_addr[5]<<16)), (u32*)(LQ_PPE32_BASE_ADDR|(0x461c<<2)));
+
+ return 0;
+}
+
+static void lq_mii_set_rx_mode (struct net_device *dev)
+{
+ // rx_mode promisc: unset unicast filter
+ if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI))
+ lq_w32(lq_r32((u32*)(LQ_PPE32_BASE_ADDR|(0x4614<<2))) & ~(1<<28), (u32*)(LQ_PPE32_BASE_ADDR|(0x4614<<2)));
+ // enable unicast filter
+ else
+ lq_w32(lq_r32((u32*)(LQ_PPE32_BASE_ADDR|(0x4614<<2))) | (1<<28), (u32*)(LQ_PPE32_BASE_ADDR|(0x4614<<2)));
+}
+
+static const struct net_device_ops lq_eth_netdev_ops = {
+ .ndo_init = lq_mii_dev_init,
+ .ndo_open = lq_mii_open,
+ .ndo_stop = lq_mii_release,
+ .ndo_start_xmit = lq_mii_tx,
+ .ndo_tx_timeout = lq_mii_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = lq_mii_eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = lq_mii_set_rx_mode,
+};
+
+static int
+lq_mii_probe(struct platform_device *dev)
+{
+ int result = 0;
+ struct lq_eth_data *eth = (struct lq_eth_data*)dev->dev.platform_data;
+ lq_etop_dev = alloc_etherdev(sizeof(struct lq_mii_priv));
+ lq_etop_dev->netdev_ops = &lq_eth_netdev_ops;
+ memcpy(mac_addr, eth->mac, 6);
+ strcpy(lq_etop_dev->name, "eth%d");
+ lq_mii_chip_init(eth->mii_mode);
+ result = register_netdev(lq_etop_dev);
+ if (result) {
+ printk(KERN_INFO "lq_etop: error %i registering device \"%s\"\n", result, lq_etop_dev->name);
+ goto out;
+ }
+
+ printk(KERN_INFO "lq_etop: driver loaded!\n");
+
+out:
+ return result;
+}
+
+static int lq_mii_remove(struct platform_device *dev)
+{
+ struct lq_mii_priv *priv = (struct lq_mii_priv *)netdev_priv(lq_etop_dev);
+
+ printk(KERN_INFO "lq_etop: lq_etop cleanup\n");
+
+ dma_device_unregister(priv->dma_device);
+ dma_device_release(priv->dma_device);
+ kfree(priv->dma_device);
+ unregister_netdev(lq_etop_dev);
+ return 0;
+}
+
+static struct platform_driver lq_mii_driver = {
+ .probe = lq_mii_probe,
+ .remove = lq_mii_remove,
+ .driver = {
+ .name = "lq_etop",
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init lq_mii_init(void)
+{
+ int ret = platform_driver_register(&lq_mii_driver);
+ if (ret)
+ printk(KERN_INFO "lq_etop: Error registering platfom driver!");
+ return ret;
+}
+
+static void __exit lq_mii_cleanup(void)
+{
+ platform_driver_unregister(&lq_mii_driver);
+}
+
+module_init(lq_mii_init);
+module_exit(lq_mii_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("ethernet driver for IFXMIPS boards");

View File

@@ -0,0 +1,246 @@
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -840,6 +840,12 @@ config TXX9_WDT
help
Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs.
+config LANTIQ_WDT
+ bool "Lantiq SoC watchdog"
+ depends on LANTIQ
+ help
+ Hardware driver for the Lantiq SoC Watchdog Timer.
+
# PARISC Architecture
# POWERPC Architecture
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
+obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
# PARISC Architecture
--- /dev/null
+++ b/drivers/watchdog/lantiq_wdt.c
@@ -0,0 +1,218 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ * Based on EP93xx wdt driver
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+
+#include <lantiq.h>
+
+#define LQ_WDT_PW1 0x00BE0000
+#define LQ_WDT_PW2 0x00DC0000
+
+#define LQ_BIU_WDT_CR 0x3F0
+#define LQ_BIU_WDT_SR 0x3F8
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+static int wdt_ok_to_close;
+#endif
+
+static int wdt_timeout = 30;
+static __iomem void *wdt_membase = NULL;
+static unsigned long io_region_clk = 0;
+
+static int
+lq_wdt_enable(unsigned int timeout)
+{
+/* printk("%s:%s[%d] %08X\n",
+ __FILE__, __func__, __LINE__,
+ lq_r32(wdt_membase + LQ_BIU_WDT_SR));
+ if(!lq_r32(wdt_membase + LQ_BIU_WDT_SR))
+ {
+*/ lq_w32(LQ_WDT_PW1, wdt_membase + LQ_BIU_WDT_CR);
+ lq_w32(LQ_WDT_PW2 |
+ (0x3 << 26) | /* PWL */
+ (0x3 << 24) | /* CLKDIV */
+ (0x1 << 31) | /* enable */
+ ((timeout * (io_region_clk / 0x40000)) + 0x1000), /* reload */
+ wdt_membase + LQ_BIU_WDT_CR);
+// }
+ return 0;
+}
+
+static void
+lq_wdt_disable(void)
+{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ wdt_ok_to_close = 0;
+#endif
+ lq_w32(LQ_WDT_PW1, wdt_membase + LQ_BIU_WDT_CR);
+ lq_w32(LQ_WDT_PW2, wdt_membase+ LQ_BIU_WDT_CR);
+}
+
+static ssize_t
+lq_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ size_t i;
+
+ if (!len)
+ return 0;
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ wdt_ok_to_close = 1;
+ }
+#endif
+ lq_wdt_enable(wdt_timeout);
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE,
+ .identity = "lq_wdt",
+};
+
+static int
+lq_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(wdt_timeout, (int __user *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(wdt_timeout, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ lq_wdt_enable(wdt_timeout);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int
+lq_wdt_open(struct inode *inode, struct file *file)
+{
+ lq_wdt_enable(wdt_timeout);
+ return nonseekable_open(inode, file);
+}
+
+static int
+lq_wdt_release(struct inode *inode, struct file *file)
+{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ if (wdt_ok_to_close)
+ lq_wdt_disable();
+ else
+#endif
+ printk(KERN_ERR "lq_wdt: watchdog closed without warning,"
+ " rebooting system\n");
+ return 0;
+}
+
+static const struct file_operations lq_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = lq_wdt_write,
+ .ioctl = lq_wdt_ioctl,
+ .open = lq_wdt_open,
+ .release = lq_wdt_release,
+};
+
+static struct miscdevice lq_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &lq_wdt_fops,
+};
+
+static int
+lq_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct clk *clk;
+ int ret = 0;
+ if(!res)
+ return -ENOENT;
+ res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if(!res)
+ return -EBUSY;
+ wdt_membase = ioremap_nocache(res->start, resource_size(res));
+ if(!wdt_membase)
+ {
+ ret = -ENOMEM;
+ goto err_release_mem_region;
+ }
+ clk = clk_get(&pdev->dev, "io");
+ io_region_clk = clk_get_rate(clk);;
+ ret = misc_register(&lq_wdt_miscdev);
+ if(!ret)
+ return 0;
+
+ iounmap(wdt_membase);
+err_release_mem_region:
+ release_mem_region(res->start, resource_size(res));
+ return ret;
+}
+
+static int
+lq_wdt_remove(struct platform_device *dev)
+{
+ lq_wdt_disable();
+ misc_deregister(&lq_wdt_miscdev);
+ return 0;
+}
+
+static struct platform_driver lq_wdt_driver = {
+ .probe = lq_wdt_probe,
+ .remove = lq_wdt_remove,
+ .driver = {
+ .name = "lq_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+init_lq_wdt(void)
+{
+ return platform_driver_register(&lq_wdt_driver);
+}
+
+static void __exit
+exit_lq_wdt(void)
+{
+ platform_driver_unregister(&lq_wdt_driver);
+}
+
+module_init(init_lq_wdt);
+module_exit(exit_lq_wdt);
+
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("ifxmips Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

View File

@@ -0,0 +1,436 @@
--- a/arch/mips/pci/Makefile
+++ b/arch/mips/pci/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capc
obj-$(CONFIG_WR_PPMC) += fixup-wrppmc.o
obj-$(CONFIG_MIKROTIK_RB532) += pci-rc32434.o ops-rc32434.o fixup-rc32434.o
obj-$(CONFIG_CPU_CAVIUM_OCTEON) += pci-octeon.o pcie-octeon.o
+obj-$(CONFIG_LANTIQ) += pci-lantiq.o ops-lantiq.o
ifdef CONFIG_PCI_MSI
obj-$(CONFIG_CPU_CAVIUM_OCTEON) += msi-octeon.o
--- /dev/null
+++ b/arch/mips/pci/ops-lantiq.c
@@ -0,0 +1,127 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <asm/addrspace.h>
+#include <linux/vmalloc.h>
+
+#include <xway.h>
+
+#define LQ_PCI_CFG_BUSNUM_SHF 16
+#define LQ_PCI_CFG_DEVNUM_SHF 11
+#define LQ_PCI_CFG_FUNNUM_SHF 8
+
+#define PCI_ACCESS_READ 0
+#define PCI_ACCESS_WRITE 1
+
+extern u32 lq_pci_mapped_cfg;
+
+static int
+lq_pci_config_access(unsigned char access_type,
+ struct pci_bus *bus, unsigned int devfn, unsigned int where, u32 *data)
+{
+ unsigned long cfg_base;
+ unsigned long flags;
+
+ u32 temp;
+
+ /* we support slot from 0 to 15 */
+ /* dev_fn 0&0x68 (AD29) is ifxmips itself */
+ if ((bus->number != 0) || ((devfn & 0xf8) > 0x78)
+ || ((devfn & 0xf8) == 0) || ((devfn & 0xf8) == 0x68))
+ return 1;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+
+ cfg_base = lq_pci_mapped_cfg;
+ cfg_base |= (bus->number << LQ_PCI_CFG_BUSNUM_SHF) | (devfn <<
+ LQ_PCI_CFG_FUNNUM_SHF) | (where & ~0x3);
+
+ /* Perform access */
+ if (access_type == PCI_ACCESS_WRITE)
+ {
+#ifdef CONFIG_SWAP_IO_SPACE
+ lq_w32(swab32(*data), ((u32*)cfg_base));
+#else
+ lq_w32(*data, ((u32*)cfg_base));
+#endif
+ } else {
+ *data = lq_r32(((u32*)(cfg_base)));
+#ifdef CONFIG_SWAP_IO_SPACE
+ *data = swab32(*data);
+#endif
+ }
+ wmb();
+
+ /* clean possible Master abort */
+ cfg_base = (lq_pci_mapped_cfg | (0x0 << LQ_PCI_CFG_FUNNUM_SHF)) + 4;
+ temp = lq_r32(((u32*)(cfg_base)));
+#ifdef CONFIG_SWAP_IO_SPACE
+ temp = swab32 (temp);
+#endif
+ cfg_base = (lq_pci_mapped_cfg | (0x68 << LQ_PCI_CFG_FUNNUM_SHF)) + 4;
+ lq_w32(temp, ((u32*)cfg_base));
+
+ spin_unlock_irqrestore(&ebu_lock, flags);
+
+ if (((*data) == 0xffffffff) && (access_type == PCI_ACCESS_READ))
+ return 1;
+
+ return 0;
+}
+
+int
+lq_pci_read_config_dword(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 * val)
+{
+ u32 data = 0;
+
+ if (lq_pci_config_access(PCI_ACCESS_READ, bus, devfn, where, &data))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (size == 1)
+ *val = (data >> ((where & 3) << 3)) & 0xff;
+ else if (size == 2)
+ *val = (data >> ((where & 3) << 3)) & 0xffff;
+ else
+ *val = data;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int
+lq_pci_write_config_dword(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ u32 data = 0;
+
+ if (size == 4)
+ {
+ data = val;
+ } else {
+ if (lq_pci_config_access(PCI_ACCESS_READ, bus, devfn, where, &data))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (size == 1)
+ data = (data & ~(0xff << ((where & 3) << 3))) |
+ (val << ((where & 3) << 3));
+ else if (size == 2)
+ data = (data & ~(0xffff << ((where & 3) << 3))) |
+ (val << ((where & 3) << 3));
+ }
+
+ if (lq_pci_config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return PCIBIOS_SUCCESSFUL;
+}
--- /dev/null
+++ b/arch/mips/pci/pci-lantiq.c
@@ -0,0 +1,293 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_device.h>
+
+#include <asm/gpio.h>
+#include <asm/addrspace.h>
+
+#include <xway.h>
+#include <xway_irq.h>
+#include <lantiq_platform.h>
+
+#define LQ_PCI_CFG_BASE 0x17000000
+#define LQ_PCI_CFG_SIZE 0x00008000
+#define LQ_PCI_MEM_BASE 0x18000000
+#define LQ_PCI_MEM_SIZE 0x02000000
+#define LQ_PCI_IO_BASE 0x1AE00000
+#define LQ_PCI_IO_SIZE 0x00200000
+
+#define PCI_CR_FCI_ADDR_MAP0 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00C0))
+#define PCI_CR_FCI_ADDR_MAP1 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00C4))
+#define PCI_CR_FCI_ADDR_MAP2 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00C8))
+#define PCI_CR_FCI_ADDR_MAP3 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00CC))
+#define PCI_CR_FCI_ADDR_MAP4 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00D0))
+#define PCI_CR_FCI_ADDR_MAP5 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00D4))
+#define PCI_CR_FCI_ADDR_MAP6 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00D8))
+#define PCI_CR_FCI_ADDR_MAP7 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00DC))
+#define PCI_CR_CLK_CTRL ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x0000))
+#define PCI_CR_PCI_MOD ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x0030))
+#define PCI_CR_PC_ARB ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x0080))
+#define PCI_CR_FCI_ADDR_MAP11hg ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00E4))
+#define PCI_CR_BAR11MASK ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x0044))
+#define PCI_CR_BAR12MASK ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x0048))
+#define PCI_CR_BAR13MASK ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x004C))
+#define PCI_CS_BASE_ADDR1 ((u32 *)(PCI_CS_PR_BASE_ADDR + 0x0010))
+#define PCI_CR_PCI_ADDR_MAP11 ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x0064))
+#define PCI_CR_FCI_BURST_LENGTH ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x00E8))
+#define PCI_CR_PCI_EOI ((u32 *)(PCI_CR_PR_BASE_ADDR + 0x002C))
+
+
+#define PCI_CS_STS_CMD ((u32 *)(PCI_CS_PR_BASE_ADDR + 0x0004))
+
+#define PCI_MASTER0_REQ_MASK_2BITS 8
+#define PCI_MASTER1_REQ_MASK_2BITS 10
+#define PCI_MASTER2_REQ_MASK_2BITS 12
+#define INTERNAL_ARB_ENABLE_BIT 0
+
+#define LQ_CGU_IFCCR ((u32 *)(LQ_CGU_BASE_ADDR + 0x0018))
+#define LQ_CGU_PCICR ((u32 *)(LQ_CGU_BASE_ADDR + 0x0034))
+
+extern int lq_pci_read_config_dword(struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 *val);
+extern int lq_pci_write_config_dword(struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 val);
+
+u32 lq_pci_mapped_cfg;
+
+/* Since the PCI REQ pins can be reused for other functionality, make it possible
+ to exclude those from interpretation by the PCI controller */
+static int lq_pci_req_mask = 0xf;
+
+struct pci_ops lq_pci_ops =
+{
+ .read = lq_pci_read_config_dword,
+ .write = lq_pci_write_config_dword
+};
+
+static struct resource pci_io_resource =
+{
+ .name = "pci io space",
+ .start = LQ_PCI_IO_BASE,
+ .end = LQ_PCI_IO_BASE + LQ_PCI_IO_SIZE - 1,
+ .flags = IORESOURCE_IO
+};
+
+static struct resource pci_mem_resource =
+{
+ .name = "pci memory space",
+ .start = LQ_PCI_MEM_BASE,
+ .end = LQ_PCI_MEM_BASE + LQ_PCI_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM
+};
+
+static struct pci_controller lq_pci_controller =
+{
+ .pci_ops = &lq_pci_ops,
+ .mem_resource = &pci_mem_resource,
+ .mem_offset = 0x00000000UL,
+ .io_resource = &pci_io_resource,
+ .io_offset = 0x00000000UL,
+};
+
+int
+pcibios_plat_dev_init(struct pci_dev *dev)
+{
+ u8 pin;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ switch(pin)
+ {
+ case 0:
+ break;
+ case 1:
+ //falling edge level triggered:0x4, low level:0xc, rising edge:0x2
+ lq_w32(lq_r32(LQ_EBU_PCC_CON) | 0xc, LQ_EBU_PCC_CON);
+ lq_w32(lq_r32(LQ_EBU_PCC_IEN) | 0x10, LQ_EBU_PCC_IEN);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ printk ("WARNING: interrupt pin %d not supported yet!\n", pin);
+ default:
+ printk ("WARNING: invalid interrupt pin %d\n", pin);
+ return 1;
+ }
+ return 0;
+}
+
+static u32
+lq_calc_bar11mask(void)
+{
+ u32 mem, bar11mask;
+
+ /* BAR11MASK value depends on available memory on system. */
+ mem = num_physpages * PAGE_SIZE;
+ bar11mask = (0x0ffffff0 & ~((1 << (fls(mem) -1)) -1)) | 8;
+
+ return bar11mask;
+}
+
+static void
+lq_pci_setup_clk(int external_clock)
+{
+ /* set clock to 33Mhz */
+ lq_w32(lq_r32(LQ_CGU_IFCCR) & ~0xf00000, LQ_CGU_IFCCR);
+ lq_w32(lq_r32(LQ_CGU_IFCCR) | 0x800000, LQ_CGU_IFCCR);
+ if (external_clock)
+ {
+ lq_w32(lq_r32(LQ_CGU_IFCCR) & ~(1 << 16), LQ_CGU_IFCCR);
+ lq_w32((1 << 30), LQ_CGU_PCICR);
+ } else {
+ lq_w32(lq_r32(LQ_CGU_IFCCR) | (1 << 16), LQ_CGU_IFCCR);
+ lq_w32((1 << 31) | (1 << 30), LQ_CGU_PCICR);
+ }
+}
+
+static void
+lq_pci_setup_gpio(void)
+{
+ /* PCI reset line is gpio driven */
+ lq_gpio_request(21, 0, 0, 1, "pci-reset");
+
+ /* PCI_REQ line */
+ lq_gpio_request(29, 1, 0, 0, "pci-req");
+
+ /* PCI_GNT line */
+ lq_gpio_request(30, 1, 0, 1, "pci-gnt");
+}
+
+static int __init
+lq_pci_startup(void)
+{
+ u32 temp_buffer;
+
+ /* setup pci clock and gpis used by pci */
+ lq_pci_setup_gpio();
+
+ /* enable auto-switching between PCI and EBU */
+ lq_w32(0xa, PCI_CR_CLK_CTRL);
+
+ /* busy, i.e. configuration is not done, PCI access has to be retried */
+ lq_w32(lq_r32(PCI_CR_PCI_MOD) & ~(1 << 24), PCI_CR_PCI_MOD);
+ wmb ();
+ /* BUS Master/IO/MEM access */
+ lq_w32(lq_r32(PCI_CS_STS_CMD) | 7, PCI_CS_STS_CMD);
+
+ /* enable external 2 PCI masters */
+ temp_buffer = lq_r32(PCI_CR_PC_ARB);
+ temp_buffer &= (~(lq_pci_req_mask << 16));
+ /* enable internal arbiter */
+ temp_buffer |= (1 << INTERNAL_ARB_ENABLE_BIT);
+ /* enable internal PCI master reqest */
+ temp_buffer &= (~(3 << PCI_MASTER0_REQ_MASK_2BITS));
+
+ /* enable EBU request */
+ temp_buffer &= (~(3 << PCI_MASTER1_REQ_MASK_2BITS));
+
+ /* enable all external masters request */
+ temp_buffer &= (~(3 << PCI_MASTER2_REQ_MASK_2BITS));
+ lq_w32(temp_buffer, PCI_CR_PC_ARB);
+ wmb ();
+
+ /* setup BAR memory regions */
+ lq_w32(0x18000000, PCI_CR_FCI_ADDR_MAP0);
+ lq_w32(0x18400000, PCI_CR_FCI_ADDR_MAP1);
+ lq_w32(0x18800000, PCI_CR_FCI_ADDR_MAP2);
+ lq_w32(0x18c00000, PCI_CR_FCI_ADDR_MAP3);
+ lq_w32(0x19000000, PCI_CR_FCI_ADDR_MAP4);
+ lq_w32(0x19400000, PCI_CR_FCI_ADDR_MAP5);
+ lq_w32(0x19800000, PCI_CR_FCI_ADDR_MAP6);
+ lq_w32(0x19c00000, PCI_CR_FCI_ADDR_MAP7);
+ lq_w32(0x1ae00000, PCI_CR_FCI_ADDR_MAP11hg);
+ lq_w32(lq_calc_bar11mask(), PCI_CR_BAR11MASK);
+ lq_w32(0, PCI_CR_PCI_ADDR_MAP11);
+ lq_w32(0, PCI_CS_BASE_ADDR1);
+#ifdef CONFIG_SWAP_IO_SPACE
+ /* both TX and RX endian swap are enabled */
+ lq_w32(lq_r32(PCI_CR_PCI_EOI) | 3, PCI_CR_PCI_EOI);
+ wmb ();
+#endif
+ /*TODO: disable BAR2 & BAR3 - why was this in the origianl infineon code */
+ lq_w32(lq_r32(PCI_CR_BAR12MASK) | 0x80000000, PCI_CR_BAR12MASK);
+ lq_w32(lq_r32(PCI_CR_BAR13MASK) | 0x80000000, PCI_CR_BAR13MASK);
+ /*use 8 dw burst length */
+ lq_w32(0x303, PCI_CR_FCI_BURST_LENGTH);
+ lq_w32(lq_r32(PCI_CR_PCI_MOD) | (1 << 24), PCI_CR_PCI_MOD);
+ wmb();
+
+ /* toggle reset pin */
+ __gpio_set_value(21, 0);
+ wmb();
+ mdelay(1);
+ __gpio_set_value(21, 1);
+ return 0;
+}
+
+int __init
+pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin){
+ switch(slot)
+ {
+ case 13:
+ /* IDSEL = AD29 --> USB Host Controller */
+ return (INT_NUM_IM1_IRL0 + 17);
+ case 14:
+ /* IDSEL = AD30 --> mini PCI connector */
+ return (INT_NUM_IM0_IRL0 + 22);
+ default:
+ printk("lq_pci: no IRQ found for slot %d, pin %d\n", slot, pin);
+ return 0;
+ }
+}
+
+static int
+lq_pci_probe(struct platform_device *pdev)
+{
+ struct lq_pci_data *lq_pci_data = (struct lq_pci_data*) pdev->dev.platform_data;
+ extern int pci_probe_only;
+
+ pci_probe_only = 0;
+ lq_pci_req_mask = lq_pci_data->req_mask;
+ lq_pci_setup_clk(lq_pci_data->clock);
+
+ lq_pci_startup();
+ lq_pci_mapped_cfg =
+ (u32)ioremap_nocache(LQ_PCI_CFG_BASE, LQ_PCI_CFG_BASE);
+ lq_pci_controller.io_map_base =
+ (unsigned long)ioremap(LQ_PCI_IO_BASE, LQ_PCI_IO_SIZE - 1);
+
+ register_pci_controller(&lq_pci_controller);
+ return 0;
+}
+
+static struct platform_driver
+lq_pci_driver = {
+ .probe = lq_pci_probe,
+ .driver = {
+ .name = "lq_pci",
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init
+pcibios_init(void)
+{
+ int ret = platform_driver_register(&lq_pci_driver);
+ if(ret)
+ printk(KERN_INFO "lq_pci: Error registering platfom driver!");
+ return ret;
+}
+
+arch_initcall(pcibios_init);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,346 @@
--- /dev/null
+++ b/include/linux/udp_redirect.h
@@ -0,0 +1,57 @@
+#ifndef _UDP_REDIRECT_H
+#define _UDP_REDIRECT_H
+
+/******************************************************************************
+
+ Copyright (c) 2006
+ Infineon Technologies AG
+ Am Campeon 1-12; 81726 Munich, Germany
+
+ THE DELIVERY OF THIS SOFTWARE AS WELL AS THE HEREBY GRANTED NON-EXCLUSIVE,
+ WORLDWIDE LICENSE TO USE, COPY, MODIFY, DISTRIBUTE AND SUBLICENSE THIS
+ SOFTWARE IS FREE OF CHARGE.
+
+ THE LICENSED SOFTWARE IS PROVIDED "AS IS" AND INFINEON EXPRESSLY DISCLAIMS
+ ALL REPRESENTATIONS AND WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING
+ WITHOUT LIMITATION, WARRANTIES OR REPRESENTATIONS OF WORKMANSHIP,
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, THAT THE
+ OPERATING OF THE LICENSED SOFTWARE WILL BE ERROR FREE OR FREE OF ANY THIRD
+ PARTY CLAIMS, INCLUDING WITHOUT LIMITATION CLAIMS OF THIRD PARTY INTELLECTUAL
+ PROPERTY INFRINGEMENT.
+
+ EXCEPT FOR ANY LIABILITY DUE TO WILFUL ACTS OR GROSS NEGLIGENCE AND EXCEPT
+ FOR ANY PERSONAL INJURY INFINEON SHALL IN NO EVENT BE LIABLE FOR ANY CLAIM
+ OR DAMAGES OF ANY KIND, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+******************************************************************************/
+
+/* ============================= */
+/* Includes */
+/* ============================= */
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+
+
+/* ============================= */
+/* Definitions */
+/* ============================= */
+#define UDP_REDIRECT_MAGIC (void*)0x55445052L
+
+
+/* ============================= */
+/* Global variable declaration */
+/* ============================= */
+extern int (*udp_do_redirect_fn)(struct sock *sk, struct sk_buff *skb);
+extern int (*udpredirect_getfrag_fn)(void *p, char * to,
+ int offset, int fraglen, int odd,
+ struct sk_buff *skb);
+/* ============================= */
+/* Global function declaration */
+/* ============================= */
+
+extern int udpredirect_getfrag(void *p, char * to, int offset,
+ int fraglen, int odd, struct sk_buff *skb);
+#endif
--- /dev/null
+++ b/net/ipv4/udp_redirect_symb.c
@@ -0,0 +1,186 @@
+/******************************************************************************
+
+ Copyright (c) 2006
+ Infineon Technologies AG
+ Am Campeon 1-12; 81726 Munich, Germany
+
+ THE DELIVERY OF THIS SOFTWARE AS WELL AS THE HEREBY GRANTED NON-EXCLUSIVE,
+ WORLDWIDE LICENSE TO USE, COPY, MODIFY, DISTRIBUTE AND SUBLICENSE THIS
+ SOFTWARE IS FREE OF CHARGE.
+
+ THE LICENSED SOFTWARE IS PROVIDED "AS IS" AND INFINEON EXPRESSLY DISCLAIMS
+ ALL REPRESENTATIONS AND WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING
+ WITHOUT LIMITATION, WARRANTIES OR REPRESENTATIONS OF WORKMANSHIP,
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, THAT THE
+ OPERATING OF THE LICENSED SOFTWARE WILL BE ERROR FREE OR FREE OF ANY THIRD
+ PARTY CLAIMS, INCLUDING WITHOUT LIMITATION CLAIMS OF THIRD PARTY INTELLECTUAL
+ PROPERTY INFRINGEMENT.
+
+ EXCEPT FOR ANY LIABILITY DUE TO WILFUL ACTS OR GROSS NEGLIGENCE AND EXCEPT
+ FOR ANY PERSONAL INJURY INFINEON SHALL IN NO EVENT BE LIABLE FOR ANY CLAIM
+ OR DAMAGES OF ANY KIND, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+******************************************************************************/
+#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE)
+/* ============================= */
+/* Includes */
+/* ============================= */
+#include <net/checksum.h>
+#include <net/udp.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/udp_redirect.h>
+
+/* ============================= */
+/* Global variable definition */
+/* ============================= */
+int (*udpredirect_getfrag_fn) (void *p, char * to, int offset,
+ int fraglen, int odd, struct sk_buff *skb) = NULL;
+int (*udp_do_redirect_fn)(struct sock *sk, struct sk_buff *skb) = NULL;
+
+/* ============================= */
+/* Local type definitions */
+/* ============================= */
+struct udpfakehdr
+{
+ struct udphdr uh;
+ u32 saddr;
+ u32 daddr;
+ struct iovec *iov;
+ u32 wcheck;
+};
+
+/* ============================= */
+/* Local function declaration */
+/* ============================= */
+static int udpredirect_csum_partial_copy_fromiovecend(unsigned char *kdata,
+ struct iovec *iov, int offset, unsigned int len, __wsum *csump);
+
+static int udpredirect_memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
+ int len);
+
+/* ============================= */
+/* Global function definition */
+/* ============================= */
+
+/*
+ Copy of udp_getfrag() from udp.c
+ This function exists because no copy_from_user() is needed for udpredirect.
+*/
+
+int
+udpredirect_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
+{
+ struct iovec *iov = from;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (udpredirect_memcpy_fromiovecend(to, iov, offset, len) < 0)
+ return -EFAULT;
+ } else {
+ __wsum csum = 0;
+ if (udpredirect_csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0)
+ return -EFAULT;
+ skb->csum = csum_block_add(skb->csum, csum, odd);
+ }
+ return 0;
+}
+
+static int udpredirect_memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
+ int len)
+{
+ /* Skip over the finished iovecs */
+ while (offset >= iov->iov_len) {
+ offset -= iov->iov_len;
+ iov++;
+ }
+
+ while (len > 0) {
+ u8 __user *base = iov->iov_base + offset;
+ int copy = min_t(unsigned int, len, iov->iov_len - offset);
+
+ offset = 0;
+ memcpy(kdata, base, copy);
+ len -= copy;
+ kdata += copy;
+ iov++;
+ }
+
+ return 0;
+}
+
+/*
+ Copy of csum_partial_copy_fromiovecend() from iovec.c
+ This function exists because no copy_from_user() is needed for udpredirect.
+*/
+
+int udpredirect_csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
+ int offset, unsigned int len, __wsum *csump)
+{
+ __wsum csum = *csump;
+ int partial_cnt = 0, err = 0;
+
+ /* Skip over the finished iovecs */
+ while (offset >= iov->iov_len) {
+ offset -= iov->iov_len;
+ iov++;
+ }
+
+ while (len > 0) {
+ u8 __user *base = iov->iov_base + offset;
+ int copy = min_t(unsigned int, len, iov->iov_len - offset);
+
+ offset = 0;
+
+ /* There is a remnant from previous iov. */
+ if (partial_cnt) {
+ int par_len = 4 - partial_cnt;
+
+ /* iov component is too short ... */
+ if (par_len > copy) {
+ memcpy(kdata, base, copy);
+ kdata += copy;
+ base += copy;
+ partial_cnt += copy;
+ len -= copy;
+ iov++;
+ if (len)
+ continue;
+ *csump = csum_partial(kdata - partial_cnt,
+ partial_cnt, csum);
+ goto out;
+ }
+ memcpy(kdata, base, par_len);
+ csum = csum_partial(kdata - partial_cnt, 4, csum);
+ kdata += par_len;
+ base += par_len;
+ copy -= par_len;
+ len -= par_len;
+ partial_cnt = 0;
+ }
+
+ if (len > copy) {
+ partial_cnt = copy % 4;
+ if (partial_cnt) {
+ copy -= partial_cnt;
+ memcpy(kdata + copy, base + copy, partial_cnt);
+ }
+ }
+
+ if (copy) {
+ csum = csum_partial_copy_nocheck(base, kdata, copy, csum);
+ }
+ len -= copy + partial_cnt;
+ kdata += copy + partial_cnt;
+ iov++;
+ }
+ *csump = csum;
+out:
+ return err;
+}
+
+EXPORT_SYMBOL(udpredirect_getfrag);
+EXPORT_SYMBOL(udp_do_redirect_fn);
+EXPORT_SYMBOL(udpredirect_getfrag_fn);
+#endif /* CONFIG_IFX_UDP_REDIRECT* */
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -14,6 +14,9 @@ obj-y := route.o inetpeer.o protocol
inet_fragment.o
obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
+ifneq ($(CONFIG_IFX_UDP_REDIRECT),)
+obj-$(CONFIG_IFX_UDP_REDIRECT) += udp_redirect_symb.o
+endif
obj-$(CONFIG_IP_FIB_HASH) += fib_hash.o
obj-$(CONFIG_IP_FIB_TRIE) += fib_trie.o
obj-$(CONFIG_PROC_FS) += proc.o
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -106,6 +106,10 @@
#include <net/xfrm.h>
#include "udp_impl.h"
+#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE)
+#include <linux/udp_redirect.h>
+#endif
+
struct udp_table udp_table __read_mostly;
EXPORT_SYMBOL(udp_table);
@@ -782,7 +786,7 @@ int udp_sendmsg(struct kiocb *iocb, stru
u8 tos;
int err, is_udplite = IS_UDPLITE(sk);
int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
- int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
+ int (*getfrag)(void *, char *, int, int, int, struct sk_buff *) = NULL;
if (len > 0xFFFF)
return -EMSGSIZE;
@@ -944,6 +948,12 @@ back_from_confirm:
do_append_data:
up->len += ulen;
+ /* UDPREDIRECT */
+#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE)
+ if(udpredirect_getfrag_fn && sk->sk_user_data == UDP_REDIRECT_MAGIC)
+ getfrag = udpredirect_getfrag_fn;
+ else
+#endif /* IFX_UDP_REDIRECT */
getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
sizeof(struct udphdr), &ipc, &rt,
@@ -1518,6 +1528,7 @@ int __udp4_lib_rcv(struct sk_buff *skb,
struct rtable *rt = skb_rtable(skb);
__be32 saddr, daddr;
struct net *net = dev_net(skb->dev);
+ int ret = 0;
/*
* Validate the packet.
@@ -1550,7 +1561,16 @@ int __udp4_lib_rcv(struct sk_buff *skb,
sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
if (sk != NULL) {
- int ret = udp_queue_rcv_skb(sk, skb);
+ /* UDPREDIRECT */
+#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE)
+ if(udp_do_redirect_fn && sk->sk_user_data == UDP_REDIRECT_MAGIC)
+ {
+ udp_do_redirect_fn(sk,skb);
+ kfree_skb(skb);
+ return(0);
+ }
+#endif
+ ret = udp_queue_rcv_skb(sk, skb);
sock_put(sk);
/* a return value > 0 means to resubmit the input, but
@@ -1845,7 +1865,7 @@ struct proto udp_prot = {
#endif
};
EXPORT_SYMBOL(udp_prot);
-
+EXPORT_SYMBOL(udp_rcv);
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_PROC_FS
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -72,6 +72,12 @@ config INET
Short answer: say Y.
+config IFX_UDP_REDIRECT
+ bool "IFX Kernel Packet Interface for UDP redirection"
+ help
+ You can say Y here if you want to use hooks from kernel for
+ UDP redirection.
+
if INET
source "net/ipv4/Kconfig"
source "net/ipv6/Kconfig"

View File

@@ -0,0 +1,42 @@
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -52,6 +52,8 @@
void (*_dma_cache_inv)(unsigned long start, unsigned long size);
EXPORT_SYMBOL(_dma_cache_wback_inv);
+EXPORT_SYMBOL(_dma_cache_wback);
+EXPORT_SYMBOL(_dma_cache_inv);
#endif /* CONFIG_DMA_NONCOHERENT */
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -153,7 +153,7 @@
static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc)
{
static const char *const class_name[] = {
- "off", "UBR", "CBR", "VBR", "ABR"};
+ "off","UBR","CBR","NTR-VBR","ABR","ANY","RT-VBR","UBR+","GFR"};
static const char *const aal_name[] = {
"---", "1", "2", "3/4", /* 0- 3 */
"???", "5", "???", "???", /* 4- 7 */
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -60,11 +60,17 @@
write_unlock_irq(&vcc_sklist_lock);
}
+struct sk_buff* (*ifx_atm_alloc_tx)(struct atm_vcc *, unsigned int) = NULL;
+EXPORT_SYMBOL(ifx_atm_alloc_tx);
+
static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size)
{
struct sk_buff *skb;
struct sock *sk = sk_atm(vcc);
+ if (ifx_atm_alloc_tx != NULL)
+ return ifx_atm_alloc_tx(vcc, size);
+
if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {
pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n",
sk_wmem_alloc_get(sk), size, sk->sk_sndbuf);

View File

@@ -0,0 +1,211 @@
--- a/arch/mips/include/asm/mach-lantiq/machine.h
+++ b/arch/mips/include/asm/mach-lantiq/machine.h
@@ -11,4 +11,7 @@
LANTIQ_MACH_EASY4010, /* Twinpass evalkit */
LANTIQ_MACH_EASY50712, /* Danube evalkit */
LANTIQ_MACH_EASY50812, /* AR9 eval board */
+ LANTIQ_MACH_ARV4518, /* Airties WAV-221, SMC-7908A-ISP */
+ LANTIQ_MACH_ARV452, /* Airties WAV-281, Arcor EasyboxA800 */
+ LANTIQ_MACH_ARV4525, /* Speedport W502V */
};
--- a/arch/mips/lantiq/xway/Kconfig
+++ b/arch/mips/lantiq/xway/Kconfig
@@ -14,6 +14,10 @@
bool "Easy4010"
default y
+config LANTIQ_MACH_ARV45XX
+ bool "ARV45XX"
+ default y
+
endmenu
endif
--- a/arch/mips/lantiq/xway/Makefile
+++ b/arch/mips/lantiq/xway/Makefile
@@ -3,3 +3,4 @@
obj-$(CONFIG_LANTIQ_MACH_EASY50812) += mach-easy50812.o
obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o
obj-$(CONFIG_LANTIQ_MACH_EASY4010) += mach-easy4010.o
+obj-$(CONFIG_LANTIQ_MACH_ARV45XX) += mach-arv45xx.o
--- /dev/null
+++ b/arch/mips/lantiq/xway/mach-arv45xx.c
@@ -0,0 +1,178 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/gpio_buttons.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/input.h>
+#include <linux/etherdevice.h>
+
+#include <machine.h>
+
+#include <xway.h>
+#include <lantiq_platform.h>
+
+#include "devices.h"
+
+#define ARV452_LATCH_SWITCH (1 << 10)
+
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition arv45xx_partitions[] =
+{
+ {
+ .name = "uboot",
+ .offset = 0x0,
+ .size = 0x20000,
+ },
+ {
+ .name = "uboot_env",
+ .offset = 0x20000,
+ .size = 0x10000,
+ },
+ {
+ .name = "linux",
+ .offset = 0x30000,
+ .size = 0x3c0000,
+ },
+ {
+ .name = "board_config",
+ .offset = 0x3f0000,
+ .size = 0x10000,
+ },
+};
+#endif
+
+static struct physmap_flash_data arv45xx_flash_data = {
+#ifdef CONFIG_MTD_PARTITIONS
+ .nr_parts = ARRAY_SIZE(arv45xx_partitions),
+ .parts = arv45xx_partitions,
+#endif
+};
+
+static struct lq_pci_data lq_pci_data = {
+ .clock = PCI_CLOCK_EXT,
+ .req_mask = 0xf,
+};
+
+static struct lq_eth_data lq_eth_data = {
+ .mii_mode = REV_MII_MODE,
+ .mac = "\xff\xff\xff\xff\xff\xff",
+};
+
+static struct gpio_led
+arv4518_leds_gpio[] __initdata = {
+ { .name = "soc:blue:power", .gpio = 3, .active_low = 1, },
+ { .name = "soc:blue:adsl", .gpio = 4, .active_low = 1, },
+ { .name = "soc:blue:internet", .gpio = 5, .active_low = 1, },
+ { .name = "soc:red:power", .gpio = 6, .active_low = 1, },
+ { .name = "soc:yello:wps", .gpio = 7, .active_low = 1, },
+ { .name = "soc:red:wps", .gpio = 9, .active_low = 1, },
+ { .name = "soc:blue:voip", .gpio = 32, .active_low = 1, },
+ { .name = "soc:blue:fxs1", .gpio = 33, .active_low = 1, },
+ { .name = "soc:blue:fxs2", .gpio = 34, .active_low = 1, },
+ { .name = "soc:blue:fxo", .gpio = 35, .active_low = 1, },
+ { .name = "soc:blue:voice", .gpio = 36, .active_low = 1, },
+ { .name = "soc:blue:usb", .gpio = 37, .active_low = 1, },
+ { .name = "soc:blue:wlan", .gpio = 38, .active_low = 1, },
+};
+
+static struct gpio_led
+arv452_leds_gpio[] __initdata = {
+ { .name = "soc:blue:power", .gpio = 3, .active_low = 1, },
+ { .name = "soc:blue:adsl", .gpio = 4, .active_low = 1, },
+ { .name = "soc:blue:internet", .gpio = 5, .active_low = 1, },
+ { .name = "soc:red:power", .gpio = 6, .active_low = 1, },
+ { .name = "soc:yello:wps", .gpio = 7, .active_low = 1, },
+ { .name = "soc:red:wps", .gpio = 9, .active_low = 1, },
+ { .name = "soc:blue:voip", .gpio = 32, .active_low = 1, },
+ { .name = "soc:blue:fxs1", .gpio = 33, .active_low = 1, },
+ { .name = "soc:blue:fxs2", .gpio = 34, .active_low = 1, },
+ { .name = "soc:blue:fxo", .gpio = 35, .active_low = 1, },
+ { .name = "soc:blue:voice", .gpio = 36, .active_low = 1, },
+ { .name = "soc:blue:usb", .gpio = 37, .active_low = 1, },
+ { .name = "soc:blue:wlan", .gpio = 38, .active_low = 1, },
+};
+
+static struct gpio_led arv4525_leds_gpio[] __initdata = {
+ { .name = "soc:green:festnetz", .gpio = 4, .active_low = 1, },
+ { .name = "soc:green:internet", .gpio = 5, .active_low = 1, },
+ { .name = "soc:green:dsl", .gpio = 6, .active_low = 1, },
+ { .name = "soc:green:wlan", .gpio = 8, .active_low = 1, },
+ { .name = "soc:green:online", .gpio = 9, .active_low = 1, },
+};
+
+static void
+arv45xx_register_ethernet(void)
+{
+#define ARV45XX_BRN_MAC 0x3f0016
+ memcpy_fromio(lq_eth_data.mac,
+ (void *)KSEG1ADDR(LQ_FLASH_START + ARV45XX_BRN_MAC), 6);
+ lq_register_ethernet(&lq_eth_data);
+}
+
+static void __init
+arv4518_init(void)
+{
+ lq_register_gpio();
+ lq_register_gpio_ebu(0);
+ lq_register_gpio_leds(arv4518_leds_gpio, ARRAY_SIZE(arv4518_leds_gpio));
+ lq_register_asc(0);
+ lq_register_asc(1);
+ lq_register_nor(&arv45xx_flash_data);
+ lq_register_pci(&lq_pci_data);
+ lq_register_wdt();
+ arv45xx_register_ethernet();
+}
+
+MIPS_MACHINE(LANTIQ_MACH_ARV4518,
+ "ARV4518",
+ "ARV4518 - SMC7908A-ISP",
+ arv4518_init);
+
+static void __init
+arv452_init(void)
+{
+ lq_register_gpio();
+ lq_register_gpio_ebu(ARV452_LATCH_SWITCH);
+ lq_register_gpio_leds(arv452_leds_gpio, ARRAY_SIZE(arv452_leds_gpio));
+ lq_register_asc(0);
+ lq_register_asc(1);
+ lq_register_nor(&arv45xx_flash_data);
+ lq_register_pci(&lq_pci_data);
+ lq_register_wdt();
+ arv45xx_register_ethernet();
+}
+
+MIPS_MACHINE(LANTIQ_MACH_ARV452,
+ "ARV452",
+ "ARV452 - Airties WAV-281, Arcor A800",
+ arv452_init);
+
+static void __init
+arv4525_init(void)
+{
+ lq_register_gpio();
+ lq_register_gpio_leds(arv4525_leds_gpio, ARRAY_SIZE(arv4525_leds_gpio));
+ lq_register_asc(0);
+ lq_register_asc(1);
+ lq_register_nor(&arv45xx_flash_data);
+ lq_register_pci(&lq_pci_data);
+ lq_register_wdt();
+ lq_eth_data.mii_mode = MII_MODE;
+ arv45xx_register_ethernet();
+}
+
+MIPS_MACHINE(LANTIQ_MACH_ARV4525,
+ "ARV4525",
+ "ARV4525 - Speedport W502V",
+ arv4525_init);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,301 @@
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1706,6 +1706,28 @@
help
IFX included extensions in APRP
+config IFX_VPE_CACHE_SPLIT
+ bool "IFX Cache Split Ways"
+ depends on IFX_VPE_EXT
+ help
+ IFX extension for reserving (splitting) cache ways among VPEs. You must
+ give kernel command line arguments vpe_icache_shared=0 or
+ vpe_dcache_shared=0 to enable splitting of icache or dcache
+ respectively. Then you can specify which cache ways should be
+ assigned to which VPE. There are total 8 cache ways, 4 each
+ for dcache and icache: dcache_way0, dcache_way1,dcache_way2,
+ dcache_way3 and icache_way0,icache_way1, icache_way2,icache_way3.
+
+ For example, if you specify vpe_icache_shared=0 and icache_way2=1,
+ then the 3rd icache way will be assigned to VPE0 and denied in VPE1.
+
+ For icache, software is required to make at least one cache way available
+ for a VPE at all times i.e., one can't assign all the icache ways to one
+ VPE.
+
+ By default, vpe_dcache_shared and vpe_icache_shared are set to 1
+ (i.e., both icache and dcache are shared among VPEs)
+
config PERFCTRS
bool "34K Performance counters"
depends on MIPS_MT && PROC_FS
--- a/arch/mips/kernel/vpe.c
+++ b/arch/mips/kernel/vpe.c
@@ -128,6 +128,13 @@
EXPORT_SYMBOL(vpe1_wdog_timeout);
#endif
+
+#ifdef CONFIG_IFX_VPE_CACHE_SPLIT /* Code for splitting the cache ways among VPEs. */
+extern int vpe_icache_shared,vpe_dcache_shared;
+extern int icache_way0,icache_way1,icache_way2,icache_way3;
+extern int dcache_way0,dcache_way1,dcache_way2,dcache_way3;
+#endif
+
/* grab the likely amount of memory we will need. */
#ifdef CONFIG_MIPS_VPE_LOADER_TOM
#define P_SIZE (2 * 1024 * 1024)
@@ -866,6 +873,65 @@
/* enable this VPE */
write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
+#ifdef CONFIG_IFX_VPE_CACHE_SPLIT
+ if ( (!vpe_icache_shared) || (!vpe_dcache_shared) ) {
+
+ /* PCP bit must be 1 to split the cache */
+ if(read_c0_mvpconf0() & MVPCONF0_PCP) {
+
+ if ( !vpe_icache_shared ){
+ write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0()) & ~VPECONF0_ICS);
+
+ /*
+ * If any cache way is 1, then that way is denied
+ * in VPE1. Otherwise assign that way to VPE1.
+ */
+ if (!icache_way0)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX0 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX0 );
+ if (!icache_way1)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX1 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX1 );
+ if (!icache_way2)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX2 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX2 );
+ if (!icache_way3)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX3 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX3 );
+ }
+
+ if ( !vpe_dcache_shared ) {
+ write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0()) & ~VPECONF0_DCS);
+
+ /*
+ * If any cache way is 1, then that way is denied
+ * in VPE1. Otherwise assign that way to VPE1.
+ */
+ if (!dcache_way0)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX0 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX0 );
+ if (!dcache_way1)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX1 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX1 );
+ if (!dcache_way2)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX2 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX2 );
+ if (!dcache_way3)
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX3 );
+ else
+ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX3 );
+ }
+ }
+ }
+#endif /* endif CONFIG_IFX_VPE_CACHE_SPLIT */
+
/* clear out any left overs from a previous program */
write_vpe_c0_status(0);
write_vpe_c0_cause(0);
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -1348,6 +1348,106 @@
__setup("coherentio", setcoherentio);
#endif
+#ifdef CONFIG_IFX_VPE_CACHE_SPLIT /* Code for splitting the cache ways among VPEs. */
+
+#include <asm/mipsmtregs.h>
+
+/*
+ * By default, vpe_icache_shared and vpe_dcache_shared
+ * values are 1 i.e., both icache and dcache are shared
+ * among the VPEs.
+ */
+
+int vpe_icache_shared = 1;
+static int __init vpe_icache_shared_val(char *str)
+{
+ get_option(&str, &vpe_icache_shared);
+ return 1;
+}
+__setup("vpe_icache_shared=", vpe_icache_shared_val);
+EXPORT_SYMBOL(vpe_icache_shared);
+
+int vpe_dcache_shared = 1;
+static int __init vpe_dcache_shared_val(char *str)
+{
+ get_option(&str, &vpe_dcache_shared);
+ return 1;
+}
+__setup("vpe_dcache_shared=", vpe_dcache_shared_val);
+EXPORT_SYMBOL(vpe_dcache_shared);
+
+/*
+ * Software is required to make atleast one icache
+ * way available for a VPE at all times i.e., one
+ * can't assign all the icache ways to one VPE.
+ */
+
+int icache_way0 = 0;
+static int __init icache_way0_val(char *str)
+{
+ get_option(&str, &icache_way0);
+ return 1;
+}
+__setup("icache_way0=", icache_way0_val);
+
+int icache_way1 = 0;
+static int __init icache_way1_val(char *str)
+{
+ get_option(&str, &icache_way1);
+ return 1;
+}
+__setup("icache_way1=", icache_way1_val);
+
+int icache_way2 = 0;
+static int __init icache_way2_val(char *str)
+{
+ get_option(&str, &icache_way2);
+ return 1;
+}
+__setup("icache_way2=", icache_way2_val);
+
+int icache_way3 = 0;
+static int __init icache_way3_val(char *str)
+{
+ get_option(&str, &icache_way3);
+ return 1;
+}
+__setup("icache_way3=", icache_way3_val);
+
+int dcache_way0 = 0;
+static int __init dcache_way0_val(char *str)
+{
+ get_option(&str, &dcache_way0);
+ return 1;
+}
+__setup("dcache_way0=", dcache_way0_val);
+
+int dcache_way1 = 0;
+static int __init dcache_way1_val(char *str)
+{
+ get_option(&str, &dcache_way1);
+ return 1;
+}
+__setup("dcache_way1=", dcache_way1_val);
+
+int dcache_way2 = 0;
+static int __init dcache_way2_val(char *str)
+{
+ get_option(&str, &dcache_way2);
+ return 1;
+}
+__setup("dcache_way2=", dcache_way2_val);
+
+int dcache_way3 = 0;
+static int __init dcache_way3_val(char *str)
+{
+ get_option(&str, &dcache_way3);
+ return 1;
+}
+__setup("dcache_way3=", dcache_way3_val);
+
+#endif /* endif CONFIG_IFX_VPE_CACHE_SPLIT */
+
void __cpuinit r4k_cache_init(void)
{
extern void build_clear_page(void);
@@ -1367,6 +1467,78 @@
break;
}
+#ifdef CONFIG_IFX_VPE_CACHE_SPLIT
+ /*
+ * We split the cache ways appropriately among the VPEs
+ * based on cache ways values we received as command line
+ * arguments
+ */
+ if ( (!vpe_icache_shared) || (!vpe_dcache_shared) ){
+
+ /* PCP bit must be 1 to split the cache */
+ if(read_c0_mvpconf0() & MVPCONF0_PCP) {
+
+ /* Set CPA bit which enables us to modify VPEOpt register */
+ write_c0_mvpcontrol((read_c0_mvpcontrol()) | MVPCONTROL_CPA);
+
+ if ( !vpe_icache_shared ){
+ write_c0_vpeconf0((read_c0_vpeconf0()) & ~VPECONF0_ICS);
+ /*
+ * If any cache way is 1, then that way is denied
+ * in VPE0. Otherwise assign that way to VPE0.
+ */
+ printk(KERN_DEBUG "icache is split\n");
+ printk(KERN_DEBUG "icache_way0=%d icache_way1=%d icache_way2=%d icache_way3=%d\n",
+ icache_way0, icache_way1,icache_way2, icache_way3);
+ if (icache_way0)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX0 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX0 );
+ if (icache_way1)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX1 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX1 );
+ if (icache_way2)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX2 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX2 );
+ if (icache_way3)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX3 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX3 );
+ }
+
+ if ( !vpe_dcache_shared ) {
+ /*
+ * If any cache way is 1, then that way is denied
+ * in VPE0. Otherwise assign that way to VPE0.
+ */
+ printk(KERN_DEBUG "dcache is split\n");
+ printk(KERN_DEBUG "dcache_way0=%d dcache_way1=%d dcache_way2=%d dcache_way3=%d\n",
+ dcache_way0, dcache_way1, dcache_way2, dcache_way3);
+ write_c0_vpeconf0((read_c0_vpeconf0()) & ~VPECONF0_DCS);
+ if (dcache_way0)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX0 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX0 );
+ if (dcache_way1)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX1 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX1 );
+ if (dcache_way2)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX2 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX2 );
+ if (dcache_way3)
+ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX3 );
+ else
+ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX3 );
+ }
+ }
+ }
+
+#endif /* endif CONFIG_IFX_VPE_CACHE_SPLIT */
+
probe_pcache();
setup_scache();

View File

@@ -0,0 +1,40 @@
--- /dev/null
+++ b/arch/mips/include/asm/mach-lantiq/pci.h
@@ -0,0 +1,14 @@
+/*
+ * lantiq SoCs specific PCI definitions
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef __ASM_MACH_LANTIQ_PCI_H
+#define __ASM_MACH_LANTIQ_PCI_H
+
+extern int (*ifxmips_pci_plat_dev_init)(struct pci_dev *dev);
+
+#endif
--- a/arch/mips/pci/pci-lantiq.c
+++ b/arch/mips/pci/pci-lantiq.c
@@ -68,6 +68,8 @@
u32 lq_pci_mapped_cfg;
+int (*lqpci_plat_dev_init)(struct pci_dev *dev) = NULL;
+
/* Since the PCI REQ pins can be reused for other functionality, make it possible
to exclude those from interpretation by the PCI controller */
static int lq_pci_req_mask = 0xf;
@@ -126,6 +128,10 @@
printk ("WARNING: invalid interrupt pin %d\n", pin);
return 1;
}
+
+ if (lqpci_plat_dev_init)
+ return lqpci_plat_dev_init(dev);
+
return 0;
}