diff options
Diffstat (limited to 'target/wag54g/patches/ar7.patch')
-rw-r--r-- | target/wag54g/patches/ar7.patch | 5087 |
1 files changed, 5087 insertions, 0 deletions
diff --git a/target/wag54g/patches/ar7.patch b/target/wag54g/patches/ar7.patch new file mode 100644 index 000000000..1398c5e71 --- /dev/null +++ b/target/wag54g/patches/ar7.patch @@ -0,0 +1,5087 @@ +diff -Nur linux-2.6.29.1.orig/arch/mips/ar7/clock.c linux-2.6.29.1/arch/mips/ar7/clock.c +--- linux-2.6.29.1.orig/arch/mips/ar7/clock.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar7/clock.c 2009-05-31 20:19:17.000000000 +0200 +@@ -0,0 +1,483 @@ ++/* ++ * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2007 Eugene Konev <ejka@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/delay.h> ++#include <asm/addrspace.h> ++#include <asm/io.h> ++#include <asm/mach-ar7/ar7.h> ++ ++#define BOOT_PLL_SOURCE_MASK 0x3 ++#define CPU_PLL_SOURCE_SHIFT 16 ++#define BUS_PLL_SOURCE_SHIFT 14 ++#define USB_PLL_SOURCE_SHIFT 18 ++#define DSP_PLL_SOURCE_SHIFT 22 ++#define BOOT_PLL_SOURCE_AFE 0 ++#define BOOT_PLL_SOURCE_BUS 0 ++#define BOOT_PLL_SOURCE_REF 1 ++#define BOOT_PLL_SOURCE_XTAL 2 ++#define BOOT_PLL_SOURCE_CPU 3 ++#define BOOT_PLL_BYPASS 0x00000020 ++#define BOOT_PLL_ASYNC_MODE 0x02000000 ++#define BOOT_PLL_2TO1_MODE 0x00008000 ++ ++#define TNETD7200_CLOCK_ID_CPU 0 ++#define TNETD7200_CLOCK_ID_DSP 1 ++#define TNETD7200_CLOCK_ID_USB 2 ++ ++#define TNETD7200_DEF_CPU_CLK 211000000 ++#define TNETD7200_DEF_DSP_CLK 125000000 ++#define TNETD7200_DEF_USB_CLK 48000000 ++ ++struct tnetd7300_clock { ++ u32 ctrl; ++#define PREDIV_MASK 0x001f0000 ++#define PREDIV_SHIFT 16 ++#define POSTDIV_MASK 0x0000001f ++ u32 unused1[3]; ++ u32 pll; ++#define MUL_MASK 0x0000f000 ++#define MUL_SHIFT 12 ++#define PLL_MODE_MASK 0x00000001 ++#define PLL_NDIV 0x00000800 ++#define PLL_DIV 0x00000002 ++#define PLL_STATUS 0x00000001 ++ u32 unused2[3]; ++}; ++ ++struct tnetd7300_clocks { ++ struct tnetd7300_clock bus; ++ struct tnetd7300_clock cpu; ++ struct tnetd7300_clock usb; ++ struct tnetd7300_clock dsp; ++}; ++ ++struct tnetd7200_clock { ++ u32 ctrl; ++ u32 unused1[3]; ++#define DIVISOR_ENABLE_MASK 0x00008000 ++ u32 mul; ++ u32 prediv; ++ u32 postdiv; ++ u32 postdiv2; ++ u32 unused2[6]; ++ u32 cmd; ++ u32 status; ++ u32 cmden; ++ u32 padding[15]; ++}; ++ ++struct tnetd7200_clocks { ++ struct tnetd7200_clock cpu; ++ struct tnetd7200_clock dsp; ++ struct tnetd7200_clock usb; ++}; ++ ++int ar7_cpu_clock = 150000000; ++EXPORT_SYMBOL(ar7_cpu_clock); ++int ar7_bus_clock = 125000000; ++EXPORT_SYMBOL(ar7_bus_clock); ++int ar7_dsp_clock; ++EXPORT_SYMBOL(ar7_dsp_clock); ++ ++static int gcd(int a, int b) ++{ ++ int c; ++ ++ if (a < b) { ++ c = a; ++ a = b; ++ b = c; ++ } ++ while ((c = (a % b))) { ++ a = b; ++ b = c; ++ } ++ return b; ++} ++ ++static void approximate(int base, int target, int *prediv, ++ int *postdiv, int *mul) ++{ ++ int i, j, k, freq, res = target; ++ for (i = 1; i <= 16; i++) ++ for (j = 1; j <= 32; j++) ++ for (k = 1; k <= 32; k++) { ++ freq = abs(base / j * i / k - target); ++ if (freq < res) { ++ res = freq; ++ *mul = i; ++ *prediv = j; ++ *postdiv = k; ++ } ++ } ++} ++ ++static void calculate(int base, int target, int *prediv, int *postdiv, ++ int *mul) ++{ ++ int tmp_gcd, tmp_base, tmp_freq; ++ ++ for (*prediv = 1; *prediv <= 32; (*prediv)++) { ++ tmp_base = base / *prediv; ++ tmp_gcd = gcd(target, tmp_base); ++ *mul = target / tmp_gcd; ++ *postdiv = tmp_base / tmp_gcd; ++ if ((*mul < 1) || (*mul >= 16)) ++ continue; ++ if ((*postdiv > 0) & (*postdiv <= 32)) ++ break; ++ } ++ ++ if (base / (*prediv) * (*mul) / (*postdiv) != target) { ++ approximate(base, target, prediv, postdiv, mul); ++ tmp_freq = base / (*prediv) * (*mul) / (*postdiv); ++ printk(KERN_WARNING ++ "Adjusted requested frequency %d to %d\n", ++ target, tmp_freq); ++ } ++ ++ printk(KERN_DEBUG "Clocks: prediv: %d, postdiv: %d, mul: %d\n", ++ *prediv, *postdiv, *mul); ++} ++ ++static int tnetd7300_dsp_clock(void) ++{ ++ u32 didr1, didr2; ++ u8 rev = ar7_chip_rev(); ++ didr1 = readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x18)); ++ didr2 = readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x1c)); ++ if (didr2 & (1 << 23)) ++ return 0; ++ if ((rev >= 0x23) && (rev != 0x57)) ++ return 250000000; ++ if ((((didr2 & 0x1fff) << 10) | ((didr1 & 0xffc00000) >> 22)) ++ > 4208000) ++ return 250000000; ++ return 0; ++} ++ ++static int tnetd7300_get_clock(u32 shift, struct tnetd7300_clock *clock, ++ u32 *bootcr, u32 bus_clock) ++{ ++ int product; ++ int base_clock = AR7_REF_CLOCK; ++ u32 ctrl = readl(&clock->ctrl); ++ u32 pll = readl(&clock->pll); ++ int prediv = ((ctrl & PREDIV_MASK) >> PREDIV_SHIFT) + 1; ++ int postdiv = (ctrl & POSTDIV_MASK) + 1; ++ int divisor = prediv * postdiv; ++ int mul = ((pll & MUL_MASK) >> MUL_SHIFT) + 1; ++ ++ switch ((*bootcr & (BOOT_PLL_SOURCE_MASK << shift)) >> shift) { ++ case BOOT_PLL_SOURCE_BUS: ++ base_clock = bus_clock; ++ break; ++ case BOOT_PLL_SOURCE_REF: ++ base_clock = AR7_REF_CLOCK; ++ break; ++ case BOOT_PLL_SOURCE_XTAL: ++ base_clock = AR7_XTAL_CLOCK; ++ break; ++ case BOOT_PLL_SOURCE_CPU: ++ base_clock = ar7_cpu_clock; ++ break; ++ } ++ ++ if (*bootcr & BOOT_PLL_BYPASS) ++ return base_clock / divisor; ++ ++ if ((pll & PLL_MODE_MASK) == 0) ++ return (base_clock >> (mul / 16 + 1)) / divisor; ++ ++ if ((pll & (PLL_NDIV | PLL_DIV)) == (PLL_NDIV | PLL_DIV)) { ++ product = (mul & 1) ? ++ (base_clock * mul) >> 1 : ++ (base_clock * (mul - 1)) >> 2; ++ return product / divisor; ++ } ++ ++ if (mul == 16) ++ return base_clock / divisor; ++ ++ return base_clock * mul / divisor; ++} ++ ++static void tnetd7300_set_clock(u32 shift, struct tnetd7300_clock *clock, ++ u32 *bootcr, u32 frequency) ++{ ++ int prediv, postdiv, mul; ++ int base_clock = ar7_bus_clock; ++ ++ switch ((*bootcr & (BOOT_PLL_SOURCE_MASK << shift)) >> shift) { ++ case BOOT_PLL_SOURCE_BUS: ++ base_clock = ar7_bus_clock; ++ break; ++ case BOOT_PLL_SOURCE_REF: ++ base_clock = AR7_REF_CLOCK; ++ break; ++ case BOOT_PLL_SOURCE_XTAL: ++ base_clock = AR7_XTAL_CLOCK; ++ break; ++ case BOOT_PLL_SOURCE_CPU: ++ base_clock = ar7_cpu_clock; ++ break; ++ } ++ ++ calculate(base_clock, frequency, &prediv, &postdiv, &mul); ++ ++ writel(((prediv - 1) << PREDIV_SHIFT) | (postdiv - 1), &clock->ctrl); ++ mdelay(1); ++ writel(4, &clock->pll); ++ while (readl(&clock->pll) & PLL_STATUS); ++ writel(((mul - 1) << MUL_SHIFT) | (0xff << 3) | 0x0e, &clock->pll); ++ mdelay(75); ++} ++ ++static void __init tnetd7300_init_clocks(void) ++{ ++ u32 *bootcr = (u32 *)ioremap_nocache(AR7_REGS_DCL, 4); ++ struct tnetd7300_clocks *clocks = ++ (struct tnetd7300_clocks *) ++ ioremap_nocache(AR7_REGS_POWER + 0x20, ++ sizeof(struct tnetd7300_clocks)); ++ ++ ar7_bus_clock = tnetd7300_get_clock(BUS_PLL_SOURCE_SHIFT, ++ &clocks->bus, bootcr, AR7_AFE_CLOCK); ++ ++ if (*bootcr & BOOT_PLL_ASYNC_MODE) ++ ar7_cpu_clock = tnetd7300_get_clock(CPU_PLL_SOURCE_SHIFT, ++ &clocks->cpu, bootcr, AR7_AFE_CLOCK); ++ else ++ ar7_cpu_clock = ar7_bus_clock; ++/* ++ tnetd7300_set_clock(USB_PLL_SOURCE_SHIFT, &clocks->usb, ++ bootcr, 48000000); ++*/ ++ if (ar7_dsp_clock == 250000000) ++ tnetd7300_set_clock(DSP_PLL_SOURCE_SHIFT, &clocks->dsp, ++ bootcr, ar7_dsp_clock); ++ ++ iounmap(clocks); ++ iounmap(bootcr); ++} ++ ++static int tnetd7200_get_clock(int base, struct tnetd7200_clock *clock, ++ u32 *bootcr, u32 bus_clock) ++{ ++ int divisor = ((readl(&clock->prediv) & 0x1f) + 1) * ++ ((readl(&clock->postdiv) & 0x1f) + 1); ++ ++ if (*bootcr & BOOT_PLL_BYPASS) ++ return base / divisor; ++ ++ return base * ((readl(&clock->mul) & 0xf) + 1) / divisor; ++} ++ ++ ++static void tnetd7200_set_clock(int base, struct tnetd7200_clock *clock, ++ int prediv, int postdiv, int postdiv2, int mul, u32 frequency) ++{ ++ printk(KERN_INFO ++ "Clocks: base = %d, frequency = %u, prediv = %d, " ++ "postdiv = %d, postdiv2 = %d, mul = %d\n", ++ base, frequency, prediv, postdiv, postdiv2, mul); ++ ++ writel(0, &clock->ctrl); ++ writel(DIVISOR_ENABLE_MASK | ((prediv - 1) & 0x1F), &clock->prediv); ++ writel((mul - 1) & 0xF, &clock->mul); ++ ++ for (mul = 0; mul < 2000; mul++) /* nop */; ++ ++ while (readl(&clock->status) & 0x1) /* nop */; ++ ++ writel(DIVISOR_ENABLE_MASK | ((postdiv - 1) & 0x1F), &clock->postdiv); ++ ++ writel(readl(&clock->cmden) | 1, &clock->cmden); ++ writel(readl(&clock->cmd) | 1, &clock->cmd); ++ ++ while (readl(&clock->status) & 0x1) /* nop */; ++ ++ writel(DIVISOR_ENABLE_MASK | ((postdiv2 - 1) & 0x1F), &clock->postdiv2); ++ ++ writel(readl(&clock->cmden) | 1, &clock->cmden); ++ writel(readl(&clock->cmd) | 1, &clock->cmd); ++ ++ while (readl(&clock->status) & 0x1) /* nop */; ++ ++ writel(readl(&clock->ctrl) | 1, &clock->ctrl); ++} ++ ++static int tnetd7200_get_clock_base(int clock_id, u32 *bootcr) ++{ ++ if (*bootcr & BOOT_PLL_ASYNC_MODE) ++ /* Async */ ++ switch (clock_id) { ++ case TNETD7200_CLOCK_ID_DSP: ++ return AR7_REF_CLOCK; ++ default: ++ return AR7_AFE_CLOCK; ++ } ++ else ++ /* Sync */ ++ if (*bootcr & BOOT_PLL_2TO1_MODE) ++ /* 2:1 */ ++ switch (clock_id) { ++ case TNETD7200_CLOCK_ID_DSP: ++ return AR7_REF_CLOCK; ++ default: ++ return AR7_AFE_CLOCK; ++ } ++ else ++ /* 1:1 */ ++ return AR7_REF_CLOCK; ++} ++ ++ ++static void __init tnetd7200_init_clocks(void) ++{ ++ u32 *bootcr = (u32 *)ioremap_nocache(AR7_REGS_DCL, 4); ++ struct tnetd7200_clocks *clocks = ++ (struct tnetd7200_clocks *) ++ ioremap_nocache(AR7_REGS_POWER + 0x80, ++ sizeof(struct tnetd7200_clocks)); ++ int cpu_base, cpu_mul, cpu_prediv, cpu_postdiv; ++ int dsp_base, dsp_mul, dsp_prediv, dsp_postdiv; ++ int usb_base, usb_mul, usb_prediv, usb_postdiv; ++ ++/* ++ Log from Fritz!Box 7170 Annex B: ++ ++ CPU revision is: 00018448 ++ Clocks: Async mode ++ Clocks: Setting DSP clock ++ Clocks: prediv: 1, postdiv: 1, mul: 5 ++ Clocks: base = 25000000, frequency = 125000000, prediv = 1, ++ postdiv = 2, postdiv2 = 1, mul = 10 ++ Clocks: Setting CPU clock ++ Adjusted requested frequency 211000000 to 211968000 ++ Clocks: prediv: 1, postdiv: 1, mul: 6 ++ Clocks: base = 35328000, frequency = 211968000, prediv = 1, ++ postdiv = 1, postdiv2 = -1, mul = 6 ++ Clocks: Setting USB clock ++ Adjusted requested frequency 48000000 to 48076920 ++ Clocks: prediv: 13, postdiv: 1, mul: 5 ++ Clocks: base = 125000000, frequency = 48000000, prediv = 13, ++ postdiv = 1, postdiv2 = -1, mul = 5 ++ ++ DSL didn't work if you didn't set the postdiv 2:1 postdiv2 combination, ++ driver hung on startup. ++ Haven't tested this on a synchronous board, ++ neither do i know what to do with ar7_dsp_clock ++*/ ++ ++ cpu_base = tnetd7200_get_clock_base(TNETD7200_CLOCK_ID_CPU, bootcr); ++ dsp_base = tnetd7200_get_clock_base(TNETD7200_CLOCK_ID_DSP, bootcr); ++ ++ if (*bootcr & BOOT_PLL_ASYNC_MODE) { ++ printk(KERN_INFO "Clocks: Async mode\n"); ++ ++ printk(KERN_INFO "Clocks: Setting DSP clock\n"); ++ calculate(dsp_base, TNETD7200_DEF_DSP_CLK, ++ &dsp_prediv, &dsp_postdiv, &dsp_mul); ++ ar7_bus_clock = ++ ((dsp_base / dsp_prediv) * dsp_mul) / dsp_postdiv; ++ tnetd7200_set_clock(dsp_base, &clocks->dsp, ++ dsp_prediv, dsp_postdiv * 2, dsp_postdiv, dsp_mul * 2, ++ ar7_bus_clock); ++ ++ printk(KERN_INFO "Clocks: Setting CPU clock\n"); ++ calculate(cpu_base, TNETD7200_DEF_CPU_CLK, &cpu_prediv, ++ &cpu_postdiv, &cpu_mul); ++ ar7_cpu_clock = ++ ((cpu_base / cpu_prediv) * cpu_mul) / cpu_postdiv; ++ tnetd7200_set_clock(cpu_base, &clocks->cpu, ++ cpu_prediv, cpu_postdiv, -1, cpu_mul, ++ ar7_cpu_clock); ++ ++ } else ++ if (*bootcr & BOOT_PLL_2TO1_MODE) { ++ printk(KERN_INFO "Clocks: Sync 2:1 mode\n"); ++ ++ printk(KERN_INFO "Clocks: Setting CPU clock\n"); ++ calculate(cpu_base, TNETD7200_DEF_CPU_CLK, &cpu_prediv, ++ &cpu_postdiv, &cpu_mul); ++ ar7_cpu_clock = ((cpu_base / cpu_prediv) * cpu_mul) ++ / cpu_postdiv; ++ tnetd7200_set_clock(cpu_base, &clocks->cpu, ++ cpu_prediv, cpu_postdiv, -1, cpu_mul, ++ ar7_cpu_clock); ++ ++ printk(KERN_INFO "Clocks: Setting DSP clock\n"); ++ calculate(dsp_base, TNETD7200_DEF_DSP_CLK, &dsp_prediv, ++ &dsp_postdiv, &dsp_mul); ++ ar7_bus_clock = ar7_cpu_clock / 2; ++ tnetd7200_set_clock(dsp_base, &clocks->dsp, ++ dsp_prediv, dsp_postdiv * 2, dsp_postdiv, ++ dsp_mul * 2, ar7_bus_clock); ++ } else { ++ printk(KERN_INFO "Clocks: Sync 1:1 mode\n"); ++ ++ printk(KERN_INFO "Clocks: Setting DSP clock\n"); ++ calculate(dsp_base, TNETD7200_DEF_DSP_CLK, &dsp_prediv, ++ &dsp_postdiv, &dsp_mul); ++ ar7_bus_clock = ((dsp_base / dsp_prediv) * dsp_mul) ++ / dsp_postdiv; ++ tnetd7200_set_clock(dsp_base, &clocks->dsp, ++ dsp_prediv, dsp_postdiv * 2, dsp_postdiv, ++ dsp_mul * 2, ar7_bus_clock); ++ ++ ar7_cpu_clock = ar7_bus_clock; ++ } ++ ++ printk(KERN_INFO "Clocks: Setting USB clock\n"); ++ usb_base = ar7_bus_clock; ++ calculate(usb_base, TNETD7200_DEF_USB_CLK, &usb_prediv, ++ &usb_postdiv, &usb_mul); ++ tnetd7200_set_clock(usb_base, &clocks->usb, ++ usb_prediv, usb_postdiv, -1, usb_mul, ++ TNETD7200_DEF_USB_CLK); ++ ++ #warning FIXME ++ ar7_dsp_clock = ar7_cpu_clock; ++ ++ iounmap(clocks); ++ iounmap(bootcr); ++} ++ ++void __init ar7_init_clocks(void) ++{ ++ switch (ar7_chip_id()) { ++ case AR7_CHIP_7100: ++#warning FIXME: Check if the new 7200 clock init works for 7100 ++ tnetd7200_init_clocks(); ++ break; ++ case AR7_CHIP_7200: ++ tnetd7200_init_clocks(); ++ break; ++ case AR7_CHIP_7300: ++ ar7_dsp_clock = tnetd7300_dsp_clock(); ++ tnetd7300_init_clocks(); ++ break; ++ default: ++ break; ++ } ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar7/gpio.c linux-2.6.29.1/arch/mips/ar7/gpio.c +--- linux-2.6.29.1.orig/arch/mips/ar7/gpio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar7/gpio.c 2009-05-31 20:19:27.000000000 +0200 +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2007 Eugene Konev <ejka@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include <linux/module.h> ++ ++#include <asm/mach-ar7/gpio.h> ++ ++static const char *ar7_gpio_list[AR7_GPIO_MAX]; ++ ++int gpio_request(unsigned gpio, const char *label) ++{ ++ if (gpio >= AR7_GPIO_MAX) ++ return -EINVAL; ++ ++ if (ar7_gpio_list[gpio]) ++ return -EBUSY; ++ ++ if (label) { ++ ar7_gpio_list[gpio] = label; ++ } else { ++ ar7_gpio_list[gpio] = "busy"; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(gpio_request); ++ ++void gpio_free(unsigned gpio) ++{ ++ BUG_ON(!ar7_gpio_list[gpio]); ++ ar7_gpio_list[gpio] = NULL; ++} ++EXPORT_SYMBOL(gpio_free); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar7/irq.c linux-2.6.29.1/arch/mips/ar7/irq.c +--- linux-2.6.29.1.orig/arch/mips/ar7/irq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar7/irq.c 2009-05-31 20:19:35.000000000 +0200 +@@ -0,0 +1,183 @@ ++/* ++ * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include <linux/interrupt.h> ++#include <linux/io.h> ++ ++#include <asm/irq_cpu.h> ++#include <asm/mipsregs.h> ++#include <asm/mach-ar7/ar7.h> ++ ++#define EXCEPT_OFFSET 0x80 ++#define PACE_OFFSET 0xA0 ++#define CHNLS_OFFSET 0x200 ++ ++#define REG_OFFSET(irq, reg) ((irq) / 32 * 0x4 + reg * 0x10) ++#define SEC_REG_OFFSET(reg) (EXCEPT_OFFSET + reg * 0x8) ++#define SEC_SR_OFFSET (SEC_REG_OFFSET(0)) /* 0x80 */ ++#define CR_OFFSET(irq) (REG_OFFSET(irq, 1)) /* 0x10 */ ++#define SEC_CR_OFFSET (SEC_REG_OFFSET(1)) /* 0x88 */ ++#define ESR_OFFSET(irq) (REG_OFFSET(irq, 2)) /* 0x20 */ ++#define SEC_ESR_OFFSET (SEC_REG_OFFSET(2)) /* 0x90 */ ++#define ECR_OFFSET(irq) (REG_OFFSET(irq, 3)) /* 0x30 */ ++#define SEC_ECR_OFFSET (SEC_REG_OFFSET(3)) /* 0x98 */ ++#define PIR_OFFSET (0x40) ++#define MSR_OFFSET (0x44) ++#define PM_OFFSET(irq) (REG_OFFSET(irq, 5)) /* 0x50 */ ++#define TM_OFFSET(irq) (REG_OFFSET(irq, 6)) /* 0x60 */ ++ ++#define REG(addr) ((u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr)) ++ ++#define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4)) ++ ++static void ar7_unmask_irq(unsigned int irq_nr); ++static void ar7_mask_irq(unsigned int irq_nr); ++static void ar7_ack_irq(unsigned int irq_nr); ++static void ar7_unmask_sec_irq(unsigned int irq_nr); ++static void ar7_mask_sec_irq(unsigned int irq_nr); ++static void ar7_ack_sec_irq(unsigned int irq_nr); ++static void ar7_cascade(void); ++static void ar7_irq_init(int base); ++static int ar7_irq_base; ++ ++static struct irq_chip ar7_irq_type = { ++ .name = "AR7", ++ .unmask = ar7_unmask_irq, ++ .mask = ar7_mask_irq, ++ .ack = ar7_ack_irq ++}; ++ ++static struct irq_chip ar7_sec_irq_type = { ++ .name = "AR7", ++ .unmask = ar7_unmask_sec_irq, ++ .mask = ar7_mask_sec_irq, ++ .ack = ar7_ack_sec_irq, ++}; ++ ++static struct irqaction ar7_cascade_action = { ++ .handler = no_action, ++ .name = "AR7 cascade interrupt" ++}; ++ ++static void ar7_unmask_irq(unsigned int irq) ++{ ++ writel(1 << ((irq - ar7_irq_base) % 32), ++ REG(ESR_OFFSET(irq - ar7_irq_base))); ++} ++ ++static void ar7_mask_irq(unsigned int irq) ++{ ++ writel(1 << ((irq - ar7_irq_base) % 32), ++ REG(ECR_OFFSET(irq - ar7_irq_base))); ++} ++ ++static void ar7_ack_irq(unsigned int irq) ++{ ++ writel(1 << ((irq - ar7_irq_base) % 32), ++ REG(CR_OFFSET(irq - ar7_irq_base))); ++} ++ ++static void ar7_unmask_sec_irq(unsigned int irq) ++{ ++ writel(1 << (irq - ar7_irq_base - 40), REG(SEC_ESR_OFFSET)); ++} ++ ++static void ar7_mask_sec_irq(unsigned int irq) ++{ ++ writel(1 << (irq - ar7_irq_base - 40), REG(SEC_ECR_OFFSET)); ++} ++ ++static void ar7_ack_sec_irq(unsigned int irq) ++{ ++ writel(1 << (irq - ar7_irq_base - 40), REG(SEC_CR_OFFSET)); ++} ++ ++void __init arch_init_irq(void) { ++ mips_cpu_irq_init(); ++ ar7_irq_init(8); ++} ++ ++static void __init ar7_irq_init(int base) ++{ ++ int i; ++ /* ++ * Disable interrupts and clear pending ++ */ ++ writel(0xffffffff, REG(ECR_OFFSET(0))); ++ writel(0xff, REG(ECR_OFFSET(32))); ++ writel(0xffffffff, REG(SEC_ECR_OFFSET)); ++ writel(0xffffffff, REG(CR_OFFSET(0))); ++ writel(0xff, REG(CR_OFFSET(32))); ++ writel(0xffffffff, REG(SEC_CR_OFFSET)); ++ ++ ar7_irq_base = base; ++ ++ for (i = 0; i < 40; i++) { ++ writel(i, REG(CHNL_OFFSET(i))); ++ /* Primary IRQ's */ ++ set_irq_chip_and_handler(base + i, &ar7_irq_type, ++ handle_level_irq); ++ /* Secondary IRQ's */ ++ if (i < 32) ++ set_irq_chip_and_handler(base + i + 40, ++ &ar7_sec_irq_type, ++ handle_level_irq); ++ } ++ ++ setup_irq(2, &ar7_cascade_action); ++ setup_irq(ar7_irq_base, &ar7_cascade_action); ++ set_c0_status(IE_IRQ0); ++} ++ ++static void ar7_cascade(void) ++{ ++ u32 status; ++ int i, irq; ++ ++ /* Primary IRQ's */ ++ irq = readl(REG(PIR_OFFSET)) & 0x3f; ++ if (irq) { ++ do_IRQ(ar7_irq_base + irq); ++ return; ++ } ++ ++ /* Secondary IRQ's are cascaded through primary '0' */ ++ writel(1, REG(CR_OFFSET(irq))); ++ status = readl(REG(SEC_SR_OFFSET)); ++ for (i = 0; i < 32; i++) { ++ if (status & 1) { ++ do_IRQ(ar7_irq_base + i + 40); ++ return; ++ } ++ status >>= 1; ++ } ++ ++ spurious_interrupt(); ++} ++ ++asmlinkage void plat_irq_dispatch(void) ++{ ++ unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; ++ if (pending & STATUSF_IP7) /* cpu timer */ ++ do_IRQ(7); ++ else if (pending & STATUSF_IP2) /* int0 hardware line */ ++ ar7_cascade(); ++ else ++ spurious_interrupt(); ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar7/Makefile linux-2.6.29.1/arch/mips/ar7/Makefile +--- linux-2.6.29.1.orig/arch/mips/ar7/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar7/Makefile 2009-05-31 19:50:05.000000000 +0200 +@@ -0,0 +1,10 @@ ++ ++obj-y := \ ++ prom.o \ ++ setup.o \ ++ memory.o \ ++ irq.o \ ++ time.o \ ++ platform.o \ ++ gpio.o \ ++ clock.o +diff -Nur linux-2.6.29.1.orig/arch/mips/ar7/memory.c linux-2.6.29.1/arch/mips/ar7/memory.c +--- linux-2.6.29.1.orig/arch/mips/ar7/memory.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar7/memory.c 2009-05-31 19:50:05.000000000 +0200 +@@ -0,0 +1,74 @@ ++/* ++ * Based on arch/mips/mm/init.c ++ * Copyright (C) 1994 - 2000 Ralf Baechle ++ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. ++ * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#include <linux/bootmem.h> ++#include <linux/init.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/pfn.h> ++#include <linux/proc_fs.h> ++#include <linux/string.h> ++#include <linux/swap.h> ++ ++#include <asm/bootinfo.h> ++#include <asm/page.h> ++#include <asm/sections.h> ++ ++#include <asm/mips-boards/prom.h> ++ ++static int __init memsize(void) ++{ ++ u32 size = (64 << 20); ++ u32 *addr = (u32 *)KSEG1ADDR(0x14000000 + size - 4); ++ u32 *kernel_end = (u32 *)KSEG1ADDR(CPHYSADDR((u32)&_end)); ++ u32 *tmpaddr = addr; ++ ++ while (tmpaddr > kernel_end) { ++ *tmpaddr = (u32)tmpaddr; ++ size >>= 1; ++ tmpaddr -= size >> 2; ++ } ++ ++ do { ++ tmpaddr += size >> 2; ++ if (*tmpaddr != (u32)tmpaddr) ++ break; ++ size <<= 1; ++ } while (size < (64 << 20)); ++ ++ writel(tmpaddr, &addr); ++ ++ return size; ++} ++ ++void __init prom_meminit(void) ++{ ++ unsigned long pages; ++ ++ pages = memsize() >> PAGE_SHIFT; ++ add_memory_region(PHYS_OFFSET, pages << PAGE_SHIFT, ++ BOOT_MEM_RAM); ++} ++ ++void __init prom_free_prom_memory(void) ++{ ++ return; ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar7/platform.c linux-2.6.29.1/arch/mips/ar7/platform.c +--- linux-2.6.29.1.orig/arch/mips/ar7/platform.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar7/platform.c 2009-06-01 13:34:18.000000000 +0200 +@@ -0,0 +1,535 @@ ++/* ++ * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include <linux/autoconf.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/platform_device.h> ++#include <linux/mtd/physmap.h> ++#include <linux/serial.h> ++#include <linux/serial_8250.h> ++#include <linux/ioport.h> ++#include <linux/io.h> ++#include <linux/version.h> ++#include <linux/vlynq.h> ++#include <linux/leds.h> ++#include <linux/string.h> ++ ++#include <asm/addrspace.h> ++#include <asm/mach-ar7/ar7.h> ++#include <asm/mach-ar7/gpio.h> ++#include <asm/mach-ar7/prom.h> ++ ++struct plat_vlynq_data { ++ struct plat_vlynq_ops ops; ++ int gpio_bit; ++ int reset_bit; ++}; ++ ++ ++static int vlynq_on(struct vlynq_device *dev) ++{ ++ int result; ++ struct plat_vlynq_data *pdata = dev->dev.platform_data; ++ ++ if ((result = gpio_request(pdata->gpio_bit, "vlynq"))) ++ goto out; ++ ++ ar7_device_reset(pdata->reset_bit); ++ ++ if ((result = ar7_gpio_disable(pdata->gpio_bit))) ++ goto out_enabled; ++ ++ if ((result = ar7_gpio_enable(pdata->gpio_bit))) ++ goto out_enabled; ++ ++ if ((result = gpio_direction_output(pdata->gpio_bit, 0))) ++ goto out_gpio_enabled; ++ ++ mdelay(50); ++ ++ gpio_set_value(pdata->gpio_bit, 1); ++ mdelay(50); ++ ++ return 0; ++ ++out_gpio_enabled: ++ ar7_gpio_disable(pdata->gpio_bit); ++out_enabled: ++ ar7_device_disable(pdata->reset_bit); ++ gpio_free(pdata->gpio_bit); ++out: ++ return result; ++} ++ ++static void vlynq_off(struct vlynq_device *dev) ++{ ++ struct plat_vlynq_data *pdata = dev->dev.platform_data; ++ ar7_gpio_disable(pdata->gpio_bit); ++ gpio_free(pdata->gpio_bit); ++ ar7_device_disable(pdata->reset_bit); ++} ++ ++static struct resource physmap_flash_resource = { ++ .name = "mem", ++ .flags = IORESOURCE_MEM, ++ .start = 0x10000000, ++ .end = 0x107fffff, ++}; ++ ++static struct resource cpmac_low_res[] = { ++ { ++ .name = "regs", ++ .flags = IORESOURCE_MEM, ++ .start = AR7_REGS_MAC0, ++ .end = AR7_REGS_MAC0 + 0x7ff, ++ }, ++ { ++ .name = "irq", ++ .flags = IORESOURCE_IRQ, ++ .start = 27, ++ .end = 27, ++ }, ++}; ++ ++static struct resource cpmac_high_res[] = { ++ { ++ .name = "regs", ++ .flags = IORESOURCE_MEM, ++ .start = AR7_REGS_MAC1, ++ .end = AR7_REGS_MAC1 + 0x7ff, ++ }, ++ { ++ .name = "irq", ++ .flags = IORESOURCE_IRQ, ++ .start = 41, ++ .end = 41, ++ }, ++}; ++ ++static struct resource vlynq_low_res[] = { ++ { ++ .name = "regs", ++ .flags = IORESOURCE_MEM, ++ .start = AR7_REGS_VLYNQ0, ++ .end = AR7_REGS_VLYNQ0 + 0xff, ++ }, ++ { ++ .name = "irq", ++ .flags = IORESOURCE_IRQ, ++ .start = 29, ++ .end = 29, ++ }, ++ { ++ .name = "mem", ++ .flags = IORESOURCE_MEM, ++ .start = 0x04000000, ++ .end = 0x04ffffff, ++ }, ++ { ++ .name = "devirq", ++ .flags = IORESOURCE_IRQ, ++ .start = 80, ++ .end = 111, ++ }, ++}; ++ ++static struct resource vlynq_high_res[] = { ++ { ++ .name = "regs", ++ .flags = IORESOURCE_MEM, ++ .start = AR7_REGS_VLYNQ1, ++ .end = AR7_REGS_VLYNQ1 + 0xff, ++ }, ++ { ++ .name = "irq", ++ .flags = IORESOURCE_IRQ, ++ .start = 33, ++ .end = 33, ++ }, ++ { ++ .name = "mem", ++ .flags = IORESOURCE_MEM, ++ .start = 0x0c000000, ++ .end = 0x0cffffff, ++ }, ++ { ++ .name = "devirq", ++ .flags = IORESOURCE_IRQ, ++ .start = 112, ++ .end = 143, ++ }, ++}; ++ ++static struct resource usb_res[] = { ++ { ++ .name = "regs", ++ .flags = IORESOURCE_MEM, ++ .start = AR7_REGS_USB, ++ .end = AR7_REGS_USB + 0xff, ++ }, ++ { ++ .name = "irq", ++ .flags = IORESOURCE_IRQ, ++ .start = 32, ++ .end = 32, ++ }, ++ { ++ .name = "mem", ++ .flags = IORESOURCE_MEM, ++ .start = 0x03400000, ++ .end = 0x034001fff, ++ }, ++}; ++ ++static struct physmap_flash_data physmap_flash_data = { ++ .width = 2, ++}; ++ ++static struct plat_cpmac_data cpmac_low_data = { ++ .reset_bit = 17, ++ .power_bit = 20, ++ .phy_mask = 0x80000000, ++}; ++ ++static struct plat_cpmac_data cpmac_high_data = { ++ .reset_bit = 21, ++ .power_bit = 22, ++ .phy_mask = 0x7fffffff, ++}; ++ ++static struct plat_vlynq_data vlynq_low_data = { ++ .ops.on = vlynq_on, ++ .ops.off = vlynq_off, ++ .reset_bit = 20, ++ .gpio_bit = 18, ++}; ++ ++static struct plat_vlynq_data vlynq_high_data = { ++ .ops.on = vlynq_on, ++ .ops.off = vlynq_off, ++ .reset_bit = 16, ++ .gpio_bit = 19, ++}; ++ ++static struct platform_device physmap_flash = { ++ .id = 0, ++ .name = "physmap-flash", ++ .dev.platform_data = &physmap_flash_data, ++ .resource = &physmap_flash_resource, ++ .num_resources = 1, ++}; ++ ++static u64 cpmac_dma_mask = DMA_32BIT_MASK; ++static struct platform_device cpmac_low = { ++ .id = 0, ++ .name = "cpmac", ++ .dev = { ++ .dma_mask = &cpmac_dma_mask, ++ .coherent_dma_mask = DMA_32BIT_MASK, ++ .platform_data = &cpmac_low_data, ++ }, ++ .resource = cpmac_low_res, ++ .num_resources = ARRAY_SIZE(cpmac_low_res), ++}; ++ ++static struct platform_device cpmac_high = { ++ .id = 1, ++ .name = "cpmac", ++ .dev = { ++ .dma_mask = &cpmac_dma_mask, ++ .coherent_dma_mask = DMA_32BIT_MASK, ++ .platform_data = &cpmac_high_data, ++ }, ++ .resource = cpmac_high_res, ++ .num_resources = ARRAY_SIZE(cpmac_high_res), ++}; ++ ++static struct platform_device vlynq_low = { ++ .id = 0, ++ .name = "vlynq", ++ .dev.platform_data = &vlynq_low_data, ++ .resource = vlynq_low_res, ++ .num_resources = ARRAY_SIZE(vlynq_low_res), ++}; ++ ++static struct platform_device vlynq_high = { ++ .id = 1, ++ .name = "vlynq", ++ .dev.platform_data = &vlynq_high_data, ++ .resource = vlynq_high_res, ++ .num_resources = ARRAY_SIZE(vlynq_high_res), ++}; ++ ++ ++/* This is proper way to define uart ports, but they are then detected ++ * as xscale and, obviously, don't work... ++ */ ++#if !defined(CONFIG_SERIAL_8250) ++ ++static struct plat_serial8250_port uart0_data = { ++ .mapbase = AR7_REGS_UART0, ++ .irq = AR7_IRQ_UART0, ++ .regshift = 2, ++ .iotype = UPIO_MEM, ++ .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, ++}; ++ ++static struct plat_serial8250_port uart1_data = { ++ .mapbase = UR8_REGS_UART1, ++ .irq = AR7_IRQ_UART1, ++ .regshift = 2, ++ .iotype = UPIO_MEM, ++ .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, ++}; ++ ++static struct plat_serial8250_port uart_data[] = { ++ uart0_data, ++ uart1_data, ++ { .flags = 0 } ++}; ++ ++static struct plat_serial8250_port uart_data_single[] = { ++ uart0_data, ++ { .flags = 0 } ++}; ++ ++static struct platform_device uart = { ++ .id = 0, ++ .name = "serial8250", ++ .dev.platform_data = uart_data_single ++}; ++#endif ++ ++static struct gpio_led default_leds[] = { ++ { .name = "status", .gpio = 8, .active_low = 1, }, ++}; ++ ++static struct gpio_led dsl502t_leds[] = { ++ { .name = "status", .gpio = 9, .active_low = 1, }, ++ { .name = "ethernet", .gpio = 7, .active_low = 1, }, ++ { .name = "usb", .gpio = 12, .active_low = 1, }, ++}; ++ ++static struct gpio_led dg834g_leds[] = { ++ { .name = "ppp", .gpio = 6, .active_low = 1, }, ++ { .name = "status", .gpio = 7, .active_low = 1, }, ++ { .name = "adsl", .gpio = 8, .active_low = 1, }, ++ { .name = "wifi", .gpio = 12, .active_low = 1, }, ++ { .name = "power", .gpio = 14, .active_low = 1, .default_tr |