* [PATCH][MIPS][1/7] AR7: core support
[not found] <200709080143.12345.technoboy85@gmail.com>
@ 2007-09-08 0:18 ` Matteo Croce
2007-09-08 17:40 ` Atsushi Nemoto
2007-09-08 0:19 ` Matteo Croce
` (5 subsequent siblings)
6 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:18 UTC (permalink / raw)
To: linux-mips
Cc: Florian Fainelli, Felix Fietkau, Eugene Konev, Nicolas Thill,
ralf, openwrt-devel, Andrew Morton
Support for memory mapping, clock and the vlynq bus
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 4a54d21..1bc65f0 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -15,6 +15,20 @@ choice
prompt "System type"
default SGI_IP22
+config AR7
+ bool "Texas Instruments AR7"
+ select BOOT_ELF32
+ select DMA_NONCOHERENT
+ select HW_HAS_PCI
+ select IRQ_CPU
+ select SWAP_IO_SPACE
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_HAS_EARLY_PRINTK
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_KGDB
+ select SYS_SUPPORTS_LITTLE_ENDIAN
+ select GENERIC_GPIO
+
config MACH_ALCHEMY
bool "Alchemy processor based machines"
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 32c1c8f..3989a8b 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -161,6 +161,13 @@ libs-$(CONFIG_SIBYTE_CFE) += arch/mips/sibyte/cfe/
#
#
+# Texas Instruments AR7
+#
+core-$(CONFIG_AR7) += arch/mips/ar7/
+cflags-$(CONFIG_AR7) += -Iinclude/asm-mips/ar7
+load-$(CONFIG_AR7) += 0xffffffff94100000
+
+#
# Acer PICA 61, Mips Magnum 4000 and Olivetti M700.
#
core-$(CONFIG_MACH_JAZZ) += arch/mips/jazz/
diff --git a/arch/mips/ar7/Makefile b/arch/mips/ar7/Makefile
new file mode 100644
index 0000000..e6ba02c
--- /dev/null
+++ b/arch/mips/ar7/Makefile
@@ -0,0 +1,13 @@
+
+obj-y := \
+ prom.o \
+ setup.o \
+ memory.o \
+ irq.o \
+ time.o \
+ platform.o \
+ gpio.o \
+ clock.o \
+ vlynq.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/ar7/clock.c b/arch/mips/ar7/clock.c
new file mode 100644
index 0000000..58f7a78
--- /dev/null
+++ b/arch/mips/ar7/clock.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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/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 {
+ volatile u32 ctrl;
+#define PREDIV_MASK 0x001f0000
+#define PREDIV_SHIFT 16
+#define POSTDIV_MASK 0x0000001f
+ u32 unused1[3];
+ volatile 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];
+} __attribute__ ((packed));
+
+struct tnetd7300_clocks {
+ struct tnetd7300_clock bus;
+ struct tnetd7300_clock cpu;
+ struct tnetd7300_clock usb;
+ struct tnetd7300_clock dsp;
+} __attribute__ ((packed));
+
+struct tnetd7200_clock {
+ volatile u32 ctrl;
+ u32 unused1[3];
+#define DIVISOR_ENABLE_MASK 0x00008000
+ volatile u32 mul;
+ volatile u32 prediv;
+ volatile u32 postdiv;
+ volatile u32 postdiv2;
+ u32 unused2[6];
+ volatile u32 cmd;
+ volatile u32 status;
+ volatile u32 cmden;
+ u32 padding[15];
+} __attribute__ ((packed));
+
+struct tnetd7200_clocks {
+ struct tnetd7200_clock cpu;
+ struct tnetd7200_clock dsp;
+ struct tnetd7200_clock usb;
+} __attribute__ ((packed));
+
+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 = clock->ctrl;
+ u32 pll = 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)
+{
+ u32 status;
+ 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);
+
+ clock->ctrl = ((prediv - 1) << PREDIV_SHIFT) | (postdiv - 1);
+ mdelay(1);
+ clock->pll = 4;
+ do {
+ status = clock->pll;
+ } while (status & PLL_STATUS);
+ clock->pll = ((mul - 1) << MUL_SHIFT) | (0xff << 3) | 0x0e;
+ 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 = ((clock->prediv & 0x1f) + 1) *
+ ((clock->postdiv & 0x1f) + 1);
+
+ if (*bootcr & BOOT_PLL_BYPASS)
+ return base / divisor;
+
+ return base * ((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);
+
+ clock->ctrl = 0;
+ clock->prediv = DIVISOR_ENABLE_MASK | ((prediv - 1) & 0x1F);
+ clock->mul = ((mul - 1) & 0xF);
+
+ for (mul = 0; mul < 2000; mul++) /* nop */;
+
+ while (clock->status & 0x1) /* nop */;
+
+ clock->postdiv = DIVISOR_ENABLE_MASK | ((postdiv - 1) & 0x1F);
+
+ clock->cmden |= 1;
+ clock->cmd |= 1;
+
+ while (clock->status & 0x1) /* nop */;
+
+ clock->postdiv2 = DIVISOR_ENABLE_MASK | ((postdiv2 - 1) & 0x1F);
+
+ clock->cmden |= 1;
+ clock->cmd |= 1;
+
+ while (clock->status & 0x1) /* nop */;
+
+ clock->ctrl |= 1;
+}
+
+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_CPU_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 --git a/arch/mips/ar7/gpio.c b/arch/mips/ar7/gpio.c
new file mode 100644
index 0000000..bcb2b17
--- /dev/null
+++ b/arch/mips/ar7/gpio.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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 <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/addrspace.h>
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/gpio.h>
+
+static char *ar7_gpio_list[AR7_GPIO_MAX] = { 0, };
+
+int gpio_request(unsigned gpio, 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 --git a/arch/mips/ar7/irq.c b/arch/mips/ar7/irq.c
new file mode 100644
index 0000000..f131301
--- /dev/null
+++ b/arch/mips/ar7/irq.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2006, 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/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) (*(volatile 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_unmask_secondary_irq(unsigned int irq_nr);
+static void ar7_mask_secondary_irq(unsigned int irq_nr);
+static irqreturn_t ar7_cascade(int interrupt, void *dev);
+static irqreturn_t ar7_secondary_cascade(int interrupt, void *dev);
+static void ar7_irq_init(int base);
+static int ar7_irq_base;
+
+static struct irq_chip ar7_irq_type = {
+ .typename = "AR7",
+ .name = "AR7",
+ .unmask = ar7_unmask_irq,
+ .mask = ar7_mask_irq,
+};
+
+static struct irq_chip ar7_secondary_irq_type = {
+ .name = "AR7",
+ .unmask = ar7_unmask_secondary_irq,
+ .mask = ar7_mask_secondary_irq,
+};
+
+static struct irqaction ar7_cascade_action = {
+ .handler = ar7_cascade,
+ .name = "AR7 cascade interrupt"
+};
+
+static struct irqaction ar7_secondary_cascade_action = {
+ .handler = ar7_secondary_cascade,
+ .name = "AR7 secondary cascade interrupt"
+};
+
+static void ar7_unmask_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* enable the interrupt channel bit */
+ REG(ESR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
+ local_irq_restore(flags);
+}
+
+static void ar7_mask_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* disable the interrupt channel bit */
+ REG(ECR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
+ local_irq_restore(flags);
+}
+
+static void ar7_unmask_secondary_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* enable the interrupt channel bit */
+ REG(SEC_ESR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
+ local_irq_restore(flags);
+}
+
+static void ar7_mask_secondary_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* disable the interrupt channel bit */
+ REG(SEC_ECR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
+ local_irq_restore(flags);
+}
+
+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
+ */
+ REG(ECR_OFFSET(0)) = 0xffffffff;
+ REG(ECR_OFFSET(32)) = 0xff;
+ REG(SEC_ECR_OFFSET) = 0xffffffff;
+ REG(CR_OFFSET(0)) = 0xffffffff;
+ REG(CR_OFFSET(32)) = 0xff;
+ REG(SEC_CR_OFFSET) = 0xffffffff;
+
+ ar7_irq_base = base;
+
+ for (i = 0; i < 40; i++) {
+ REG(CHNL_OFFSET(i)) = i;
+ /* Primary IRQ's */
+ irq_desc[i + base].status = IRQ_DISABLED;
+ irq_desc[i + base].action = NULL;
+ irq_desc[i + base].depth = 1;
+ irq_desc[i + base].chip = &ar7_irq_type;
+ /* Secondary IRQ's */
+ if (i < 32) {
+ irq_desc[i + base + 40].status = IRQ_DISABLED;
+ irq_desc[i + base + 40].action = NULL;
+ irq_desc[i + base + 40].depth = 1;
+ irq_desc[i + base + 40].chip = &ar7_secondary_irq_type;
+ }
+ }
+
+ setup_irq(2, &ar7_cascade_action);
+ setup_irq(ar7_irq_base, &ar7_secondary_cascade_action);
+ set_c0_status(IE_IRQ0);
+}
+
+static irqreturn_t ar7_cascade(int interrupt, void *dev)
+{
+ int irq;
+
+ irq = (REG(PIR_OFFSET) & 0x3F);
+ REG(CR_OFFSET(irq)) = 1 << (irq % 32);
+
+ do_IRQ(irq + ar7_irq_base);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ar7_secondary_cascade(int interrupt, void *dev)
+{
+ int irq = 0, i;
+ unsigned long status;
+
+ status = REG(SEC_SR_OFFSET);
+ if (unlikely(!status)) {
+ spurious_interrupt();
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < 32; i++)
+ if (status & (i << 1)) {
+ irq = i + 40;
+ REG(SEC_CR_OFFSET) = 1 << i;
+ break;
+ }
+
+ do_IRQ(irq + ar7_irq_base);
+
+ return IRQ_HANDLED;
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int pending = read_c0_status() & read_c0_cause();
+ if (pending & STATUSF_IP7) /* cpu timer */
+ do_IRQ(7);
+ else if (pending & STATUSF_IP2) /* int0 hardware line */
+ do_IRQ(2);
+ else
+ spurious_interrupt();
+}
diff --git a/arch/mips/ar7/memory.c b/arch/mips/ar7/memory.c
new file mode 100644
index 0000000..58e2d8e
--- /dev/null
+++ b/arch/mips/ar7/memory.c
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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);
+ volatile u32 *addr = (u32 *)KSEG1ADDR(0x14000000 + size - 4);
+ u32 *kernel_end = (u32 *)KSEG1ADDR(CPHYSADDR((u32)&_end));
+
+ while (addr > kernel_end) {
+ *addr = (u32)addr;
+ size >>= 1;
+ addr -= size >> 2;
+ }
+
+ do {
+ addr += size >> 2;
+ if (*addr != (u32)addr)
+ break;
+ size <<= 1;
+ } while (size < (64 << 20));
+
+ return size;
+}
+
+void __init prom_meminit(void)
+{
+ unsigned long pages;
+
+ pages = memsize() >> PAGE_SHIFT;
+ add_memory_region(ARCH_PFN_OFFSET << PAGE_SHIFT, pages <<
+ PAGE_SHIFT, BOOT_MEM_RAM);
+}
+
+void __init prom_free_prom_memory(void)
+{
+ return;
+}
diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c
new file mode 100644
index 0000000..8706bff
--- /dev/null
+++ b/arch/mips/ar7/platform.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2006, 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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/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 <asm/addrspace.h>
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/gpio.h>
+#include <asm/ar7/prom.h>
+#include <asm/ar7/vlynq.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)))
+ goto out_gpio_enabled;
+
+ gpio_set_value(pdata->gpio_bit, 0);
+ 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 = 0x103fffff,
+};
+
+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 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 struct platform_device cpmac_low = {
+ .id = 0,
+ .name = "cpmac",
+ .dev.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.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 inline unsigned char char2hex(char h)
+{
+ switch (h) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return h - '0';
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return h - 'A' + 10;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return h - 'a' + 10;
+ default:
+ return 0;
+ }
+}
+
+static void cpmac_get_mac(int instance, unsigned char *dev_addr)
+{
+ int i;
+ char name[5], default_mac[] = "00:00:00:12:34:56", *mac;
+
+ mac = NULL;
+ sprintf(name, "mac%c", 'a' + instance);
+ mac = prom_getenv(name);
+ if (!mac) {
+ sprintf(name, "mac%c", 'a');
+ mac = prom_getenv(name);
+ }
+ if (!mac)
+ mac = default_mac;
+ for (i = 0; i < 6; i++)
+ dev_addr[i] = (char2hex(mac[i * 3]) << 4) +
+ char2hex(mac[i * 3 + 1]);
+}
+
+static int __init ar7_register_devices(void)
+{
+ int res;
+
+#ifdef CONFIG_SERIAL_8250
+
+ static struct uart_port uart_port[2];
+
+ memset(uart_port, 0, sizeof(struct uart_port) * 2);
+
+ uart_port[0].type = PORT_AR7;
+ uart_port[0].line = 0;
+ uart_port[0].irq = AR7_IRQ_UART0;
+ uart_port[0].uartclk = ar7_bus_freq() / 2;
+ uart_port[0].iotype = UPIO_MEM;
+ uart_port[0].mapbase = AR7_REGS_UART0;
+ uart_port[0].membase = ioremap(uart_port[0].mapbase, 256);
+ uart_port[0].regshift = 2;
+ res = early_serial_setup(&uart_port[0]);
+ if (res)
+ return res;
+
+
+ /* Only TNETD73xx have a second serial port */
+ if (ar7_has_second_uart()) {
+ uart_port[1].type = PORT_AR7;
+ uart_port[1].line = 1;
+ uart_port[1].irq = AR7_IRQ_UART1;
+ uart_port[1].uartclk = ar7_bus_freq() / 2;
+ uart_port[1].iotype = UPIO_MEM;
+ uart_port[1].mapbase = UR8_REGS_UART1;
+ uart_port[1].membase = ioremap(uart_port[1].mapbase, 256);
+ uart_port[1].regshift = 2;
+ res = early_serial_setup(&uart_port[1]);
+ if (res)
+ return res;
+ }
+
+#else /* !CONFIG_SERIAL_8250 */
+
+ uart_data[0].uartclk = ar7_bus_freq() / 2;
+ uart_data[1].uartclk = uart_data[0].uartclk;
+
+ /* Only TNETD73xx have a second serial port */
+ if (ar7_has_second_uart())
+ uart.dev.platform_data = uart_data;
+
+ res = platform_device_register(&uart);
+ if (res)
+ return res;
+
+#endif /* CONFIG_SERIAL_8250 */
+
+ res = platform_device_register(&physmap_flash);
+ if (res)
+ return res;
+
+ res = platform_device_register(&vlynq_low);
+ if (res)
+ return res;
+
+ ar7_device_disable(vlynq_low_data.reset_bit);
+ if (ar7_has_high_vlynq()) {
+ ar7_device_disable(vlynq_high_data.reset_bit);
+ res = platform_device_register(&vlynq_high);
+ if (res)
+ return res;
+ }
+
+ if (ar7_has_high_cpmac()) {
+ cpmac_get_mac(1, cpmac_high_data.dev_addr);
+ res = platform_device_register(&cpmac_high);
+ if (res)
+ return res;
+ } else {
+ cpmac_low_data.phy_mask = 0xffffffff;
+ }
+
+ cpmac_get_mac(0, cpmac_low_data.dev_addr);
+ res = platform_device_register(&cpmac_low);
+
+ return res;
+}
+
+
+arch_initcall(ar7_register_devices);
diff --git a/arch/mips/ar7/prom.c b/arch/mips/ar7/prom.c
new file mode 100644
index 0000000..0437f65
--- /dev/null
+++ b/arch/mips/ar7/prom.c
@@ -0,0 +1,320 @@
+/*
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute 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 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.
+ *
+ * Putting things on the screen/serial line using YAMONs facilities.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <asm/bootinfo.h>
+#include <asm/gdb-stub.h>
+
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/prom.h>
+
+#define MAX_ENTRY 80
+
+struct env_var {
+ char *name;
+ char *value;
+};
+
+static struct env_var adam2_env[MAX_ENTRY] = { { 0, }, };
+
+char *prom_getenv(char *name)
+{
+ int i;
+ for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++)
+ if (!strcmp(name, adam2_env[i].name))
+ return adam2_env[i].value;
+
+ return NULL;
+}
+EXPORT_SYMBOL(prom_getenv);
+
+char * __init prom_getcmdline(void)
+{
+ return &(arcs_cmdline[0]);
+}
+
+static void __init ar7_init_cmdline(int argc, char *argv[])
+{
+ char *cp;
+ int actr;
+
+ actr = 1; /* Always ignore argv[0] */
+
+ cp = &(arcs_cmdline[0]);
+ while (actr < argc) {
+ strcpy(cp, argv[actr]);
+ cp += strlen(argv[actr]);
+ *cp++ = ' ';
+ actr++;
+ }
+ if (cp != &(arcs_cmdline[0])) {
+ /* get rid of trailing space */
+ --cp;
+ *cp = '\0';
+ }
+}
+
+struct psbl_rec {
+ u32 psbl_size;
+ u32 env_base;
+ u32 env_size;
+ u32 ffs_base;
+ u32 ffs_size;
+};
+
+static __initdata char psp_env_version[] = "TIENV0.8";
+
+struct psp_env_chunk {
+ u8 num;
+ u8 ctrl;
+ u16 csum;
+ u8 len;
+ char data[11];
+} __attribute__ ((packed));
+
+struct psp_var_map_entry {
+ u8 num;
+ char *value;
+};
+
+static struct psp_var_map_entry psp_var_map[] = {
+ { 1, "cpufrequency" },
+ { 2, "memsize" },
+ { 3, "flashsize" },
+ { 4, "modetty0" },
+ { 5, "modetty1" },
+ { 8, "maca" },
+ { 9, "macb" },
+ { 28, "sysfrequency" },
+ { 38, "mipsfrequency" },
+};
+
+/*
+
+Well-known variable (num is looked up in table above for matching variable name)
+Example: cpufrequency=211968000
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+| 01 |CTRL|CHECKSUM | 01 | _2 | _1 | _1 | _9 | _6 | _8 | _0 | _0 | _0 | \0 | FF
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+
+Name=Value pair in a single chunk
+Example: NAME=VALUE
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+| 00 |CTRL|CHECKSUM | 01 | _N | _A | _M | _E | _0 | _V | _A | _L | _U | _E | \0
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+
+Name=Value pair in 2 chunks (len is the number of chunks)
+Example: bootloaderVersion=1.3.7.15
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+| 00 |CTRL|CHECKSUM | 02 | _b | _o | _o | _t | _l | _o | _a | _d | _e | _r | _V
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+| _e | _r | _s | _i | _o | _n | \0 | _1 | _. | _3 | _. | _7 | _. | _1 | _5 | \0
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
+
+Data is padded with 0xFF
+
+*/
+
+#define PSP_ENV_SIZE 4096
+
+static char psp_env_data[PSP_ENV_SIZE] = { 0, };
+
+static char * __init lookup_psp_var_map(u8 num)
+{
+ int i;
+
+ for (i = 0; i < sizeof(psp_var_map); i++)
+ if (psp_var_map[i].num == num)
+ return psp_var_map[i].value;
+
+ return NULL;
+}
+
+static void __init add_adam2_var(char *name, char *value)
+{
+ int i;
+ for (i = 0; i < MAX_ENTRY; i++) {
+ if (!adam2_env[i].name) {
+ adam2_env[i].name = name;
+ adam2_env[i].value = value;
+ return;
+ } else if (!strcmp(adam2_env[i].name, name)) {
+ adam2_env[i].value = value;
+ return;
+ }
+ }
+}
+
+static int __init parse_psp_env(void *psp_env_base)
+{
+ int i, n;
+ char *name, *value;
+ struct psp_env_chunk *chunks = (struct psp_env_chunk *)psp_env_data;
+
+ memcpy_fromio(chunks, psp_env_base, PSP_ENV_SIZE);
+
+ i = 1;
+ n = PSP_ENV_SIZE / sizeof(struct psp_env_chunk);
+ while (i < n) {
+ if ((chunks[i].num == 0xff) || ((i + chunks[i].len) > n))
+ break;
+ value = chunks[i].data;
+ if (chunks[i].num) {
+ name = lookup_psp_var_map(chunks[i].num);
+ } else {
+ name = value;
+ value += strlen(name) + 1;
+ }
+ if (name)
+ add_adam2_var(name, value);
+ i += chunks[i].len;
+ }
+ return 0;
+}
+
+static void __init ar7_init_env(struct env_var *env)
+{
+ int i;
+ struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300));
+ void *psp_env = (void *)KSEG1ADDR(psbl->env_base);
+
+ if (strcmp(psp_env, psp_env_version) == 0) {
+ parse_psp_env(psp_env);
+ } else {
+ for (i = 0; i < MAX_ENTRY; i++, env++)
+ if (env->name)
+ add_adam2_var(env->name, env->value);
+ }
+}
+
+static void __init console_config(void)
+{
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+ char console_string[40];
+ int baud = 0;
+ char parity = '\0', bits = '\0', flow = '\0';
+ char *s, *p;
+
+ if (strstr(prom_getcmdline(), "console="))
+ return;
+
+#ifdef CONFIG_KGDB
+ if (!strstr(prom_getcmdline(), "nokgdb")) {
+ strcat(prom_getcmdline(), " console=kgdb");
+ kgdb_enabled = 1;
+ return;
+ }
+#endif
+
+ if ((s = prom_getenv("modetty0"))) {
+ baud = simple_strtoul(s, &p, 10);
+ s = p;
+ if (*s == ',') s++;
+ if (*s) parity = *s++;
+ if (*s == ',') s++;
+ if (*s) bits = *s++;
+ if (*s == ',') s++;
+ if (*s == 'h') flow = 'r';
+ }
+
+ if (baud == 0)
+ baud = 38400;
+ if (parity != 'n' && parity != 'o' && parity != 'e')
+ parity = 'n';
+ if (bits != '7' && bits != '8')
+ bits = '8';
+ if (flow == '\0')
+ flow = 'r';
+
+ sprintf(console_string, " console=ttyS0,%d%c%c%c", baud,
+ parity, bits, flow);
+ strcat(prom_getcmdline(), console_string);
+#endif
+}
+
+void __init prom_init(void)
+{
+ ar7_init_cmdline(fw_arg0, (char **)fw_arg1);
+ ar7_init_env((struct env_var *)fw_arg2);
+ console_config();
+}
+
+#define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4)))
+static inline unsigned int serial_in(int offset)
+{
+ return readb((void *)PORT(offset));
+}
+
+static inline void serial_out(int offset, int value)
+{
+ writeb(value, (void *)PORT(offset));
+}
+
+char prom_getchar(void)
+{
+ while (!(serial_in(UART_LSR) & UART_LSR_DR));
+ return serial_in(UART_RX);
+}
+
+int prom_putchar(char c)
+{
+ while ((serial_in(UART_LSR) & UART_LSR_TEMT) == 0);
+ serial_out(UART_TX, c);
+ return 1;
+}
+
+/* from adm5120/prom.c */
+void prom_printf(char *fmt, ...)
+{
+ va_list args;
+ int l;
+ char *p, *buf_end;
+ char buf[1024];
+
+ va_start(args, fmt);
+ l = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf) */
+ va_end(args);
+
+ buf_end = buf + l;
+
+ for (p = buf; p < buf_end; p++) {
+ /* Crude cr/nl handling is better than none */
+ if (*p == '\n')
+ prom_putchar('\r');
+ prom_putchar(*p);
+ }
+}
+
+#ifdef CONFIG_KGDB
+int putDebugChar(char c)
+{
+ return prom_putchar(c);
+}
+
+char getDebugChar(void)
+{
+ return prom_getchar();
+}
+#endif
diff --git a/arch/mips/ar7/setup.c b/arch/mips/ar7/setup.c
new file mode 100644
index 0000000..8e6d878
--- /dev/null
+++ b/arch/mips/ar7/setup.c
@@ -0,0 +1,119 @@
+/*
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute 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 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.
+ */
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/pm.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/cpu.h>
+#include <linux/irq.h>
+#include <asm/time.h>
+#include <linux/io.h>
+#include <asm/reboot.h>
+
+#include <asm/dma.h>
+#include <asm/traps.h>
+#include <asm/gdb-stub.h>
+#include <asm/ar7/ar7.h>
+
+extern void ar7_time_init(void);
+static void ar7_machine_restart(char *command);
+static void ar7_machine_halt(void);
+static void ar7_machine_power_off(void);
+
+static void ar7_machine_restart(char *command)
+{
+ volatile u32 *softres_reg = (u32 *)ioremap(AR7_REGS_RESET +
+ AR7_RESET_SOFTWARE, 1);
+ *softres_reg = 1;
+}
+
+static void ar7_machine_halt(void)
+{
+ while (1);
+}
+
+static void ar7_machine_power_off(void)
+{
+ volatile u32 *power_reg = (u32 *)ioremap(AR7_REGS_POWER, 1);
+ u32 power_state = *power_reg | (3 << 30);
+ *power_reg = power_state;
+ ar7_machine_halt();
+}
+
+const char *get_system_type(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7300:
+ return "TI AR7 (TNETD7300)";
+ case AR7_CHIP_7100:
+ return "TI AR7 (TNETD7100)";
+ case AR7_CHIP_7200:
+ return "TI AR7 (TNETD7200)";
+ default:
+ return "TI AR7 (Unknown)";
+ }
+}
+
+static int __init ar7_init_console(void)
+{
+ return 0;
+}
+
+/*
+ * Initializes basic routines and structures pointers, memory size (as
+ * given by the bios and saves the command line.
+ */
+
+extern void ar7_init_clocks(void);
+
+void __init plat_mem_setup(void)
+{
+ unsigned long io_base;
+
+ _machine_restart = ar7_machine_restart;
+ _machine_halt = ar7_machine_halt;
+ pm_power_off = ar7_machine_power_off;
+ board_time_init = ar7_time_init;
+ panic_timeout = 3;
+
+ io_base = (unsigned long)ioremap(AR7_REGS_BASE, 0x10000);
+ if (!io_base) panic("Can't remap IO base!\n");
+ set_io_port_base(io_base);
+
+ prom_meminit();
+#warning FIXME: clock initialisation
+ ar7_init_clocks();
+
+ ioport_resource.start = 0;
+ ioport_resource.end = ~0;
+ iomem_resource.start = 0;
+ iomem_resource.end = ~0;
+
+ printk(KERN_INFO "%s, ID: 0x%04x, Revision: 0x%02x\n",
+ get_system_type(),
+ ar7_chip_id(), ar7_chip_rev());
+}
+
+console_initcall(ar7_init_console);
diff --git a/arch/mips/ar7/time.c b/arch/mips/ar7/time.c
new file mode 100644
index 0000000..fea75c1
--- /dev/null
+++ b/arch/mips/ar7/time.c
@@ -0,0 +1,46 @@
+/*
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute 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 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.
+ *
+ * Setting up the clock on the MIPS boards.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/mc146818rtc.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+#include <linux/irq.h>
+#include <linux/cpu.h>
+#include <asm/time.h>
+
+#include <asm/ar7/ar7.h>
+
+void __init ar7_time_init(void)
+{
+ mips_hpt_frequency = ar7_cpu_freq() / 2;
+}
+
+void __init plat_timer_setup(struct irqaction *irq)
+{
+ setup_irq(7, irq);
+}
diff --git a/arch/mips/ar7/vlynq.c b/arch/mips/ar7/vlynq.c
new file mode 100644
index 0000000..dd4796e
--- /dev/null
+++ b/arch/mips/ar7/vlynq.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2006, 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/io.h>
+
+#include <asm/addrspace.h>
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/vlynq.h>
+
+#define PER_DEVICE_IRQS 32
+
+#define VLYNQ_CTRL_PM_ENABLE 0x80000000
+#define VLYNQ_CTRL_CLOCK_INT 0x00008000
+#define VLYNQ_CTRL_CLOCK_DIV(x) ((x & 7) << 16)
+#define VLYNQ_CTRL_INT_LOCAL 0x00004000
+#define VLYNQ_CTRL_INT_ENABLE 0x00002000
+#define VLYNQ_CTRL_INT_VECTOR(x) ((x & 0x1f) << 8)
+#define VLYNQ_CTRL_INT2CFG 0x00000080
+#define VLYNQ_CTRL_RESET 0x00000001
+
+#define VLYNQ_STATUS_RERROR 0x00000100
+#define VLYNQ_STATUS_LERROR 0x00000080
+#define VLYNQ_STATUS_LINK 0x00000001
+
+#define VINT_ENABLE 0x00000100
+#define VINT_TYPE_EDGE 0x00000080
+#define VINT_LEVEL_LOW 0x00000040
+#define VINT_VECTOR(x) (x & 0x1f)
+#define VINT_OFFSET(irq) (8 * ((irq) % 4))
+
+#define VLYNQ_AUTONEGO_V2 0x00010000
+
+struct vlynq_regs {
+ volatile u32 revision;
+ volatile u32 control;
+ volatile u32 status;
+ volatile u32 int_prio;
+ volatile u32 int_status;
+ volatile u32 int_pending;
+ volatile u32 int_ptr;
+ volatile u32 tx_offset;
+ volatile struct vlynq_mapping rx_mapping[4];
+ volatile u32 chip;
+ volatile u32 autonego;
+ volatile u32 unused[6];
+ volatile u32 int_device[8];
+} __attribute__ ((packed));
+
+#ifdef VLYNQ_DEBUG
+static void vlynq_dump_regs(struct vlynq_device *dev)
+{
+ int i;
+ printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n",
+ dev->local, dev->remote);
+ for (i = 0; i < 32; i++) {
+ printk(KERN_DEBUG "VLYNQ: local %d: %08x\n",
+ i + 1, ((u32 *)dev->local)[i]);
+ printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n",
+ i + 1, ((u32 *)dev->remote)[i]);
+ }
+}
+
+static void vlynq_dump_mem(u32 *base, int count)
+{
+ int i;
+ for (i = 0; i < (count + 3) / 4; i++) {
+ if (i % 4 == 0) printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4);
+ printk(KERN_DEBUG " 0x%08x", *(base + i));
+ }
+ printk(KERN_DEBUG "\n");
+}
+#endif
+
+int vlynq_linked(struct vlynq_device *dev)
+{
+ int i;
+ for (i = 0; i < 10; i++)
+ if (dev->local->status & VLYNQ_STATUS_LINK) {
+ printk(KERN_INFO "%s: linked\n", dev->dev.bus_id);
+ return 1;
+ } else {
+ mdelay(1);
+ }
+ return 0;
+}
+
+static void vlynq_irq_unmask(unsigned int irq)
+{
+ volatile u32 val;
+ struct vlynq_device *dev = irq_desc[irq].chip_data;
+ int virq;
+
+ BUG_ON(!dev);
+ virq = irq - dev->irq_start;
+ val = dev->remote->int_device[virq >> 2];
+ val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq);
+ dev->remote->int_device[virq >> 2] = val;
+}
+
+static void vlynq_irq_mask(unsigned int irq)
+{
+ volatile u32 val;
+ struct vlynq_device *dev = irq_desc[irq].chip_data;
+ int virq;
+
+ BUG_ON(!dev);
+ virq = irq - dev->irq_start;
+ val = dev->remote->int_device[virq >> 2];
+ val &= ~(VINT_ENABLE << VINT_OFFSET(virq));
+ dev->remote->int_device[virq >> 2] = val;
+}
+
+static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
+{
+ volatile u32 val;
+ struct vlynq_device *dev = irq_desc[irq].chip_data;
+ int virq;
+
+ BUG_ON(!dev);
+ virq = irq - dev->irq_start;
+ val = dev->remote->int_device[virq >> 2];
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ val |= VINT_TYPE_EDGE << VINT_OFFSET(virq);
+ val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
+ val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
+ val |= VINT_LEVEL_LOW << VINT_OFFSET(virq);
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev->remote->int_device[virq >> 2] = val;
+ return 0;
+}
+
+static irqreturn_t vlynq_irq(int irq, void *dev_id)
+{
+ struct vlynq_device *dev = dev_id;
+ u32 status, ack;
+ int virq = 0;
+
+ status = dev->local->int_status;
+ dev->local->int_status = status;
+
+ if (status & (1 << dev->local_irq)) { /* Local vlynq IRQ. Ack */
+ ack = dev->local->status;
+ dev->local->status = ack;
+ }
+
+ if (status & (1 << dev->remote_irq)) { /* Remote vlynq IRQ. Ack */
+ ack = dev->remote->status;
+ dev->remote->status = ack;
+ }
+
+ status &= ~((1 << dev->local_irq) | (1 << dev->remote_irq));
+ while (status) {
+ if (status & 1) /* Remote device IRQ. Pass. */
+ do_IRQ(dev->irq_start + virq);
+ status >>= 1;
+ virq++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct irq_chip vlynq_irq_chip = {
+ .typename = "VLYNQ",
+ .name = "vlynq",
+ .unmask = vlynq_irq_unmask,
+ .mask = vlynq_irq_mask,
+ .set_type = vlynq_irq_type,
+};
+
+static int vlynq_setup_irq(struct vlynq_device *dev)
+{
+ u32 val;
+ int i;
+
+ if (dev->local_irq == dev->remote_irq) {
+ printk(KERN_WARNING
+ "%s: local vlynq irq should be different from remote\n",
+ dev->dev.bus_id);
+ return -EINVAL;
+ }
+
+ val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq);
+ val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
+ VLYNQ_CTRL_INT2CFG;
+ dev->local->int_ptr = 0x14;
+ dev->local->control |= val;
+
+ val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
+ val |= VLYNQ_CTRL_INT_ENABLE;
+ dev->remote->int_ptr = 0x14;
+ dev->remote->control |= val;
+
+ for (i = 0; i < PER_DEVICE_IRQS; i++) {
+ if ((i == dev->local_irq) || (i == dev->remote_irq))
+ continue;
+ irq_desc[dev->irq_start + i].status = IRQ_DISABLED;
+ irq_desc[dev->irq_start + i].action = 0;
+ irq_desc[dev->irq_start + i].depth = 1;
+ irq_desc[dev->irq_start + i].chip = &vlynq_irq_chip;
+ irq_desc[dev->irq_start + i].chip_data = dev;
+ dev->remote->int_device[i >> 2] = 0;
+ }
+
+ if (request_irq(dev->irq, vlynq_irq, SA_SHIRQ, "vlynq", dev)) {
+ printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static void vlynq_free_irq(struct vlynq_device *dev)
+{
+ free_irq(dev->irq, dev);
+}
+
+static void vlynq_device_release(struct device *dev)
+{
+ struct vlynq_device *vdev = to_vlynq_device(dev);
+ kfree(vdev);
+}
+
+static int vlynq_device_probe(struct device *dev)
+{
+ struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
+ if (drv->probe)
+ return drv->probe(to_vlynq_device(dev));
+ return 0;
+}
+
+static int vlynq_device_remove(struct device *dev)
+{
+ struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
+ if (drv->remove)
+ return drv->remove(to_vlynq_device(dev));
+ return 0;
+}
+
+int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner)
+{
+ driver->driver.name = driver->name;
+ driver->driver.bus = &vlynq_bus_type;
+/* driver->driver.owner = owner;*/
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL(__vlynq_register_driver);
+
+void vlynq_unregister_driver(struct vlynq_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(vlynq_unregister_driver);
+
+int vlynq_device_enable(struct vlynq_device *dev)
+{
+ u32 val;
+ u32 div;
+ int result;
+ struct plat_vlynq_ops *ops = dev->dev.platform_data;
+
+ result = ops->on(dev);
+ if (result)
+ return result;
+
+ dev->local->control = 0;
+ dev->remote->control = 0;
+
+ printk(KERN_DEBUG "ar7_dsp_freq() = %d\n", ar7_dsp_freq());
+ if (vlynq_linked(dev))
+ return vlynq_setup_irq(dev);
+
+ for (val = 0; val < 8; val++) {
+ dev->local->control = VLYNQ_CTRL_CLOCK_DIV(val) |
+ VLYNQ_CTRL_CLOCK_INT;
+ if (vlynq_linked(dev))
+ return vlynq_setup_irq(dev);
+ }
+
+ return -ENODEV;
+}
+
+void vlynq_device_disable(struct vlynq_device *dev)
+{
+ struct plat_vlynq_ops *ops = dev->dev.platform_data;
+
+ vlynq_free_irq(dev);
+ ops->off(dev);
+}
+
+u32 vlynq_local_id(struct vlynq_device *dev)
+{
+ return dev->local->chip;
+}
+
+u32 vlynq_remote_id(struct vlynq_device *dev)
+{
+ return dev->remote->chip;
+}
+
+void vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset,
+ struct vlynq_mapping *mapping)
+{
+ int i;
+
+ dev->local->tx_offset = tx_offset;
+ for (i = 0; i < 4; i++) {
+ dev->local->rx_mapping[i].offset = mapping[i].offset;
+ dev->local->rx_mapping[i].size = mapping[i].size;
+ }
+}
+
+void vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset,
+ struct vlynq_mapping *mapping)
+{
+ int i;
+
+ dev->remote->tx_offset = tx_offset;
+ for (i = 0; i < 4; i++) {
+ dev->remote->rx_mapping[i].offset = mapping[i].offset;
+ dev->remote->rx_mapping[i].size = mapping[i].size;
+ }
+}
+
+int vlynq_virq_to_irq(struct vlynq_device *dev, int virq)
+{
+ if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ if ((virq == dev->local_irq) || (virq == dev->remote_irq))
+ return -EINVAL;
+
+ return dev->irq_start + virq;
+}
+
+int vlynq_irq_to_virq(struct vlynq_device *dev, int irq)
+{
+ if ((irq < dev->irq_start) || (irq >= dev->irq_start + PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ return irq - dev->irq_start;
+}
+
+int vlynq_set_local_irq(struct vlynq_device *dev, int virq)
+{
+ if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ if (virq == dev->remote_irq)
+ return -EINVAL;
+
+ dev->local_irq = virq;
+
+ return 0;
+}
+
+int vlynq_set_remote_irq(struct vlynq_device *dev, int virq)
+{
+ if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ if (virq == dev->local_irq)
+ return -EINVAL;
+
+ dev->remote_irq = virq;
+
+ return 0;
+}
+
+static int vlynq_probe(struct platform_device *pdev)
+{
+ struct vlynq_device *dev;
+ struct resource *regs_res, *mem_res, *irq_res;
+ int len, result;
+
+ if (strcmp(pdev->name, "vlynq"))
+ return -ENODEV;
+
+ regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!regs_res)
+ return -ENODEV;
+
+ mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
+ if (!mem_res)
+ return -ENODEV;
+
+ irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq");
+ if (!irq_res)
+ return -ENODEV;
+
+ dev = kmalloc(sizeof(struct vlynq_device), GFP_KERNEL);
+ if (!dev) {
+ printk(KERN_ERR "vlynq: failed to allocate device structure\n");
+ return -ENOMEM;
+ }
+
+ memset(dev, 0, sizeof(struct vlynq_device));
+
+ dev->id = pdev->id;
+ dev->dev.bus = &vlynq_bus_type;
+ dev->dev.parent = &pdev->dev;
+ snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id);
+ dev->dev.bus_id[BUS_ID_SIZE - 1] = 0;
+ dev->dev.platform_data = pdev->dev.platform_data;
+ dev->dev.release = vlynq_device_release;
+
+ dev->regs_start = regs_res->start;
+ dev->regs_end = regs_res->end;
+ dev->mem_start = mem_res->start;
+ dev->mem_end = mem_res->end;
+
+ len = regs_res->end - regs_res->start;
+ if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) {
+ printk(KERN_ERR "%s: Can't request vlynq registers\n",
+ dev->dev.bus_id);
+ result = -ENXIO;
+ goto fail_request;
+ }
+
+ dev->local = ioremap_nocache(regs_res->start, len);
+ if (!dev->local) {
+ printk(KERN_ERR "%s: Can't remap vlynq registers\n",
+ dev->dev.bus_id);
+ result = -ENXIO;
+ goto fail_remap;
+ }
+
+ dev->remote = (struct vlynq_regs *)((u32)dev->local + 128);
+
+ dev->irq = platform_get_irq_byname(pdev, "irq");
+ dev->irq_start = irq_res->start;
+ dev->irq_end = irq_res->end;
+ dev->local_irq = 31;
+ dev->remote_irq = 30;
+
+ if (device_register(&dev->dev))
+ goto fail_register;
+ platform_set_drvdata(pdev, dev);
+
+ printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n",
+ dev->dev.bus_id, (void *)dev->regs_start, dev->irq,
+ (void *)dev->mem_start);
+
+ return 0;
+
+fail_register:
+fail_remap:
+ iounmap(dev->local);
+fail_request:
+ release_mem_region(regs_res->start, len);
+ kfree(dev);
+ return result;
+}
+
+static int vlynq_remove(struct platform_device *pdev)
+{
+ struct vlynq_device *dev = platform_get_drvdata(pdev);
+
+ device_unregister(&dev->dev);
+ release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start);
+
+ kfree(dev);
+
+ return 0;
+}
+
+static struct platform_driver vlynq_driver = {
+ .driver.name = "vlynq",
+ .probe = vlynq_probe,
+ .remove = vlynq_remove,
+};
+
+struct bus_type vlynq_bus_type = {
+ .name = "vlynq",
+ .probe = vlynq_device_probe,
+ .remove = vlynq_device_remove,
+};
+EXPORT_SYMBOL(vlynq_bus_type);
+
+#ifdef CONFIG_PCI
+extern void vlynq_pci_init(void);
+#endif
+int __init vlynq_init(void)
+{
+ int res = 0;
+
+ res = bus_register(&vlynq_bus_type);
+ if (res)
+ goto fail_bus;
+
+ res = platform_driver_register(&vlynq_driver);
+ if (res)
+ goto fail_platform;
+
+#ifdef CONFIG_PCI
+ vlynq_pci_init();
+#endif
+
+ return 0;
+
+fail_platform:
+ bus_unregister(&vlynq_bus_type);
+fail_bus:
+ return res;
+}
+
+/*
+void __devexit vlynq_exit(void)
+{
+ platform_driver_unregister(&vlynq_driver);
+ bus_unregister(&vlynq_bus_type);
+}
+*/
+
+
+subsys_initcall(vlynq_init);
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 6379003..75a46ba 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
exception_handlers[n] = handler;
if (n == 0 && cpu_has_divec) {
+#ifdef CONFIG_AR7
+ /* lui k0, 0x0000 */
+ *(volatile u32 *)(CAC_BASE+0x200) =
+ 0x3c1a0000 | (handler >> 16);
+ /* ori k0, 0x0000 */
+ *(volatile u32 *)(CAC_BASE+0x204) =
+ 0x375a0000 | (handler & 0xffff);
+ /* jr k0 */
+ *(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+ /* nop */
+ *(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+ flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
+#else
*(volatile u32 *)(ebase + 0x200) = 0x08000000 |
(0x03ffffff & (handler >> 2));
flush_icache_range(ebase + 0x200, ebase + 0x204);
+#endif
}
return (void *)old_handler;
}
diff --git a/include/asm-mips/ar7/ar7.h b/include/asm-mips/ar7/ar7.h
new file mode 100644
index 0000000..b1efa6e
--- /dev/null
+++ b/include/asm-mips/ar7/ar7.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006, 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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
+ */
+
+#ifndef __AR7_H__
+#define __AR7_H__
+
+#include <linux/delay.h>
+#include <asm/addrspace.h>
+#include <linux/io.h>
+
+#define AR7_REGS_BASE 0x08610000
+
+#define AR7_REGS_MAC0 (AR7_REGS_BASE + 0x0000)
+#define AR7_REGS_GPIO (AR7_REGS_BASE + 0x0900)
+/* 0x08610A00 - 0x08610BFF (512 bytes, 128 bytes / clock) */
+#define AR7_REGS_POWER (AR7_REGS_BASE + 0x0a00)
+#define AR7_REGS_UART0 (AR7_REGS_BASE + 0x0e00)
+#define AR7_REGS_RESET (AR7_REGS_BASE + 0x1600)
+#define AR7_REGS_VLYNQ0 (AR7_REGS_BASE + 0x1800)
+#define AR7_REGS_DCL (AR7_REGS_BASE + 0x1A00)
+#define AR7_REGS_VLYNQ1 (AR7_REGS_BASE + 0x1C00)
+#define AR7_REGS_MDIO (AR7_REGS_BASE + 0x1E00)
+#define AR7_REGS_IRQ (AR7_REGS_BASE + 0x2400)
+#define AR7_REGS_MAC1 (AR7_REGS_BASE + 0x2800)
+
+#define AR7_REGS_WDT (AR7_REGS_BASE + 0x1f00)
+#define UR8_REGS_WDT (AR7_REGS_BASE + 0x0b00)
+#define UR8_REGS_UART1 (AR7_REGS_BASE + 0x0f00)
+
+#define AR7_RESET_PEREPHERIAL 0x0
+#define AR7_RESET_SOFTWARE 0x4
+#define AR7_RESET_STATUS 0x8
+
+#define AR7_RESET_BIT_CPMAC_LO 17
+#define AR7_RESET_BIT_CPMAC_HI 21
+#define AR7_RESET_BIT_MDIO 22
+#define AR7_RESET_BIT_EPHY 26
+
+/* GPIO control registers */
+#define AR7_GPIO_INPUT 0x0
+#define AR7_GPIO_OUTPUT 0x4
+#define AR7_GPIO_DIR 0x8
+#define AR7_GPIO_ENABLE 0xC
+
+#define AR7_CHIP_7100 0x18
+#define AR7_CHIP_7200 0x2b
+#define AR7_CHIP_7300 0x05
+
+/* Interrupts */
+#define AR7_IRQ_UART0 15
+#define AR7_IRQ_UART1 16
+
+/* Clocks */
+#define AR7_AFE_CLOCK 35328000
+#define AR7_REF_CLOCK 25000000
+#define AR7_XTAL_CLOCK 24000000
+
+struct plat_cpmac_data {
+ int reset_bit;
+ int power_bit;
+ u32 phy_mask;
+ char dev_addr[6];
+};
+
+struct plat_dsl_data {
+ int reset_bit_dsl;
+ int reset_bit_sar;
+};
+
+extern int ar7_cpu_clock, ar7_bus_clock, ar7_dsp_clock;
+
+static inline u16 ar7_chip_id(void)
+{
+ return readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x14)) & 0xffff;
+}
+
+static inline u8 ar7_chip_rev(void)
+{
+ return (readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x14)) >> 16) & 0xff;
+}
+
+static inline int ar7_cpu_freq(void)
+{
+ return ar7_cpu_clock;
+}
+
+static inline int ar7_bus_freq(void)
+{
+ return ar7_bus_clock;
+}
+
+static inline int ar7_vbus_freq(void)
+{
+ return ar7_bus_clock / 2;
+}
+#define ar7_cpmac_freq ar7_vbus_freq
+
+static inline int ar7_dsp_freq(void)
+{
+ return ar7_dsp_clock;
+}
+
+static inline int ar7_has_high_cpmac(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7100:
+ case AR7_CHIP_7200:
+ return 0;
+ default:
+ return 1;
+ }
+}
+#define ar7_has_high_vlynq ar7_has_high_cpmac
+#define ar7_has_second_uart ar7_has_high_cpmac
+
+static inline void ar7_device_enable(u32 bit)
+{
+ void *reset_reg =
+ (void *)KSEG1ADDR(AR7_REGS_RESET + AR7_RESET_PEREPHERIAL);
+ writel(readl(reset_reg) | (1 << bit), reset_reg);
+ mdelay(20);
+}
+
+static inline void ar7_device_disable(u32 bit)
+{
+ void *reset_reg =
+ (void *)KSEG1ADDR(AR7_REGS_RESET + AR7_RESET_PEREPHERIAL);
+ writel(readl(reset_reg) & ~(1 << bit), reset_reg);
+ mdelay(20);
+}
+
+static inline void ar7_device_reset(u32 bit)
+{
+ ar7_device_disable(bit);
+ ar7_device_enable(bit);
+}
+
+static inline void ar7_device_on(u32 bit)
+{
+ void *power_reg = (void *)KSEG1ADDR(AR7_REGS_POWER);
+ writel(readl(power_reg) | (1 << bit), power_reg);
+ mdelay(20);
+}
+
+static inline void ar7_device_off(u32 bit)
+{
+ void *power_reg = (void *)KSEG1ADDR(AR7_REGS_POWER);
+ writel(readl(power_reg) & ~(1 << bit), power_reg);
+ mdelay(20);
+}
+
+#endif /* __AR7_H__ */
diff --git a/include/asm-mips/ar7/gpio.h b/include/asm-mips/ar7/gpio.h
new file mode 100644
index 0000000..1b5d3aa
--- /dev/null
+++ b/include/asm-mips/ar7/gpio.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 Florian Fainelli <florian@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
+ */
+
+#ifndef __AR7_GPIO_H__
+#define __AR7_GPIO_H__
+#include <asm/ar7/ar7.h>
+
+#define AR7_GPIO_MAX 32
+
+extern int gpio_request(unsigned gpio, char *label);
+extern void gpio_free(unsigned gpio);
+
+/* Common GPIO layer */
+static inline int gpio_direction_input(unsigned gpio)
+{
+ void __iomem *gpio_dir =
+ (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_DIR);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_dir) | (1 << gpio), gpio_dir);
+
+ return 0;
+}
+
+static inline int gpio_direction_output(unsigned gpio)
+{
+ void __iomem *gpio_dir =
+ (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_DIR);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_dir) & ~(1 << gpio), gpio_dir);
+
+ return 0;
+}
+
+static inline int gpio_get_value(unsigned gpio)
+{
+ void __iomem *gpio_in =
+ (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_INPUT);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ return ((readl(gpio_in) & (1 << gpio)) != 0);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+ void __iomem *gpio_out =
+ (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_OUTPUT);
+ volatile unsigned tmp;
+
+ if (gpio >= AR7_GPIO_MAX)
+ return;
+
+ tmp = readl(gpio_out) & ~(1 << gpio);
+ if (value)
+ tmp |= 1 << gpio;
+ writel(tmp, gpio_out);
+}
+
+static inline int gpio_to_irq(unsigned gpio)
+{
+ return -EINVAL;
+}
+
+static inline int irq_to_gpio(unsigned irq)
+{
+ return -EINVAL;
+}
+
+/* Board specific GPIO functions */
+static inline int ar7_gpio_enable(unsigned gpio)
+{
+ void __iomem *gpio_en =
+ (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_ENABLE);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_en) | (1 << gpio), gpio_en);
+
+ return 0;
+}
+
+static inline int ar7_gpio_disable(unsigned gpio)
+{
+ void __iomem *gpio_en =
+ (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_ENABLE);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_en) & ~(1 << gpio), gpio_en);
+
+ return 0;
+}
+
+#include <asm-generic/gpio.h>
+
+#endif
diff --git a/include/asm-mips/ar7/mmzone.h b/include/asm-mips/ar7/mmzone.h
new file mode 100644
index 0000000..2789689
--- /dev/null
+++ b/include/asm-mips/ar7/mmzone.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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
+ */
+
+#ifndef _ASM_MACH_MMZONE_H
+#define _ASM_MACH_MMZONE_H
+
+extern pg_data_t __node_data[];
+#define NODE_DATA(nid) (&__node_data[nid])
+#define NODE_MEM_MAP(nid) (NODE_DATA(nid)->node_mem_map)
+#define pa_to_nid(addr) (((addr) >= ARCH_PFN_OFFSET << PAGE_SHIFT) ? 0 : -1)
+
+#endif /* _ASM_MACH_MMZONE_H */
diff --git a/include/asm-mips/ar7/prom.h b/include/asm-mips/ar7/prom.h
new file mode 100644
index 0000000..62d7d5c
--- /dev/null
+++ b/include/asm-mips/ar7/prom.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2006, 2007 Florian Fainelli <florian@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
+ */
+
+#ifndef __PROM_H__
+#define __PROM_H__
+
+extern char *prom_getenv(char *name);
+extern void prom_printf(char *fmt, ...);
+
+#endif /* __PROM_H__ */
diff --git a/include/asm-mips/ar7/spaces.h b/include/asm-mips/ar7/spaces.h
new file mode 100644
index 0000000..f4d1237
--- /dev/null
+++ b/include/asm-mips/ar7/spaces.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002 Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_AR7_SPACES_H
+#define _ASM_AR7_SPACES_H
+
+#define CAC_BASE 0x80000000
+#define IO_BASE 0xa0000000
+#define UNCAC_BASE 0xa0000000
+#define MAP_BASE 0xc0000000
+
+/*
+ * This handles the memory map.
+ * We handle pages at KSEG0 for kernels with 32 bit address space.
+ */
+#define PAGE_OFFSET 0x94000000UL
+#define PHYS_OFFSET 0x14000000UL
+
+/*
+ * Memory above this physical address will be considered highmem.
+ */
+#ifndef HIGHMEM_START
+#define HIGHMEM_START 0x40000000UL
+#endif
+
+#endif /* __ASM_AR7_SPACES_H */
diff --git a/include/asm-mips/ar7/vlynq.h b/include/asm-mips/ar7/vlynq.h
new file mode 100644
index 0000000..02482e0
--- /dev/null
+++ b/include/asm-mips/ar7/vlynq.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2006, 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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
+ */
+
+
+#ifndef __VLYNQ_H__
+#define __VLYNQ_H__
+
+struct vlynq_mapping {
+ u32 size;
+ u32 offset;
+} __attribute__ ((packed));
+
+struct vlynq_device_id {
+ u32 id;
+};
+
+struct vlynq_regs;
+struct vlynq_device {
+ u32 id;
+ int irq;
+ int local_irq;
+ int remote_irq;
+ int clock_div;
+ u32 regs_start, regs_end;
+ u32 mem_start, mem_end;
+ u32 irq_start, irq_end;
+ void *priv;
+ struct vlynq_regs *local;
+ struct vlynq_regs *remote;
+ struct device dev;
+};
+
+struct vlynq_driver {
+ char *name;
+ int (*probe)(struct vlynq_device *dev);
+ int (*remove)(struct vlynq_device *dev);
+ struct device_driver driver;
+};
+
+#define to_vlynq_driver(drv) container_of(drv, struct vlynq_driver, driver)
+
+struct plat_vlynq_ops {
+ int (*on)(struct vlynq_device *dev);
+ void (*off)(struct vlynq_device *dev);
+};
+
+#define to_vlynq_device(device) container_of(device, struct vlynq_device, dev)
+
+extern struct bus_type vlynq_bus_type;
+
+extern int __vlynq_register_driver(struct vlynq_driver *driver,
+ struct module *owner);
+
+static inline int vlynq_register_driver(struct vlynq_driver *driver)
+{
+ return __vlynq_register_driver(driver, THIS_MODULE);
+}
+
+extern void vlynq_unregister_driver(struct vlynq_driver *driver);
+extern int vlynq_device_enable(struct vlynq_device *dev);
+extern void vlynq_device_disable(struct vlynq_device *dev);
+extern u32 vlynq_local_id(struct vlynq_device *dev);
+extern u32 vlynq_remote_id(struct vlynq_device *dev);
+extern void vlynq_set_local_mapping(struct vlynq_device *dev,
+ u32 tx_offset,
+ struct vlynq_mapping *mapping);
+extern void vlynq_set_remote_mapping(struct vlynq_device *dev,
+ u32 tx_offset,
+ struct vlynq_mapping *mapping);
+extern int vlynq_virq_to_irq(struct vlynq_device *dev, int virq);
+extern int vlynq_irq_to_virq(struct vlynq_device *dev, int irq);
+extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq);
+extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq);
+
+#endif /* __VLYNQ_H__ */
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][1/7] AR7: core support
2007-09-08 0:18 ` [PATCH][MIPS][1/7] AR7: core support Matteo Croce
@ 2007-09-08 17:40 ` Atsushi Nemoto
2007-09-11 22:43 ` Matteo Croce
0 siblings, 1 reply; 30+ messages in thread
From: Atsushi Nemoto @ 2007-09-08 17:40 UTC (permalink / raw)
To: technoboy85
Cc: linux-mips, florian, nbd, ejka, nico, ralf, openwrt-devel, akpm
On Sat, 8 Sep 2007 02:18:49 +0200, Matteo Croce <technoboy85@gmail.com> wrote:
> Support for memory mapping, clock and the vlynq bus
Just some random comments.
> diff --git a/arch/mips/ar7/Makefile b/arch/mips/ar7/Makefile
> new file mode 100644
> index 0000000..e6ba02c
> --- /dev/null
> +++ b/arch/mips/ar7/Makefile
> @@ -0,0 +1,13 @@
> +
> +obj-y := \
> + prom.o \
> + setup.o \
> + memory.o \
> + irq.o \
> + time.o \
> + platform.o \
> + gpio.o \
> + clock.o \
> + vlynq.o
> +
> +EXTRA_AFLAGS := $(CFLAGS)
This EXTRA_AFLAGS line is redundant. You can drop it safely.
> diff --git a/arch/mips/ar7/irq.c b/arch/mips/ar7/irq.c
> new file mode 100644
> index 0000000..f131301
> --- /dev/null
> +++ b/arch/mips/ar7/irq.c
...
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/irq.h>
No need to include <linux/irq.h>
> +#define REG(addr) (*(volatile u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr))
You can remove this "volatile" rewriting something like:
#define REG_RD(addr) \
readl((void __iomem *)(KSEG1ADDR(AR7_REGS_IRQ) + (addr)))
#define REG_WR(val, addr) \
writel(val, (void __iomem *)(KSEG1ADDR(AR7_REGS_IRQ) + (addr)))
> +static struct irq_chip ar7_irq_type = {
> + .typename = "AR7",
The typename is obsolete and not needed if you have name field.
> +static void ar7_unmask_irq(unsigned int irq)
> +{
> + unsigned long flags;
> + local_irq_save(flags);
> + /* enable the interrupt channel bit */
> + REG(ESR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
> + local_irq_restore(flags);
> +}
> +
> +static void ar7_mask_irq(unsigned int irq)
> +{
> + unsigned long flags;
> + local_irq_save(flags);
> + /* disable the interrupt channel bit */
> + REG(ECR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
> + local_irq_restore(flags);
> +}
> +
> +static void ar7_unmask_secondary_irq(unsigned int irq)
> +{
> + unsigned long flags;
> + local_irq_save(flags);
> + /* enable the interrupt channel bit */
> + REG(SEC_ESR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
> + local_irq_restore(flags);
> +}
> +
> +static void ar7_mask_secondary_irq(unsigned int irq)
> +{
> + unsigned long flags;
> + local_irq_save(flags);
> + /* disable the interrupt channel bit */
> + REG(SEC_ECR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
> + local_irq_restore(flags);
> +}
The mask, unmask routines are always called irq disabled. So you can
drop these local_irqsave/restore pairs.
> + for (i = 0; i < 40; i++) {
> + REG(CHNL_OFFSET(i)) = i;
> + /* Primary IRQ's */
> + irq_desc[i + base].status = IRQ_DISABLED;
> + irq_desc[i + base].action = NULL;
> + irq_desc[i + base].depth = 1;
> + irq_desc[i + base].chip = &ar7_irq_type;
> + /* Secondary IRQ's */
> + if (i < 32) {
> + irq_desc[i + base + 40].status = IRQ_DISABLED;
> + irq_desc[i + base + 40].action = NULL;
> + irq_desc[i + base + 40].depth = 1;
> + irq_desc[i + base + 40].chip = &ar7_secondary_irq_type;
> + }
> + }
Use set_irq_chip() or its variants instead of touching irq_desc[]
directly. It seems this platform can set GENERIC_HARDIRQS_NO__DO_IRQ
in Kconfig, using handle_level_irq irq handler.
> +asmlinkage void plat_irq_dispatch(void)
> +{
> + unsigned int pending = read_c0_status() & read_c0_cause();
> + if (pending & STATUSF_IP7) /* cpu timer */
> + do_IRQ(7);
> + else if (pending & STATUSF_IP2) /* int0 hardware line */
> + do_IRQ(2);
> + else
> + spurious_interrupt();
> +}
It would be safer to mask 'pending' with ST0_IM.
> diff --git a/arch/mips/ar7/prom.c b/arch/mips/ar7/prom.c
> new file mode 100644
> index 0000000..0437f65
...
> +/* from adm5120/prom.c */
> +void prom_printf(char *fmt, ...)
> +{
> + va_list args;
> + int l;
> + char *p, *buf_end;
> + char buf[1024];
> +
> + va_start(args, fmt);
> + l = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf) */
> + va_end(args);
> +
> + buf_end = buf + l;
> +
> + for (p = buf; p < buf_end; p++) {
> + /* Crude cr/nl handling is better than none */
> + if (*p == '\n')
> + prom_putchar('\r');
> + prom_putchar(*p);
> + }
> +}
prom_printf() is not used in recent linux-mips.
> diff --git a/arch/mips/ar7/time.c b/arch/mips/ar7/time.c
> new file mode 100644
> index 0000000..fea75c1
> --- /dev/null
> +++ b/arch/mips/ar7/time.c
...
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/time.h>
> +#include <linux/timex.h>
> +#include <linux/mc146818rtc.h>
> +#include <linux/ptrace.h>
> +#include <linux/hardirq.h>
> +#include <linux/irq.h>
> +#include <linux/cpu.h>
> +#include <asm/time.h>
You do not have to include so many headers.
> diff --git a/arch/mips/ar7/vlynq.c b/arch/mips/ar7/vlynq.c
> new file mode 100644
> index 0000000..dd4796e
> --- /dev/null
> +++ b/arch/mips/ar7/vlynq.c
...
> +struct vlynq_regs {
> + volatile u32 revision;
> + volatile u32 control;
> + volatile u32 status;
> + volatile u32 int_prio;
> + volatile u32 int_status;
> + volatile u32 int_pending;
> + volatile u32 int_ptr;
> + volatile u32 tx_offset;
> + volatile struct vlynq_mapping rx_mapping[4];
> + volatile u32 chip;
> + volatile u32 autonego;
> + volatile u32 unused[6];
> + volatile u32 int_device[8];
> +} __attribute__ ((packed));
You can drop these volatile, using readl/writel for accessing these
members.
> +static void vlynq_irq_unmask(unsigned int irq)
> +{
> + volatile u32 val;
> + struct vlynq_device *dev = irq_desc[irq].chip_data;
This "volatile" should be removed.
Use get_irq_chip_data(irq) instead of using irq_desc[] directly.
> +static void vlynq_irq_mask(unsigned int irq)
> +{
> + volatile u32 val;
This "volatile" should be removed.
> +static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
> +{
> + volatile u32 val;
This "volatile" should be removed.
> +static struct irq_chip vlynq_irq_chip = {
> + .typename = "VLYNQ",
The typename is obsolete and not needed if you have name field.
> + for (i = 0; i < PER_DEVICE_IRQS; i++) {
> + if ((i == dev->local_irq) || (i == dev->remote_irq))
> + continue;
> + irq_desc[dev->irq_start + i].status = IRQ_DISABLED;
> + irq_desc[dev->irq_start + i].action = 0;
> + irq_desc[dev->irq_start + i].depth = 1;
> + irq_desc[dev->irq_start + i].chip = &vlynq_irq_chip;
> + irq_desc[dev->irq_start + i].chip_data = dev;
> + dev->remote->int_device[i >> 2] = 0;
> + }
Use set_irq_chip() or its variants.
> + dev = kmalloc(sizeof(struct vlynq_device), GFP_KERNEL);
> + if (!dev) {
> + printk(KERN_ERR "vlynq: failed to allocate device structure\n");
> + return -ENOMEM;
> + }
> +
> + memset(dev, 0, sizeof(struct vlynq_device));
Use kzalloc().
> +int __init vlynq_init(void)
This can be static.
> diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> index 6379003..75a46ba 100644
> --- a/arch/mips/kernel/traps.c
> +++ b/arch/mips/kernel/traps.c
> @@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
>
> exception_handlers[n] = handler;
> if (n == 0 && cpu_has_divec) {
> +#ifdef CONFIG_AR7
> + /* lui k0, 0x0000 */
> + *(volatile u32 *)(CAC_BASE+0x200) =
> + 0x3c1a0000 | (handler >> 16);
> + /* ori k0, 0x0000 */
> + *(volatile u32 *)(CAC_BASE+0x204) =
> + 0x375a0000 | (handler & 0xffff);
> + /* jr k0 */
> + *(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
> + /* nop */
> + *(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
> + flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
> +#else
> *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
> (0x03ffffff & (handler >> 2));
> flush_icache_range(ebase + 0x200, ebase + 0x204);
> +#endif
> }
> return (void *)old_handler;
> }
Runtime checking, something like this would be better than ifdef:
if ((handler ^ (ebase + 4)) & 0xfc000000)
/* use jr */
...
} else {
/* use j */
...
}
---
Atsushi Nemoto
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][1/7] AR7: core support
2007-09-08 17:40 ` Atsushi Nemoto
@ 2007-09-11 22:43 ` Matteo Croce
2007-09-12 11:10 ` Ralf Baechle
2007-09-12 11:22 ` Thomas Bogendoerfer
0 siblings, 2 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-11 22:43 UTC (permalink / raw)
To: Atsushi Nemoto
Cc: linux-mips, florian, nbd, ejka, nico, ralf, openwrt-devel, akpm
Il Saturday 08 September 2007 19:40:20 Atsushi Nemoto ha scritto:
> > diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> > index 6379003..75a46ba 100644
> > --- a/arch/mips/kernel/traps.c
> > +++ b/arch/mips/kernel/traps.c
> > @@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
> >
> > exception_handlers[n] = handler;
> > if (n == 0 && cpu_has_divec) {
> > +#ifdef CONFIG_AR7
> > + /* lui k0, 0x0000 */
> > + *(volatile u32 *)(CAC_BASE+0x200) =
> > + 0x3c1a0000 | (handler >> 16);
> > + /* ori k0, 0x0000 */
> > + *(volatile u32 *)(CAC_BASE+0x204) =
> > + 0x375a0000 | (handler & 0xffff);
> > + /* jr k0 */
> > + *(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
> > + /* nop */
> > + *(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
> > + flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
> > +#else
> > *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
> > (0x03ffffff & (handler >> 2));
> > flush_icache_range(ebase + 0x200, ebase + 0x204);
> > +#endif
> > }
> > return (void *)old_handler;
> > }
>
> Runtime checking, something like this would be better than ifdef:
>
> if ((handler ^ (ebase + 4)) & 0xfc000000)
> /* use jr */
> ...
> } else {
> /* use j */
> ...
> }
This will not make the code bigger? What's wrong with #ifdef?
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][1/7] AR7: core support
2007-09-11 22:43 ` Matteo Croce
@ 2007-09-12 11:10 ` Ralf Baechle
2007-09-12 11:22 ` Thomas Bogendoerfer
1 sibling, 0 replies; 30+ messages in thread
From: Ralf Baechle @ 2007-09-12 11:10 UTC (permalink / raw)
To: Matteo Croce
Cc: Atsushi Nemoto, linux-mips, florian, nbd, ejka, nico,
openwrt-devel, akpm
On Wed, Sep 12, 2007 at 12:43:42AM +0200, Matteo Croce wrote:
> > > --- a/arch/mips/kernel/traps.c
> > > +++ b/arch/mips/kernel/traps.c
> > > @@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
> > >
> > > exception_handlers[n] = handler;
> > > if (n == 0 && cpu_has_divec) {
> > > +#ifdef CONFIG_AR7
> > > + /* lui k0, 0x0000 */
> > > + *(volatile u32 *)(CAC_BASE+0x200) =
s/CAC_BASE/ebase/
> > > + 0x3c1a0000 | (handler >> 16);
> > > + /* ori k0, 0x0000 */
> > > + *(volatile u32 *)(CAC_BASE+0x204) =
> > > + 0x375a0000 | (handler & 0xffff);
> > > + /* jr k0 */
> > > + *(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
> > > + /* nop */
> > > + *(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
> > > + flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
All these volatile keywords are unnecessary btw.
You may want to read Documentation/volatile-considered-harmful.txt on
why volatile is almost always a bad idea.
> > > +#else
> > > *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
> > > (0x03ffffff & (handler >> 2));
Just like this one, so I will remove it now.
> > > flush_icache_range(ebase + 0x200, ebase + 0x204);
> > > +#endif
> > > }
> > > return (void *)old_handler;
> > > }
> >
> > Runtime checking, something like this would be better than ifdef:
> >
> > if ((handler ^ (ebase + 4)) & 0xfc000000)
> > /* use jr */
> > ...
> > } else {
> > /* use j */
> > ...
> > }
> This will not make the code bigger?
It will by a miniscule amount. Which hardly matters because the function
is (whops, should be ...) __init code anyway.
> What's wrong with #ifdef?
#ifdef makes for harder to read code (To paraphrase Linus - the kernel is
write once and read 10 million times) , is less flexible and has a tendence
to hide bugs in the deactivated part. So generally avoid.
And actually in this specific case it also should be a runtime decission
simply because in the not so distant future the will be hardware which
will simply need that.
Ralf
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][1/7] AR7: core support
2007-09-11 22:43 ` Matteo Croce
2007-09-12 11:10 ` Ralf Baechle
@ 2007-09-12 11:22 ` Thomas Bogendoerfer
2007-09-12 13:55 ` Ralf Baechle
1 sibling, 1 reply; 30+ messages in thread
From: Thomas Bogendoerfer @ 2007-09-12 11:22 UTC (permalink / raw)
To: Matteo Croce
Cc: Atsushi Nemoto, linux-mips, florian, nbd, ejka, nico, ralf,
openwrt-devel, akpm
On Wed, Sep 12, 2007 at 12:43:42AM +0200, Matteo Croce wrote:
> Il Saturday 08 September 2007 19:40:20 Atsushi Nemoto ha scritto:
> > > diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> > > index 6379003..75a46ba 100644
> > > --- a/arch/mips/kernel/traps.c
> > > +++ b/arch/mips/kernel/traps.c
> > > @@ -1075,9 +1075,23 @@ void *set_except_vector(int n, void *addr)
> > >
> > > exception_handlers[n] = handler;
> > > if (n == 0 && cpu_has_divec) {
> > > +#ifdef CONFIG_AR7
> > > + /* lui k0, 0x0000 */
> > > + *(volatile u32 *)(CAC_BASE+0x200) =
> > > + 0x3c1a0000 | (handler >> 16);
> > > + /* ori k0, 0x0000 */
> > > + *(volatile u32 *)(CAC_BASE+0x204) =
> > > + 0x375a0000 | (handler & 0xffff);
> > > + /* jr k0 */
> > > + *(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
> > > + /* nop */
> > > + *(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
> > > + flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
> > > +#else
> > > *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
> > > (0x03ffffff & (handler >> 2));
> > > flush_icache_range(ebase + 0x200, ebase + 0x204);
> > > +#endif
> > > }
> > > return (void *)old_handler;
> > > }
> >
> > Runtime checking, something like this would be better than ifdef:
> >
> > if ((handler ^ (ebase + 4)) & 0xfc000000)
> > /* use jr */
> > ...
> > } else {
> > /* use j */
> > ...
> > }
> This will not make the code bigger? What's wrong with #ifdef?
probably nothing, but having a generic decision whether we need
a version with j or jr will help other platforms as well. Why make
AR7 a special case ?
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][1/7] AR7: core support
2007-09-12 11:22 ` Thomas Bogendoerfer
@ 2007-09-12 13:55 ` Ralf Baechle
0 siblings, 0 replies; 30+ messages in thread
From: Ralf Baechle @ 2007-09-12 13:55 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: Matteo Croce, Atsushi Nemoto, linux-mips, florian, nbd, ejka,
nico, openwrt-devel, akpm
On Wed, Sep 12, 2007 at 01:22:04PM +0200, Thomas Bogendoerfer wrote:
> probably nothing, but having a generic decision whether we need
> a version with j or jr will help other platforms as well. Why make
> AR7 a special case ?
The destination is potencially outside the range of the j instruction.
Which is something that reasonably be expected on other systems as
well. One scenario for example is having a few k of RAM at phys 0
then the rest starting at 256MB phys.
It's somewhat easier with an ebase register of course but older cores
don't have that.
Ralf
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH][MIPS][2/7] AR7: mtd partition map
[not found] <200709080143.12345.technoboy85@gmail.com>
@ 2007-09-08 0:19 ` Matteo Croce
2007-09-08 0:19 ` Matteo Croce
` (5 subsequent siblings)
6 siblings, 0 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:19 UTC (permalink / raw)
To: linux-mips
Cc: Felix Fietkau, Eugene Konev, dwmw2, linux-mtd, openwrt-devel,
Andrew Morton
Partition map support
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index fbec8cd..c1b2508 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -150,6 +150,12 @@ config MTD_AFS_PARTS
for your particular device. It won't happen automatically. The
'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
+config MTD_AR7_PARTS
+ tristate "TI AR7 partitioning support"
+ depends on MTD_PARTITIONS
+ ---help---
+ TI AR7 partitioning support
+
comment "User Modules And Translation Layers"
config MTD_CHAR
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 6d958a4..8451c64 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c
new file mode 100644
index 0000000..72dd07a
--- /dev/null
+++ b/drivers/mtd/ar7part.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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
+ *
+ * TI AR7 flash partition table.
+ * Based on ar7 map by Felix Fietkau.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/bootmem.h>
+#include <linux/squashfs_fs.h>
+
+struct ar7_bin_rec {
+ unsigned int checksum;
+ unsigned int length;
+ unsigned int address;
+};
+
+static struct mtd_partition ar7_parts[5];
+
+static int create_mtd_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ unsigned long origin)
+{
+ struct ar7_bin_rec header;
+ unsigned int offset, len;
+ unsigned int pre_size = master->erasesize;
+ unsigned int post_size = 0, root_offset = 0xe0000;
+ int retries = 10;
+
+ printk(KERN_INFO "Parsing AR7 partition map...\n");
+
+ ar7_parts[0].name = "loader";
+ ar7_parts[0].offset = 0;
+ ar7_parts[0].size = master->erasesize;
+ ar7_parts[0].mask_flags = MTD_WRITEABLE;
+
+ ar7_parts[1].name = "config";
+ ar7_parts[1].offset = 0;
+ ar7_parts[1].size = master->erasesize;
+ ar7_parts[1].mask_flags = 0;
+
+ do {
+ offset = pre_size;
+ master->read(master, offset,
+ sizeof(header), &len, (u_char *)&header);
+ if (!strncmp((char *)&header, "TIENV0.8", 8))
+ ar7_parts[1].offset = pre_size;
+ if (header.checksum == 0xfeedfa42)
+ break;
+ if (header.checksum == 0xfeed1281)
+ break;
+ pre_size += master->erasesize;
+ } while (retries--);
+
+ pre_size = offset;
+
+ if (!ar7_parts[1].offset) {
+ ar7_parts[1].offset = master->size - master->erasesize;
+ post_size = master->erasesize;
+ }
+
+ switch (header.checksum) {
+ case 0xfeedfa42:
+ while (header.length) {
+ offset += sizeof(header) + header.length;
+ master->read(master, offset, sizeof(header),
+ &len, (u_char *)&header);
+ }
+ root_offset = offset + sizeof(header) + 4;
+ break;
+ case 0xfeed1281:
+ while (header.length) {
+ offset += sizeof(header) + header.length;
+ master->read(master, offset, sizeof(header),
+ &len, (u_char *)&header);
+ }
+ root_offset = offset + sizeof(header) + 4 + 0xff;
+ root_offset &= ~(u32)0xff;
+ break;
+ default:
+ printk(KERN_ERR "Unknown magic: %08x\n", header.checksum);
+ break;
+ }
+
+ master->read(master, root_offset,
+ sizeof(header), &len, (u_char *)&header);
+ if (header.checksum != SQUASHFS_MAGIC) {
+ root_offset += master->erasesize - 1;
+ root_offset &= ~(master->erasesize - 1);
+ }
+
+ ar7_parts[2].name = "linux";
+ ar7_parts[2].offset = pre_size;
+ ar7_parts[2].size = master->size - pre_size - post_size;
+ ar7_parts[2].mask_flags = 0;
+
+ ar7_parts[3].name = "rootfs";
+ ar7_parts[3].offset = root_offset;
+ ar7_parts[3].size = master->size - root_offset - post_size;
+ ar7_parts[3].mask_flags = 0;
+
+ *pparts = ar7_parts;
+ return 4;
+}
+
+static struct mtd_part_parser ar7_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = create_mtd_partitions,
+ .name = "ar7part",
+};
+
+static int __init ar7_parser_init(void)
+{
+ return register_mtd_parser(&ar7_parser);
+}
+
+module_init(ar7_parser_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Felix Fietkau, Eugene Konev");
+MODULE_DESCRIPTION("MTD partitioning for TI AR7");
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH][MIPS][2/7] AR7: mtd partition map
@ 2007-09-08 0:19 ` Matteo Croce
0 siblings, 0 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:19 UTC (permalink / raw)
To: linux-mips
Cc: Eugene Konev, Felix Fietkau, linux-mtd, Andrew Morton,
openwrt-devel, dwmw2
Partition map support
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index fbec8cd..c1b2508 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -150,6 +150,12 @@ config MTD_AFS_PARTS
for your particular device. It won't happen automatically. The
'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
+config MTD_AR7_PARTS
+ tristate "TI AR7 partitioning support"
+ depends on MTD_PARTITIONS
+ ---help---
+ TI AR7 partitioning support
+
comment "User Modules And Translation Layers"
config MTD_CHAR
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 6d958a4..8451c64 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c
new file mode 100644
index 0000000..72dd07a
--- /dev/null
+++ b/drivers/mtd/ar7part.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007 Felix Fietkau, Eugene Konev
+ *
+ * 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
+ *
+ * TI AR7 flash partition table.
+ * Based on ar7 map by Felix Fietkau.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/bootmem.h>
+#include <linux/squashfs_fs.h>
+
+struct ar7_bin_rec {
+ unsigned int checksum;
+ unsigned int length;
+ unsigned int address;
+};
+
+static struct mtd_partition ar7_parts[5];
+
+static int create_mtd_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ unsigned long origin)
+{
+ struct ar7_bin_rec header;
+ unsigned int offset, len;
+ unsigned int pre_size = master->erasesize;
+ unsigned int post_size = 0, root_offset = 0xe0000;
+ int retries = 10;
+
+ printk(KERN_INFO "Parsing AR7 partition map...\n");
+
+ ar7_parts[0].name = "loader";
+ ar7_parts[0].offset = 0;
+ ar7_parts[0].size = master->erasesize;
+ ar7_parts[0].mask_flags = MTD_WRITEABLE;
+
+ ar7_parts[1].name = "config";
+ ar7_parts[1].offset = 0;
+ ar7_parts[1].size = master->erasesize;
+ ar7_parts[1].mask_flags = 0;
+
+ do {
+ offset = pre_size;
+ master->read(master, offset,
+ sizeof(header), &len, (u_char *)&header);
+ if (!strncmp((char *)&header, "TIENV0.8", 8))
+ ar7_parts[1].offset = pre_size;
+ if (header.checksum == 0xfeedfa42)
+ break;
+ if (header.checksum == 0xfeed1281)
+ break;
+ pre_size += master->erasesize;
+ } while (retries--);
+
+ pre_size = offset;
+
+ if (!ar7_parts[1].offset) {
+ ar7_parts[1].offset = master->size - master->erasesize;
+ post_size = master->erasesize;
+ }
+
+ switch (header.checksum) {
+ case 0xfeedfa42:
+ while (header.length) {
+ offset += sizeof(header) + header.length;
+ master->read(master, offset, sizeof(header),
+ &len, (u_char *)&header);
+ }
+ root_offset = offset + sizeof(header) + 4;
+ break;
+ case 0xfeed1281:
+ while (header.length) {
+ offset += sizeof(header) + header.length;
+ master->read(master, offset, sizeof(header),
+ &len, (u_char *)&header);
+ }
+ root_offset = offset + sizeof(header) + 4 + 0xff;
+ root_offset &= ~(u32)0xff;
+ break;
+ default:
+ printk(KERN_ERR "Unknown magic: %08x\n", header.checksum);
+ break;
+ }
+
+ master->read(master, root_offset,
+ sizeof(header), &len, (u_char *)&header);
+ if (header.checksum != SQUASHFS_MAGIC) {
+ root_offset += master->erasesize - 1;
+ root_offset &= ~(master->erasesize - 1);
+ }
+
+ ar7_parts[2].name = "linux";
+ ar7_parts[2].offset = pre_size;
+ ar7_parts[2].size = master->size - pre_size - post_size;
+ ar7_parts[2].mask_flags = 0;
+
+ ar7_parts[3].name = "rootfs";
+ ar7_parts[3].offset = root_offset;
+ ar7_parts[3].size = master->size - root_offset - post_size;
+ ar7_parts[3].mask_flags = 0;
+
+ *pparts = ar7_parts;
+ return 4;
+}
+
+static struct mtd_part_parser ar7_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = create_mtd_partitions,
+ .name = "ar7part",
+};
+
+static int __init ar7_parser_init(void)
+{
+ return register_mtd_parser(&ar7_parser);
+}
+
+module_init(ar7_parser_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Felix Fietkau, Eugene Konev");
+MODULE_DESCRIPTION("MTD partitioning for TI AR7");
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH][MIPS][3/7] AR7: gpio char device
[not found] <200709080143.12345.technoboy85@gmail.com>
2007-09-08 0:18 ` [PATCH][MIPS][1/7] AR7: core support Matteo Croce
2007-09-08 0:19 ` Matteo Croce
@ 2007-09-08 0:20 ` Matteo Croce
2007-09-08 0:20 ` [PATCH][MIPS][4/7] AR7: leds driver Matteo Croce
` (3 subsequent siblings)
6 siblings, 0 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:20 UTC (permalink / raw)
To: linux-mips; +Cc: Nicolas Thill, openwrt-devel, Andrew Morton
Char device to access GPIO pins
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index b391776..d56cfd7 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -928,6 +928,15 @@ config MWAVE
To compile this driver as a module, choose M here: the
module will be called mwave.
+config AR7_GPIO
+ tristate "TI AR7 GPIO Support"
+ depends on AR7
+ help
+ Give userspace access to the GPIO pins on the Texas Instruments AR7
+ processors.
+
+ If compiled as a module, it will be called ar7_gpio.
+
config SCx200_GPIO
tristate "NatSemi SCx200 GPIO Support"
depends on SCx200
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index d68ddbe..804319e 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_COBALT_LCD) += lcd.o
obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
+obj-$(CONFIG_AR7_GPIO) += ar7_gpio.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
diff --git a/drivers/char/ar7_gpio.c b/drivers/char/ar7_gpio.c
new file mode 100644
index 0000000..d57a23e
--- /dev/null
+++ b/drivers/char/ar7_gpio.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2007 Nicolas Thill <nico@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/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <gpio.h>
+
+#define DRVNAME "ar7_gpio"
+#define LONGNAME "TI AR7 GPIOs Driver"
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+
+static int ar7_gpio_major;
+
+static ssize_t ar7_gpio_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ int pin = iminor(file->f_dentry->d_inode);
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ gpio_set_value(pin, 0);
+ break;
+ case '1':
+ gpio_set_value(pin, 1);
+ break;
+ case 'd':
+ case 'D':
+ ar7_gpio_disable(pin);
+ break;
+ case 'e':
+ case 'E':
+ ar7_gpio_enable(pin);
+ break;
+ case 'i':
+ case 'I':
+ case '<':
+ gpio_direction_input(pin);
+ break;
+ case 'o':
+ case 'O':
+ case '>':
+ gpio_direction_output(pin);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return len;
+}
+
+static ssize_t ar7_gpio_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ int pin = iminor(file->f_dentry->d_inode);
+ int value;
+
+ value = gpio_get_value(pin);
+ if (put_user(value ? '1' : '0', buf))
+ return -EFAULT;
+
+ return 1;
+}
+
+static int ar7_gpio_open(struct inode *inode, struct file *file)
+{
+ int m = iminor(inode);
+
+ if (m >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ return nonseekable_open(inode, file);
+}
+
+static int ar7_gpio_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations ar7_gpio_fops = {
+ .owner = THIS_MODULE,
+ .write = ar7_gpio_write,
+ .read = ar7_gpio_read,
+ .open = ar7_gpio_open,
+ .release = ar7_gpio_release,
+ .llseek = no_llseek,
+};
+
+static struct platform_device *ar7_gpio_device;
+
+static int __init ar7_gpio_init(void)
+{
+ int rc;
+
+ ar7_gpio_device = platform_device_alloc(DRVNAME, -1);
+ if (!ar7_gpio_device)
+ return -ENOMEM;
+
+ rc = platform_device_add(ar7_gpio_device);
+ if (rc < 0)
+ goto out_put;
+
+ rc = register_chrdev(ar7_gpio_major, DRVNAME, &ar7_gpio_fops);
+ if (rc < 0)
+ goto out_put;
+
+ ar7_gpio_major = rc;
+
+ rc = 0;
+
+ goto out;
+
+out_put:
+ platform_device_put(ar7_gpio_device);
+out:
+ return rc;
+}
+
+static void __exit ar7_gpio_exit(void)
+{
+ unregister_chrdev(ar7_gpio_major, DRVNAME);
+ platform_device_unregister(ar7_gpio_device);
+}
+
+module_init(ar7_gpio_init);
+module_exit(ar7_gpio_exit);
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH][MIPS][4/7] AR7: leds driver
[not found] <200709080143.12345.technoboy85@gmail.com>
` (2 preceding siblings ...)
2007-09-08 0:20 ` [PATCH][MIPS][3/7] AR7: gpio char device Matteo Croce
@ 2007-09-08 0:20 ` Matteo Croce
2007-09-11 21:43 ` Richard Purdie
2007-09-08 0:21 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
` (2 subsequent siblings)
6 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:20 UTC (permalink / raw)
To: linux-mips; +Cc: rpurdie, Nicolas Thill, openwrt-devel, Andrew Morton
Support for the leds in front of the board usually used to show power
status, network traffic, connected eth devices etc.
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 4468cb3..b1c7a32 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -18,6 +18,12 @@ config LEDS_CLASS
comment "LED drivers"
+config LEDS_AR7
+ tristate "LED Support for the TI AR7"
+ depends LEDS_CLASS && AR7
+ help
+ This option enables support for the LEDs on TI AR7.
+
config LEDS_CORGI
tristate "LED Support for the Sharp SL-C7x0 series"
depends on LEDS_CLASS && PXA_SHARP_C7xx
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index f8995c9..6d78192 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
+obj-$(CONFIG_LEDS_AR7) += leds-ar7.o
obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o
diff --git a/drivers/leds/leds-ar7.c b/drivers/leds/leds-ar7.c
new file mode 100644
index 0000000..cf0afec
--- /dev/null
+++ b/drivers/leds/leds-ar7.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007 Nicolas Thill <nico@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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <gpio.h>
+
+#define DRVNAME "ar7-leds"
+#define LONGNAME "TI AR7 LEDs driver"
+#define AR7_GPIO_BIT_STATUS_LED 8
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+
+static void ar7_status_led_set(struct led_classdev *pled,
+ enum led_brightness value)
+{
+ gpio_set_value(AR7_GPIO_BIT_STATUS_LED, value ? 0 : 1);
+}
+
+static struct led_classdev ar7_status_led = {
+ .name = "ar7:status",
+ .brightness_set = ar7_status_led_set,
+};
+
+#ifdef CONFIG_PM
+static int ar7_leds_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ led_classdev_suspend(&ar7_status_led);
+ return 0;
+}
+
+static int ar7_leds_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&ar7_status_led);
+ return 0;
+}
+#else /* CONFIG_PM */
+#define ar7_leds_suspend NULL
+#define ar7_leds_resume NULL
+#endif /* CONFIG_PM */
+
+static int ar7_leds_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ rc = led_classdev_register(&pdev->dev, &ar7_status_led);
+ if (rc < 0)
+ goto out;
+
+ ar7_gpio_enable(AR7_GPIO_BIT_STATUS_LED);
+ gpio_direction_output(AR7_GPIO_BIT_STATUS_LED);
+
+out:
+ return rc;
+}
+
+static int ar7_leds_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&ar7_status_led);
+
+ return 0;
+}
+
+static struct platform_device *ar7_leds_device;
+
+static struct platform_driver ar7_leds_driver = {
+ .probe = ar7_leds_probe,
+ .remove = ar7_leds_remove,
+ .suspend = ar7_leds_suspend,
+ .resume = ar7_leds_resume,
+ .driver = {
+ .name = DRVNAME,
+ },
+};
+
+static int __init ar7_leds_init(void)
+{
+ int rc;
+
+ ar7_leds_device = platform_device_alloc(DRVNAME, -1);
+ if (!ar7_leds_device)
+ return -ENOMEM;
+
+ rc = platform_device_add(ar7_leds_device);
+ if (rc < 0)
+ goto out_put;
+
+ rc = platform_driver_register(&ar7_leds_driver);
+ if (rc < 0)
+ goto out_put;
+
+ goto out;
+
+out_put:
+ platform_device_put(ar7_leds_device);
+out:
+ return rc;
+}
+
+static void __exit ar7_leds_exit(void)
+{
+ platform_driver_unregister(&ar7_leds_driver);
+ platform_device_unregister(ar7_leds_device);
+}
+
+module_init(ar7_leds_init);
+module_exit(ar7_leds_exit);
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][4/7] AR7: leds driver
2007-09-08 0:20 ` [PATCH][MIPS][4/7] AR7: leds driver Matteo Croce
@ 2007-09-11 21:43 ` Richard Purdie
0 siblings, 0 replies; 30+ messages in thread
From: Richard Purdie @ 2007-09-11 21:43 UTC (permalink / raw)
To: Matteo Croce; +Cc: linux-mips, Nicolas Thill, openwrt-devel, Andrew Morton
On Sat, 2007-09-08 at 02:20 +0200, Matteo Croce wrote:
> Support for the leds in front of the board usually used to show power
> status, network traffic, connected eth devices etc.
>
> Signed-off-by: Matteo Croce <technoboy85@gmail.com>
> Signed-off-by: Nicolas Thill <nico@openwrt.org>
The usual approach to drivers like this is to add the device definition
(ar7_leds_device) to the platform specific code which is probably in
arch/mips/ somewhere? See arch/arm/mach-pxa/{corgi|poodle|spitz}.c for
example.
Also, does MIPS have any kind of generic GPIO framework? A quick look at
the code suggests you might be able to use drivers/leds/leds-gpio.c
although you might need to add a definition of gpio_set_value_cansleep()
since MIPS appears to lack it.
Regards,
Richard
(LED Maintainer)
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH][MIPS][5/7] AR7: watchdog timer
[not found] <200709080143.12345.technoboy85@gmail.com>
` (3 preceding siblings ...)
2007-09-08 0:20 ` [PATCH][MIPS][4/7] AR7: leds driver Matteo Croce
@ 2007-09-08 0:21 ` Matteo Croce
2007-09-08 0:22 ` [PATCH][MIPS][6/7] AR7: serial Matteo Croce
2007-09-08 0:23 ` [PATCH][MIPS][7/7] AR7: ethernet Matteo Croce
6 siblings, 0 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:21 UTC (permalink / raw)
To: linux-mips
Cc: Nicolas Thill, Enrik Berkhan, Christer Weinigel, wim,
openwrt-devel, Andrew Morton
Driver for the watchdog timer. It worked with 2.4, doesn't does with 2.6.
Apart that it doesn't reboots the device it works :)
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
Signed-off-by: Enrik Berkhan <Enrik.Berkhan@akk.org>
Signed-off-by: Christer Weinigel <wingel@nano-system.com>
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 37bddc1..78d4940 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -583,6 +583,12 @@ config SBC_EPX_C3_WATCHDOG
# MIPS Architecture
+config AR7_WDT
+ tristate "TI AR7 Watchdog Timer"
+ depends on WATCHDOG && AR7
+ help
+ Hardware driver for the TI AR7 Watchdog Timer.
+
config INDYDOG
tristate "Indy/I2 Hardware Watchdog"
depends on SGI_IP22
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 389f8b1..76424f2 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
# M68KNOMMU Architecture
# MIPS Architecture
+obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
obj-$(CONFIG_INDYDOG) += indydog.o
obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
diff --git a/drivers/char/watchdog/ar7_wdt.c b/drivers/char/watchdog/ar7_wdt.c
new file mode 100644
index 0000000..3117ffc
--- /dev/null
+++ b/drivers/char/watchdog/ar7_wdt.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
+ * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
+ *
+ * Some code taken from:
+ * National Semiconductor SCx200 Watchdog support
+ * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+ *
+ * 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 <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/addrspace.h>
+#include <asm/ar7/ar7.h>
+
+#define DRVNAME "ar7_wdt"
+#define LONGNAME "TI AR7 Watchdog Timer"
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int margin = 60;
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+struct ar7_wdt {
+ u32 kick_lock;
+ u32 kick;
+ u32 change_lock;
+ u32 change ;
+ u32 disable_lock;
+ u32 disable;
+ u32 prescale_lock;
+ u32 prescale;
+};
+
+static struct semaphore open_semaphore;
+static unsigned expect_close;
+
+/* XXX currently fixed, allows max margin ~68.72 secs */
+#define prescale_value 0xFFFF
+
+/* Offset of the WDT registers */
+static unsigned long ar7_regs_wdt;
+/* Pointer to the remapped WDT IO space */
+static struct ar7_wdt *ar7_wdt;
+static void ar7_wdt_get_regs(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7100:
+ case AR7_CHIP_7200:
+ ar7_regs_wdt = AR7_REGS_WDT;
+ break;
+ default:
+ ar7_regs_wdt = UR8_REGS_WDT;
+ break;
+ }
+}
+
+
+static void ar7_wdt_kick(u32 value)
+{
+ ar7_wdt->kick_lock = 0x5555;
+ if ((ar7_wdt->kick_lock & 3) == 1) {
+ ar7_wdt->kick_lock = 0xAAAA;
+ if ((ar7_wdt->kick_lock & 3) == 3) {
+ ar7_wdt->kick = value;
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
+}
+
+static void ar7_wdt_prescale(u32 value)
+{
+ ar7_wdt->prescale_lock = 0x5A5A;
+ if ((ar7_wdt->prescale_lock & 3) == 1) {
+ ar7_wdt->prescale_lock = 0xA5A5;
+ if ((ar7_wdt->prescale_lock & 3) == 3) {
+ ar7_wdt->prescale = value;
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
+}
+
+static void ar7_wdt_change(u32 value)
+{
+ ar7_wdt->change_lock = 0x6666;
+ if ((ar7_wdt->change_lock & 3) == 1) {
+ ar7_wdt->change_lock = 0xBBBB;
+ if ((ar7_wdt->change_lock & 3) == 3) {
+ ar7_wdt->change = value;
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
+}
+
+static void ar7_wdt_disable(u32 value)
+{
+ ar7_wdt->disable_lock = 0x7777;
+ if ((ar7_wdt->disable_lock & 3) == 1) {
+ ar7_wdt->disable_lock = 0xCCCC;
+ if ((ar7_wdt->disable_lock & 3) == 2) {
+ ar7_wdt->disable_lock = 0xDDDD;
+ if ((ar7_wdt->disable_lock & 3) == 3) {
+ ar7_wdt->disable = value;
+ return;
+ }
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
+}
+
+static void ar7_wdt_update_margin(int new_margin)
+{
+ u32 change;
+
+ change = new_margin * (ar7_vbus_freq() / prescale_value);
+ if (change < 1) change = 1;
+ if (change > 0xFFFF) change = 0xFFFF;
+ ar7_wdt_change(change);
+ margin = change * prescale_value / ar7_vbus_freq();
+ printk(KERN_INFO DRVNAME
+ ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
+ margin, prescale_value, change, ar7_vbus_freq());
+}
+
+static void ar7_wdt_enable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
+ ar7_wdt_disable(1);
+ ar7_wdt_kick(1);
+}
+
+static void ar7_wdt_disable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
+ ar7_wdt_disable(0);
+}
+
+static int ar7_wdt_open(struct inode *inode, struct file *file)
+{
+ /* only allow one at a time */
+ if (down_trylock(&open_semaphore))
+ return -EBUSY;
+ ar7_wdt_enable_wdt();
+ expect_close = 0;
+
+ return 0;
+}
+
+static int ar7_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!expect_close) {
+ printk(KERN_WARNING DRVNAME
+ ": watchdog device closed unexpectedly,"
+ "will not disable the watchdog timer\n");
+ } else if (!nowayout) {
+ ar7_wdt_disable_wdt();
+ }
+ up(&open_semaphore);
+
+ return 0;
+}
+
+static int ar7_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_HALT || code == SYS_POWER_OFF)
+ if (!nowayout)
+ ar7_wdt_disable_wdt();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ar7_wdt_notifier = {
+ .notifier_call = ar7_wdt_notify_sys
+};
+
+static ssize_t ar7_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /* check for a magic close character */
+ if (len) {
+ size_t i;
+
+ ar7_wdt_kick(1);
+
+ expect_close = 0;
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data+i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 1;
+ }
+
+ }
+ return len;
+}
+
+static int ar7_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info ident = {
+ .identity = LONGNAME,
+ .firmware_version = 1,
+ .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
+ };
+ int new_margin;
+
+ switch (cmd) {
+ default:
+ return -ENOTTY;
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int *)arg))
+ return -EFAULT;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ ar7_wdt_kick(1);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int *)arg))
+ return -EFAULT;
+ if (new_margin < 1)
+ return -EINVAL;
+
+ ar7_wdt_update_margin(new_margin);
+ ar7_wdt_kick(1);
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(margin, (int *)arg))
+ return -EFAULT;
+ return 0;
+ }
+}
+
+static struct file_operations ar7_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ar7_wdt_write,
+ .ioctl = ar7_wdt_ioctl,
+ .open = ar7_wdt_open,
+ .release = ar7_wdt_release,
+};
+
+static struct miscdevice ar7_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ar7_wdt_fops,
+};
+
+static int __init ar7_wdt_init(void)
+{
+ int rc;
+
+ ar7_wdt_get_regs();
+
+ if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt),
+ LONGNAME)) {
+ printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
+ return -EBUSY;
+ }
+
+ ar7_wdt = (struct ar7_wdt *)
+ ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt));
+
+ ar7_wdt_disable_wdt();
+ ar7_wdt_prescale(prescale_value);
+ ar7_wdt_update_margin(margin);
+
+ sema_init(&open_semaphore, 1);
+
+ rc = misc_register(&ar7_wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR DRVNAME ": unable to register misc device\n");
+ goto out_alloc;
+ }
+
+ rc = register_reboot_notifier(&ar7_wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR DRVNAME
+ ": unable to register reboot notifier\n");
+ goto out_register;
+ }
+ goto out;
+
+out_register:
+ misc_deregister(&ar7_wdt_miscdev);
+out_alloc:
+ release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
+out:
+ return rc;
+}
+
+static void __exit ar7_wdt_cleanup(void)
+{
+ unregister_reboot_notifier(&ar7_wdt_notifier);
+ misc_deregister(&ar7_wdt_miscdev);
+ iounmap(ar7_wdt);
+ release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
+}
+
+module_init(ar7_wdt_init);
+module_exit(ar7_wdt_cleanup);
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH][MIPS][6/7] AR7: serial
[not found] <200709080143.12345.technoboy85@gmail.com>
` (4 preceding siblings ...)
2007-09-08 0:21 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
@ 2007-09-08 0:22 ` Matteo Croce
2007-09-08 0:23 ` [PATCH][MIPS][7/7] AR7: ethernet Matteo Croce
6 siblings, 0 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:22 UTC (permalink / raw)
To: linux-mips
Cc: Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial,
openwrt-devel, Andrew Morton
Serial support, should not broke other archs
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index f94109c..94253b7 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -267,6 +267,13 @@ static const struct serial8250_config uart_config[] = {
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO,
},
+ [PORT_AR7] = {
+ .name = "TI-AR7",
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
};
#if defined (CONFIG_SERIAL_8250_AU1X00)
@@ -2453,7 +2460,11 @@ static void serial8250_console_putchar(struct uart_port *port, int ch)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
+#ifdef CONFIG_AR7
+ wait_for_xmitr(up, BOTH_EMPTY);
+#else
wait_for_xmitr(up, UART_LSR_THRE);
+#endif
serial_out(up, UART_TX, ch);
}
diff --git a/include/linux/serialP.h b/include/linux/serialP.h
index e811a61..ba5734a 100644
--- a/include/linux/serialP.h
+++ b/include/linux/serialP.h
@@ -135,6 +135,9 @@ struct rs_multiport_struct {
* the interrupt line _up_ instead of down, so if we register the IRQ
* while the UART is in that state, we die in an IRQ storm. */
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
+#elif defined(CONFIG_AR7)
+/* This is how it is set up by bootloader... */
+#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2|UART_MCR_OUT1|UART_MCR_RTS|UART_MCR_DTR)
#else
#define ALPHA_KLUDGE_MCR 0
#endif
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 09d17b0..8ad2c3b 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -40,6 +40,7 @@
#define PORT_NS16550A 14
#define PORT_XSCALE 15
#define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */
+#define PORT_AR7 16
#define PORT_MAX_8250 16 /* max port ID */
/*
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH][MIPS][7/7] AR7: ethernet
[not found] <200709080143.12345.technoboy85@gmail.com>
` (5 preceding siblings ...)
2007-09-08 0:22 ` [PATCH][MIPS][6/7] AR7: serial Matteo Croce
@ 2007-09-08 0:23 ` Matteo Croce
2007-09-12 16:50 ` Ralf Baechle
6 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-08 0:23 UTC (permalink / raw)
To: linux-mips
Cc: Eugene Konev, netdev, davem, kuznet, pekkas, jmorris, yoshfuji,
kaber, openwrt-devel, Andrew Morton, Jeff Garzik
Driver for the cpmac 100M ethernet driver.
It works fine disabling napi support, enabling it gives a kernel panic
when the first IPv6 packet has to be forwarded.
Other than that works fine.
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index d9b7d9c..6f38a84 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1822,6 +1822,15 @@ config SC92031
To compile this driver as a module, choose M here: the module
will be called sc92031. This is recommended.
+config CPMAC
+ tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
+ depends on NET_ETHERNET && EXPERIMENTAL && AR7
+ select PHYLIB
+ select FIXED_PHY
+ select FIXED_MII_100_FDX
+ help
+ TI AR7 CPMAC Ethernet support
+
config NET_POCKET
bool "Pocket and portable adapters"
depends on PARPORT
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 535d2a0..bb22df9 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_8139CP) += 8139cp.o
obj-$(CONFIG_8139TOO) += 8139too.o
obj-$(CONFIG_ZNET) += znet.o
obj-$(CONFIG_LAN_SAA9730) += saa9730.o
+obj-$(CONFIG_CPMAC) += cpmac.o
obj-$(CONFIG_DEPCA) += depca.o
obj-$(CONFIG_EWRK3) += ewrk3.o
obj-$(CONFIG_ATP) += atp.o
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
new file mode 100644
index 0000000..c10ab08
--- /dev/null
+++ b/drivers/net/cpmac.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (C) 2006, 2007 Eugene Konev
+ *
+ * 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 <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <asm/ar7/ar7.h>
+#include <gpio.h>
+
+MODULE_AUTHOR("Eugene Konev");
+MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
+MODULE_LICENSE("GPL");
+
+static int rx_ring_size = 64;
+static int disable_napi;
+module_param(rx_ring_size, int, 64);
+module_param(disable_napi, int, 0);
+MODULE_PARM_DESC(rx_ring_size, "Size of rx ring (in skbs)");
+MODULE_PARM_DESC(disable_napi, "Disable NAPI polling");
+
+/* Register definitions */
+struct cpmac_control_regs {
+ u32 revision;
+ u32 control;
+ u32 teardown;
+ u32 unused;
+} __attribute__ ((packed));
+
+struct cpmac_int_regs {
+ u32 stat_raw;
+ u32 stat_masked;
+ u32 enable;
+ u32 clear;
+} __attribute__ ((packed));
+
+struct cpmac_stats {
+ u32 good;
+ u32 bcast;
+ u32 mcast;
+ u32 pause;
+ u32 crc_error;
+ u32 align_error;
+ u32 oversized;
+ u32 jabber;
+ u32 undersized;
+ u32 fragment;
+ u32 filtered;
+ u32 qos_filtered;
+ u32 octets;
+} __attribute__ ((packed));
+
+struct cpmac_regs {
+ struct cpmac_control_regs tx_ctrl;
+ struct cpmac_control_regs rx_ctrl;
+ u32 unused1[56];
+ u32 mbp;
+/* MBP bits */
+#define MBP_RXPASSCRC 0x40000000
+#define MBP_RXQOS 0x20000000
+#define MBP_RXNOCHAIN 0x10000000
+#define MBP_RXCMF 0x01000000
+#define MBP_RXSHORT 0x00800000
+#define MBP_RXCEF 0x00400000
+#define MBP_RXPROMISC 0x00200000
+#define MBP_PROMISCCHAN(chan) (((chan) & 0x7) << 16)
+#define MBP_RXBCAST 0x00002000
+#define MBP_BCASTCHAN(chan) (((chan) & 0x7) << 8)
+#define MBP_RXMCAST 0x00000020
+#define MBP_MCASTCHAN(chan) ((chan) & 0x7)
+ u32 unicast_enable;
+ u32 unicast_clear;
+ u32 max_len;
+ u32 buffer_offset;
+ u32 filter_flow_threshold;
+ u32 unused2[2];
+ u32 flow_thre[8];
+ u32 free_buffer[8];
+ u32 mac_control;
+#define MAC_TXPTYPE 0x00000200
+#define MAC_TXPACE 0x00000040
+#define MAC_MII 0x00000020
+#define MAC_TXFLOW 0x00000010
+#define MAC_RXFLOW 0x00000008
+#define MAC_MTEST 0x00000004
+#define MAC_LOOPBACK 0x00000002
+#define MAC_FDX 0x00000001
+ u32 mac_status;
+#define MACST_QOS 0x4
+#define MACST_RXFLOW 0x2
+#define MACST_TXFLOW 0x1
+ u32 emc_control;
+ u32 unused3;
+ struct cpmac_int_regs tx_int;
+ u32 mac_int_vector;
+/* Int Status bits */
+#define INTST_STATUS 0x80000
+#define INTST_HOST 0x40000
+#define INTST_RX 0x20000
+#define INTST_TX 0x10000
+ u32 mac_eoi_vector;
+ u32 unused4[2];
+ struct cpmac_int_regs rx_int;
+ u32 mac_int_stat_raw;
+ u32 mac_int_stat_masked;
+ u32 mac_int_enable;
+ u32 mac_int_clear;
+ u32 mac_addr_low[8];
+ u32 mac_addr_mid;
+ u32 mac_addr_high;
+ u32 mac_hash_low;
+ u32 mac_hash_high;
+ u32 boff_test;
+ u32 pac_test;
+ u32 rx_pause;
+ u32 tx_pause;
+ u32 unused5[2];
+ struct cpmac_stats rx_stats;
+ struct cpmac_stats tx_stats;
+ u32 unused6[232];
+ u32 tx_ptr[8];
+ u32 rx_ptr[8];
+ u32 tx_ack[8];
+ u32 rx_ack[8];
+
+} __attribute__ ((packed));
+
+struct cpmac_mdio_regs {
+ u32 version;
+ u32 control;
+#define MDIOC_IDLE 0x80000000
+#define MDIOC_ENABLE 0x40000000
+#define MDIOC_PREAMBLE 0x00100000
+#define MDIOC_FAULT 0x00080000
+#define MDIOC_FAULTDETECT 0x00040000
+#define MDIOC_INTTEST 0x00020000
+#define MDIOC_CLKDIV(div) ((div) & 0xff)
+ u32 alive;
+ u32 link;
+ struct cpmac_int_regs link_int;
+ struct cpmac_int_regs user_int;
+ u32 unused[20];
+ volatile u32 access;
+#define MDIO_BUSY 0x80000000
+#define MDIO_WRITE 0x40000000
+#define MDIO_REG(reg) (((reg) & 0x1f) << 21)
+#define MDIO_PHY(phy) (((phy) & 0x1f) << 16)
+#define MDIO_DATA(data) ((data) & 0xffff)
+ u32 physel;
+} __attribute__ ((packed));
+
+/* Descriptor */
+struct cpmac_desc {
+ u32 hw_next;
+ u32 hw_data;
+ u16 buflen;
+ u16 bufflags;
+ u16 datalen;
+ u16 dataflags;
+/* Flags bits */
+#define CPMAC_SOP 0x8000
+#define CPMAC_EOP 0x4000
+#define CPMAC_OWN 0x2000
+#define CPMAC_EOQ 0x1000
+ struct sk_buff *skb;
+ struct cpmac_desc *next;
+} __attribute__ ((packed));
+
+struct cpmac_priv {
+ struct net_device_stats stats;
+ spinlock_t lock; /* irq{save,restore} */
+ struct sk_buff *skb_pool;
+ int free_skbs;
+ struct cpmac_desc *rx_head;
+ int tx_head, tx_tail;
+ struct cpmac_desc *desc_ring;
+ struct cpmac_regs *regs;
+ struct mii_bus *mii_bus;
+ struct phy_device *phy;
+ char phy_name[BUS_ID_SIZE];
+ struct plat_cpmac_data *config;
+ int oldlink, oldspeed, oldduplex;
+ u32 msg_enable;
+ struct net_device *dev;
+ struct work_struct alloc_work;
+};
+
+static irqreturn_t cpmac_irq(int, void *);
+static void cpmac_reset(struct net_device *dev);
+static void cpmac_hw_init(struct net_device *dev);
+static int cpmac_stop(struct net_device *dev);
+static int cpmac_open(struct net_device *dev);
+
+#undef CPMAC_DEBUG
+#define CPMAC_LOW_THRESH 32
+#define CPMAC_ALLOC_SIZE 64
+#define CPMAC_SKB_SIZE 1518
+#define CPMAC_TX_RING_SIZE 8
+
+#ifdef CPMAC_DEBUG
+static void cpmac_dump_regs(u32 *base, int count)
+{
+ int i;
+ for (i = 0; i < (count + 3) / 4; i++) {
+ if (i % 4 == 0) printk(KERN_DEBUG "\nCPMAC[0x%04x]:", i * 4);
+ printk(KERN_DEBUG " 0x%08x", *(base + i));
+ }
+ printk(KERN_DEBUG "\n");
+}
+
+static const char *cpmac_dump_buf(const uint8_t *buf, unsigned size)
+{
+ static char buffer[3 * 25 + 1];
+ char *p = &buffer[0];
+ if (size > 20)
+ size = 20;
+ while (size-- > 0)
+ p += sprintf(p, " %02x", *buf++);
+ return buffer;
+}
+#endif
+
+static int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct cpmac_mdio_regs *regs = bus->priv;
+ u32 val;
+
+ while ((val = regs->access) & MDIO_BUSY);
+ regs->access = MDIO_BUSY | MDIO_REG(regnum & 0x1f) |
+ MDIO_PHY(phy_id & 0x1f);
+ while ((val = regs->access) & MDIO_BUSY);
+
+ return val & 0xffff;
+}
+
+static int cpmac_mdio_write(struct mii_bus *bus, int phy_id,
+ int regnum, u16 val)
+{
+ struct cpmac_mdio_regs *regs = bus->priv;
+
+ while (regs->access & MDIO_BUSY);
+ regs->access = MDIO_BUSY | MDIO_WRITE |
+ MDIO_REG(regnum & 0x1f) | MDIO_PHY(phy_id & 0x1f) | val;
+
+ return 0;
+}
+
+static int cpmac_mdio_reset(struct mii_bus *bus)
+{
+ ar7_device_reset(AR7_RESET_BIT_MDIO);
+ ((struct cpmac_mdio_regs *)bus->priv)->control = MDIOC_ENABLE |
+ MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1);
+
+ return 0;
+}
+
+static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
+
+static struct mii_bus cpmac_mii = {
+ .name = "cpmac-mii",
+ .read = cpmac_mdio_read,
+ .write = cpmac_mdio_write,
+ .reset = cpmac_mdio_reset,
+ .irq = mii_irqs,
+};
+
+static int cpmac_config(struct net_device *dev, struct ifmap *map)
+{
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ /* Don't allow changing the I/O address */
+ if (map->base_addr != dev->base_addr)
+ return -EOPNOTSUPP;
+
+ /* ignore other fields */
+ return 0;
+}
+
+static int cpmac_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+ return 0;
+}
+
+static void cpmac_set_multicast_list(struct net_device *dev)
+{
+ struct dev_mc_list *iter;
+ int i;
+ int hash, tmp;
+ int hashlo = 0, hashhi = 0;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ priv->regs->mbp &= ~MBP_PROMISCCHAN(0); /* promisc channel 0 */
+ priv->regs->mbp |= MBP_RXPROMISC;
+ } else {
+ priv->regs->mbp &= ~MBP_RXPROMISC;
+ if (dev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ priv->regs->mac_hash_low = 0xffffffff;
+ priv->regs->mac_hash_high = 0xffffffff;
+ } else {
+ for (i = 0, iter = dev->mc_list; i < dev->mc_count;
+ i++, iter = iter->next) {
+ hash = 0;
+ tmp = iter->dmi_addr[0];
+ hash ^= (tmp >> 2) ^ (tmp << 4);
+ tmp = iter->dmi_addr[1];
+ hash ^= (tmp >> 4) ^ (tmp << 2);
+ tmp = iter->dmi_addr[2];
+ hash ^= (tmp >> 6) ^ tmp;
+ tmp = iter->dmi_addr[4];
+ hash ^= (tmp >> 2) ^ (tmp << 4);
+ tmp = iter->dmi_addr[5];
+ hash ^= (tmp >> 4) ^ (tmp << 2);
+ tmp = iter->dmi_addr[6];
+ hash ^= (tmp >> 6) ^ tmp;
+ hash &= 0x3f;
+ if (hash < 32) {
+ hashlo |= 1<<hash;
+ } else {
+ hashhi |= 1<<(hash - 32);
+ }
+ }
+
+ priv->regs->mac_hash_low = hashlo;
+ priv->regs->mac_hash_high = hashhi;
+ }
+ }
+}
+
+static struct sk_buff *cpmac_get_skb(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ skb = priv->skb_pool;
+ if (likely(skb))
+ priv->skb_pool = skb->next;
+ else {
+ skb = dev_alloc_skb(CPMAC_SKB_SIZE + 2);
+ if (skb) {
+ skb->next = NULL;
+ skb_reserve(skb, 2);
+ skb->dev = priv->dev;
+ }
+ }
+
+ if (likely(priv->free_skbs))
+ priv->free_skbs--;
+
+ if (priv->free_skbs < CPMAC_LOW_THRESH)
+ schedule_work(&priv->alloc_work);
+
+ return skb;
+}
+
+static struct sk_buff *cpmac_rx_one(struct net_device *dev,
+ struct cpmac_priv *priv,
+ struct cpmac_desc *desc)
+{
+ unsigned long flags;
+ char *data;
+ struct sk_buff *skb, *result = NULL;
+
+ priv->regs->rx_ack[0] = virt_to_phys(desc);
+ if (unlikely(!desc->datalen)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING "%s: rx: spurious interrupt\n",
+ dev->name);
+ priv->stats.rx_errors++;
+ return NULL;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ skb = cpmac_get_skb(dev);
+ if (likely(skb)) {
+ data = (char *)phys_to_virt(desc->hw_data);
+ dma_cache_inv((u32)data, desc->datalen);
+ skb_put(desc->skb, desc->datalen);
+ desc->skb->protocol = eth_type_trans(desc->skb, dev);
+ desc->skb->ip_summed = CHECKSUM_NONE;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += desc->datalen;
+ result = desc->skb;
+ desc->skb = skb;
+ } else {
+#ifdef CPMAC_DEBUG
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "%s: low on skbs, dropping packet\n",
+ dev->name);
+#endif
+ priv->stats.rx_dropped++;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ desc->hw_data = virt_to_phys(desc->skb->data);
+ desc->buflen = CPMAC_SKB_SIZE;
+ desc->dataflags = CPMAC_OWN;
+ dma_cache_wback((u32)desc, 16);
+
+ return result;
+}
+
+static void cpmac_rx(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct cpmac_desc *desc;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+ if (unlikely(!priv->rx_head)) {
+ spin_unlock(&priv->lock);
+ return;
+ }
+
+ desc = priv->rx_head;
+ dma_cache_inv((u32)desc, 16);
+#ifdef CPMAC_DEBUG
+ printk(KERN_DEBUG "%s: len=%d, %s\n", __func__, pkt->datalen,
+ cpmac_dump_buf(data, pkt->datalen));
+#endif
+
+ while ((desc->dataflags & CPMAC_OWN) == 0) {
+ skb = cpmac_rx_one(dev, priv, desc);
+ if (likely(skb))
+ netif_rx(skb);
+ desc = desc->next;
+ dma_cache_inv((u32)desc, 16);
+ }
+
+ priv->rx_head = desc;
+ priv->regs->rx_ptr[0] = virt_to_phys(desc);
+ spin_unlock(&priv->lock);
+}
+
+static int cpmac_poll(struct net_device *dev, int *budget)
+{
+ struct sk_buff *skb;
+ struct cpmac_desc *desc;
+ int received = 0, quota = min(dev->quota, *budget);
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (unlikely(!priv->rx_head)) {
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "%s: rx: polling, but no queue\n",
+ dev->name);
+ netif_rx_complete(dev);
+ return 0;
+ }
+
+ desc = priv->rx_head;
+ dma_cache_inv((u32)desc, 16);
+
+ while ((received < quota) && ((desc->dataflags & CPMAC_OWN) == 0)) {
+ skb = cpmac_rx_one(dev, priv, desc);
+ if (likely(skb)) {
+ netif_receive_skb(skb);
+ received++;
+ }
+ desc = desc->next;
+ priv->rx_head = desc;
+ dma_cache_inv((u32)desc, 16);
+ }
+
+ *budget -= received;
+ dev->quota -= received;
+#ifdef CPMAC_DEBUG
+ printk(KERN_DEBUG "%s: processed %d packets\n", dev->name, received);
+#endif
+ if (desc->dataflags & CPMAC_OWN) {
+ priv->regs->rx_ptr[0] = virt_to_phys(desc);
+ netif_rx_complete(dev);
+ priv->regs->rx_int.enable = 0x1;
+ priv->regs->rx_int.clear = 0xfe;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+cpmac_alloc_skbs(struct work_struct *work)
+{
+ struct cpmac_priv *priv = container_of(work, struct cpmac_priv,
+ alloc_work);
+ unsigned long flags;
+ int i, num_skbs = 0;
+ struct sk_buff *skb, *skbs = NULL;
+
+ for (i = 0; i < CPMAC_ALLOC_SIZE; i++) {
+ skb = alloc_skb(CPMAC_SKB_SIZE + 2, GFP_KERNEL);
+ if (!skb)
+ break;
+ skb->next = skbs;
+ skb_reserve(skb, 2);
+ skb->dev = priv->dev;
+ num_skbs++;
+ skbs = skb;
+ }
+
+ if (skbs) {
+ spin_lock_irqsave(&priv->lock, flags);
+ for (skb = priv->skb_pool; skb && skb->next; skb = skb->next);
+ if (!skb)
+ priv->skb_pool = skbs;
+ else
+ skb->next = skbs;
+ priv->free_skbs += num_skbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+#ifdef CPMAC_DEBUG
+ printk(KERN_DEBUG "%s: allocated %d skbs\n",
+ priv->dev->name, num_skbs);
+#endif
+ }
+}
+
+static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned long flags;
+ int len, chan;
+ struct cpmac_desc *desc;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ len = skb->len;
+#ifdef CPMAC_DEBUG
+ printk(KERN_DEBUG "%s: len=%d\n", __func__, len);
+ /* cpmac_dump_buf(const uint8_t * buf, unsigned size) */
+#endif
+ if (unlikely(len < ETH_ZLEN)) {
+ if (unlikely(skb_padto(skb, ETH_ZLEN))) {
+ if (printk_ratelimit())
+ printk(KERN_NOTICE
+ "%s: padding failed, dropping\n",
+ dev->name);
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+ len = ETH_ZLEN;
+ }
+ spin_lock_irqsave(&priv->lock, flags);
+ chan = priv->tx_tail++;
+ priv->tx_tail %= 8;
+ if (priv->tx_tail == priv->tx_head)
+ netif_stop_queue(dev);
+
+ desc = &priv->desc_ring[chan];
+ dma_cache_inv((u32)desc, 16);
+ if (desc->dataflags & CPMAC_OWN) {
+ printk(KERN_NOTICE "%s: tx dma ring full, dropping\n",
+ dev->name);
+ priv->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+
+ dev->trans_start = jiffies;
+ desc->dataflags = CPMAC_SOP | CPMAC_EOP | CPMAC_OWN;
+ desc->skb = skb;
+ desc->hw_data = virt_to_phys(skb->data);
+ dma_cache_wback((u32)skb->data, len);
+ desc->buflen = len;
+ desc->datalen = len;
+ desc->hw_next = 0;
+ dma_cache_wback((u32)desc, 16);
+ priv->regs->tx_ptr[chan] = virt_to_phys(desc);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void cpmac_end_xmit(struct net_device *dev, int channel)
+{
+ struct cpmac_desc *desc;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+ desc = &priv->desc_ring[channel];
+ priv->regs->tx_ack[channel] = virt_to_phys(desc);
+ if (likely(desc->skb)) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += desc->skb->len;
+ dev_kfree_skb_irq(desc->skb);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ } else
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "%s: end_xmit: spurious interrupt\n",
+ dev->name);
+ spin_unlock(&priv->lock);
+}
+
+static void cpmac_reset(struct net_device *dev)
+{
+ int i;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ ar7_device_reset(priv->config->reset_bit);
+ priv->regs->rx_ctrl.control &= ~1;
+ priv->regs->tx_ctrl.control &= ~1;
+ for (i = 0; i < 8; i++) {
+ priv->regs->tx_ptr[i] = 0;
+ priv->regs->rx_ptr[i] = 0;
+ }
+ priv->regs->mac_control &= ~MAC_MII; /* disable mii */
+}
+
+static inline void cpmac_free_rx_ring(struct net_device *dev)
+{
+ struct cpmac_desc *desc;
+ int i;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (unlikely(!priv->rx_head))
+ return;
+
+ desc = priv->rx_head;
+ dma_cache_inv((u32)desc, 16);
+
+ for (i = 0; i < rx_ring_size; i++) {
+ desc->buflen = CPMAC_SKB_SIZE;
+ if ((desc->dataflags & CPMAC_OWN) == 0) {
+ desc->dataflags = CPMAC_OWN;
+ priv->stats.rx_dropped++;
+ }
+ dma_cache_wback((u32)desc, 16);
+ desc = desc->next;
+ dma_cache_inv((u32)desc, 16);
+ }
+}
+
+static irqreturn_t cpmac_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct cpmac_priv *priv = netdev_priv(dev);
+ u32 status;
+
+ if (!dev)
+ return IRQ_NONE;
+
+ status = priv->regs->mac_int_vector;
+
+ if (status & INTST_TX)
+ cpmac_end_xmit(dev, (status & 7));
+
+ if (status & INTST_RX) {
+ if (disable_napi)
+ cpmac_rx(dev);
+ else {
+ priv->regs->rx_int.enable = 0;
+ priv->regs->rx_int.clear = 0xff;
+ netif_rx_schedule(dev);
+ }
+ }
+
+ priv->regs->mac_eoi_vector = 0;
+
+ if (unlikely(status & (INTST_HOST | INTST_STATUS))) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "%s: hw error, resetting...\n",
+ dev->name);
+ spin_lock(&priv->lock);
+ phy_stop(priv->phy);
+ cpmac_reset(dev);
+ cpmac_free_rx_ring(dev);
+ cpmac_hw_init(dev);
+ spin_unlock(&priv->lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void cpmac_tx_timeout(struct net_device *dev)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+ struct cpmac_desc *desc;
+
+ priv->stats.tx_errors++;
+ desc = &priv->desc_ring[priv->tx_head++];
+ priv->tx_head %= 8;
+ printk(KERN_NOTICE "%s: transmit timeout\n", dev->name);
+ if (desc->skb)
+ dev_kfree_skb(desc->skb);
+ netif_wake_queue(dev);
+}
+
+static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+ if (!(netif_running(dev)))
+ return -EINVAL;
+ if (!priv->phy)
+ return -EINVAL;
+ if ((cmd == SIOCGMIIPHY) || (cmd == SIOCGMIIREG) ||
+ (cmd == SIOCSMIIREG))
+ return phy_mii_ioctl(priv->phy, if_mii(ifr), cmd);
+
+ return -EINVAL;
+}
+
+static int cpmac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (priv->phy)
+ return phy_ethtool_gset(priv->phy, cmd);
+
+ return -EINVAL;
+}
+
+static int cpmac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (priv->phy)
+ return phy_ethtool_sset(priv->phy, cmd);
+
+ return -EINVAL;
+}
+
+static void cpmac_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, "cpmac");
+ strcpy(info->version, "0.0.3");
+ info->fw_version[0] = '\0';
+ sprintf(info->bus_info, "%s", "cpmac");
+ info->regdump_len = 0;
+}
+
+static const struct ethtool_ops cpmac_ethtool_ops = {
+ .get_settings = cpmac_get_settings,
+ .set_settings = cpmac_set_settings,
+ .get_drvinfo = cpmac_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+static struct net_device_stats *cpmac_stats(struct net_device *dev)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (netif_device_present(dev))
+ return &priv->stats;
+
+ return NULL;
+}
+
+static int cpmac_change_mtu(struct net_device *dev, int mtu)
+{
+ unsigned long flags;
+ struct cpmac_priv *priv = netdev_priv(dev);
+ spinlock_t *lock = &priv->lock;
+
+ if ((mtu < 68) || (mtu > 1500))
+ return -EINVAL;
+
+ spin_lock_irqsave(lock, flags);
+ dev->mtu = mtu;
+ spin_unlock_irqrestore(lock, flags);
+
+ return 0;
+}
+
+static void cpmac_adjust_link(struct net_device *dev)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ int new_state = 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->phy->link) {
+ if (priv->phy->duplex != priv->oldduplex) {
+ new_state = 1;
+ priv->oldduplex = priv->phy->duplex;
+ }
+
+ if (priv->phy->speed != priv->oldspeed) {
+ new_state = 1;
+ priv->oldspeed = priv->phy->speed;
+ }
+
+ if (!priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 1;
+ netif_schedule(dev);
+ }
+ } else if (priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+ }
+
+ if (new_state)
+ phy_print_status(priv->phy);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void cpmac_hw_init(struct net_device *dev)
+{
+ int i;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ for (i = 0; i < 8; i++)
+ priv->regs->tx_ptr[i] = 0;
+ priv->regs->rx_ptr[0] = virt_to_phys(priv->rx_head);
+
+ priv->regs->mbp = MBP_RXSHORT | MBP_RXBCAST | MBP_RXMCAST;
+ priv->regs->unicast_enable = 0x1;
+ priv->regs->unicast_clear = 0xfe;
+ priv->regs->buffer_offset = 0;
+ for (i = 0; i < 8; i++)
+ priv->regs->mac_addr_low[i] = dev->dev_addr[5];
+ priv->regs->mac_addr_mid = dev->dev_addr[4];
+ priv->regs->mac_addr_high = dev->dev_addr[0] | (dev->dev_addr[1] << 8)
+ | (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+ priv->regs->max_len = CPMAC_SKB_SIZE;
+ priv->regs->rx_int.enable = 0x1;
+ priv->regs->rx_int.clear = 0xfe;
+ priv->regs->tx_int.enable = 0xff;
+ priv->regs->tx_int.clear = 0;
+ priv->regs->mac_int_enable = 3;
+ priv->regs->mac_int_clear = 0xfc;
+
+ priv->regs->rx_ctrl.control |= 1;
+ priv->regs->tx_ctrl.control |= 1;
+ priv->regs->mac_control |= MAC_MII | MAC_FDX;
+
+ priv->phy->state = PHY_CHANGELINK;
+ phy_start(priv->phy);
+}
+
+static int cpmac_open(struct net_device *dev)
+{
+ int i, size, res;
+ struct cpmac_priv *priv = netdev_priv(dev);
+ struct cpmac_desc *desc;
+ struct sk_buff *skb;
+
+ priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link,
+ 0, PHY_INTERFACE_MODE_MII);
+ if (IS_ERR(priv->phy)) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(priv->phy);
+ }
+
+ if (!request_mem_region(dev->mem_start, dev->mem_end -
+ dev->mem_start, dev->name)) {
+ printk(KERN_ERR "%s: failed to request registers\n",
+ dev->name);
+ res = -ENXIO;
+ goto fail_reserve;
+ }
+
+ priv->regs = ioremap_nocache(dev->mem_start, dev->mem_end -
+ dev->mem_start);
+ if (!priv->regs) {
+ printk(KERN_ERR "%s: failed to remap registers\n", dev->name);
+ res = -ENXIO;
+ goto fail_remap;
+ }
+
+ priv->rx_head = NULL;
+ size = sizeof(struct cpmac_desc) * (rx_ring_size +
+ CPMAC_TX_RING_SIZE);
+ priv->desc_ring = (struct cpmac_desc *)kmalloc(size, GFP_KERNEL);
+ if (!priv->desc_ring) {
+ res = -ENOMEM;
+ goto fail_alloc;
+ }
+
+ memset((char *)priv->desc_ring, 0, size);
+
+ priv->skb_pool = NULL;
+ priv->free_skbs = 0;
+ priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
+
+ INIT_WORK(&priv->alloc_work, cpmac_alloc_skbs);
+ schedule_work(&priv->alloc_work);
+ flush_scheduled_work();
+
+ for (i = 0; i < rx_ring_size; i++) {
+ desc = &priv->rx_head[i];
+ skb = cpmac_get_skb(dev);
+ if (!skb) {
+ res = -ENOMEM;
+ goto fail_desc;
+ }
+ desc->skb = skb;
+ desc->hw_data = virt_to_phys(skb->data);
+ desc->buflen = CPMAC_SKB_SIZE;
+ desc->dataflags = CPMAC_OWN;
+ desc->next = &priv->rx_head[(i + 1) % rx_ring_size];
+ desc->hw_next = virt_to_phys(desc->next);
+ dma_cache_wback((u32)desc, 16);
+ }
+
+ if ((res = request_irq(dev->irq, cpmac_irq, SA_INTERRUPT,
+ dev->name, dev))) {
+ printk(KERN_ERR "%s: failed to obtain irq\n", dev->name);
+ goto fail_irq;
+ }
+
+ cpmac_reset(dev);
+ cpmac_hw_init(dev);
+
+ netif_start_queue(dev);
+ return 0;
+
+fail_irq:
+fail_desc:
+ for (i = 0; i < rx_ring_size; i++)
+ if (priv->rx_head[i].skb)
+ kfree_skb(priv->rx_head[i].skb);
+fail_alloc:
+ kfree(priv->desc_ring);
+
+ for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
+ priv->skb_pool = skb->next;
+ kfree_skb(skb);
+ }
+
+ iounmap(priv->regs);
+
+fail_remap:
+ release_mem_region(dev->mem_start, dev->mem_end -
+ dev->mem_start);
+
+fail_reserve:
+ phy_disconnect(priv->phy);
+
+ return res;
+}
+
+static int cpmac_stop(struct net_device *dev)
+{
+ int i;
+ struct sk_buff *skb;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ phy_stop(priv->phy);
+ phy_disconnect(priv->phy);
+ priv->phy = NULL;
+
+ cpmac_reset(dev);
+
+ for (i = 0; i < 8; i++) {
+ priv->regs->rx_ptr[i] = 0;
+ priv->regs->tx_ptr[i] = 0;
+ priv->regs->mbp = 0;
+ }
+
+ free_irq(dev->irq, dev);
+ release_mem_region(dev->mem_start, dev->mem_end -
+ dev->mem_start);
+
+ cancel_delayed_work(&priv->alloc_work);
+ flush_scheduled_work();
+
+ priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
+ for (i = 0; i < rx_ring_size; i++)
+ if (priv->rx_head[i].skb)
+ kfree_skb(priv->rx_head[i].skb);
+
+ kfree(priv->desc_ring);
+
+ for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
+ priv->skb_pool = skb->next;
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static int external_switch;
+
+static int __devinit cpmac_probe(struct platform_device *pdev)
+{
+ int i, rc, phy_id;
+ struct resource *res;
+ struct cpmac_priv *priv;
+ struct net_device *dev;
+ struct plat_cpmac_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+
+ for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
+ if (!(pdata->phy_mask & (1 << phy_id)))
+ continue;
+ if (!cpmac_mii.phy_map[phy_id])
+ continue;
+ break;
+ }
+
+ if (phy_id == PHY_MAX_ADDR) {
+ if (external_switch)
+ phy_id = 0;
+ else {
+ printk(KERN_ERR "cpmac: no PHY present\n");
+ return -ENODEV;
+ }
+ }
+
+ dev = alloc_etherdev(sizeof(struct cpmac_priv));
+
+ if (!dev) {
+ printk(KERN_ERR
+ "cpmac: Unable to allocate net_device structure!\n");
+ return -ENOMEM;
+ }
+
+ SET_MODULE_OWNER(dev);
+ platform_set_drvdata(pdev, dev);
+ priv = netdev_priv(dev);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!res) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ dev->mem_start = res->start;
+ dev->mem_end = res->end;
+ dev->irq = platform_get_irq_byname(pdev, "irq");
+
+ dev->mtu = 1500;
+ dev->open = cpmac_open;
+ dev->stop = cpmac_stop;
+ dev->set_config = cpmac_config;
+ dev->hard_start_xmit = cpmac_start_xmit;
+ dev->do_ioctl = cpmac_ioctl;
+ dev->get_stats = cpmac_stats;
+ dev->change_mtu = cpmac_change_mtu;
+ dev->set_mac_address = cpmac_set_mac_address;
+ dev->set_multicast_list = cpmac_set_multicast_list;
+ dev->tx_timeout = cpmac_tx_timeout;
+ dev->ethtool_ops = &cpmac_ethtool_ops;
+ if (!disable_napi) {
+ dev->poll = cpmac_poll;
+ dev->weight = min(rx_ring_size, 64);
+ }
+
+ memset(priv, 0, sizeof(struct cpmac_priv));
+ spin_lock_init(&priv->lock);
+ priv->msg_enable = netif_msg_init(NETIF_MSG_WOL, 0x3fff);
+ priv->config = pdata;
+ priv->dev = dev;
+ memcpy(dev->dev_addr, priv->config->dev_addr, sizeof(dev->dev_addr));
+ if (phy_id == 31)
+ snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT,
+ cpmac_mii.id, phy_id);
+ else
+ snprintf(priv->phy_name, BUS_ID_SIZE, "fixed@%d:%d", 100, 1);
+
+ if ((rc = register_netdev(dev))) {
+ printk(KERN_ERR "cpmac: error %i registering device %s\n",
+ rc, dev->name);
+ goto fail;
+ }
+
+ printk(KERN_INFO "cpmac: device %s (regs: %p, irq: %d, phy: %s, mac: ",
+ dev->name, (u32 *)dev->mem_start, dev->irq,
+ priv->phy_name);
+ for (i = 0; i < 6; i++)
+ printk("%02x%s", dev->dev_addr[i], i < 5 ? ":" : ")\n");
+
+ return 0;
+
+fail:
+ free_netdev(dev);
+ return rc;
+}
+
+static int __devexit cpmac_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ unregister_netdev(dev);
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver cpmac_driver = {
+ .driver.name = "cpmac",
+ .probe = cpmac_probe,
+ .remove = cpmac_remove,
+};
+
+int __devinit cpmac_init(void)
+{
+ u32 mask;
+ int i, res;
+ cpmac_mii.priv =
+ ioremap_nocache(AR7_REGS_MDIO, sizeof(struct cpmac_mdio_regs));
+
+ if (!cpmac_mii.priv) {
+ printk(KERN_ERR "Can't ioremap mdio registers\n");
+ return -ENXIO;
+ }
+
+#warning FIXME: unhardcode gpio&reset bits
+ ar7_gpio_disable(26);
+ ar7_gpio_disable(27);
+ ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
+ ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
+ ar7_device_reset(AR7_RESET_BIT_EPHY);
+
+ cpmac_mii.reset(&cpmac_mii);
+
+ for (i = 0; i < 300000; i++) {
+ mask = ((struct cpmac_mdio_regs *)cpmac_mii.priv)->alive;
+ if (mask)
+ break;
+ }
+
+/* mask &= 0x7fffffff;
+ if (mask & (mask - 1)) {*/
+ external_switch = 1;
+ mask = 0;
+/* }*/
+
+ cpmac_mii.phy_mask = ~(mask | 0x80000000);
+
+ res = mdiobus_register(&cpmac_mii);
+ if (res)
+ goto fail_mii;
+
+ res = platform_driver_register(&cpmac_driver);
+ if (res)
+ goto fail_cpmac;
+
+ return 0;
+
+fail_cpmac:
+ mdiobus_unregister(&cpmac_mii);
+
+fail_mii:
+ iounmap(cpmac_mii.priv);
+
+ return res;
+}
+
+void __devexit cpmac_exit(void)
+{
+ platform_driver_unregister(&cpmac_driver);
+ mdiobus_unregister(&cpmac_mii);
+}
+
+module_init(cpmac_init);
+module_exit(cpmac_exit);
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][7/7] AR7: ethernet
2007-09-08 0:23 ` [PATCH][MIPS][7/7] AR7: ethernet Matteo Croce
@ 2007-09-12 16:50 ` Ralf Baechle
2007-09-13 1:42 ` Thiemo Seufer
0 siblings, 1 reply; 30+ messages in thread
From: Ralf Baechle @ 2007-09-12 16:50 UTC (permalink / raw)
To: Matteo Croce
Cc: linux-mips, Eugene Konev, netdev, davem, kuznet, pekkas, jmorris,
yoshfuji, kaber, openwrt-devel, Andrew Morton, Jeff Garzik
On Sat, Sep 08, 2007 at 02:23:00AM +0200, Matteo Croce wrote:
> Driver for the cpmac 100M ethernet driver.
> It works fine disabling napi support, enabling it gives a kernel panic
> when the first IPv6 packet has to be forwarded.
> Other than that works fine.
>
> Signed-off-by: Matteo Croce <technoboy85@gmail.com>
> Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index d9b7d9c..6f38a84 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -1822,6 +1822,15 @@ config SC92031
> To compile this driver as a module, choose M here: the module
> will be called sc92031. This is recommended.
>
> +config CPMAC
> + tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
> + depends on NET_ETHERNET && EXPERIMENTAL && AR7
The dependency on NET_ETHERNET is not needed because this config block is
enclosed in a
if NET_ETHERNET
...
endif # NET_ETHERNET
block.
> + select PHYLIB
> + select FIXED_PHY
> + select FIXED_MII_100_FDX
> + help
> + TI AR7 CPMAC Ethernet support
> +
> config NET_POCKET
> bool "Pocket and portable adapters"
> depends on PARPORT
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 535d2a0..bb22df9 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -156,6 +156,7 @@ obj-$(CONFIG_8139CP) += 8139cp.o
> obj-$(CONFIG_8139TOO) += 8139too.o
> obj-$(CONFIG_ZNET) += znet.o
> obj-$(CONFIG_LAN_SAA9730) += saa9730.o
> +obj-$(CONFIG_CPMAC) += cpmac.o
> obj-$(CONFIG_DEPCA) += depca.o
> obj-$(CONFIG_EWRK3) += ewrk3.o
> obj-$(CONFIG_ATP) += atp.o
> diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
> new file mode 100644
> index 0000000..c10ab08
> --- /dev/null
> +++ b/drivers/net/cpmac.c
> @@ -0,0 +1,1194 @@
> +/*
> + * Copyright (C) 2006, 2007 Eugene Konev
> + *
> + * 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 <linux/init.h>
> +#include <linux/moduleparam.h>
> +
> +#include <linux/sched.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +#include <linux/version.h>
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/skbuff.h>
> +#include <linux/mii.h>
> +#include <linux/phy.h>
> +#include <linux/platform_device.h>
> +#include <asm/ar7/ar7.h>
> +#include <gpio.h>
> +
> +MODULE_AUTHOR("Eugene Konev");
> +MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
> +MODULE_LICENSE("GPL");
> +
> +static int rx_ring_size = 64;
> +static int disable_napi;
> +module_param(rx_ring_size, int, 64);
> +module_param(disable_napi, int, 0);
> +MODULE_PARM_DESC(rx_ring_size, "Size of rx ring (in skbs)");
> +MODULE_PARM_DESC(disable_napi, "Disable NAPI polling");
> +
> +/* Register definitions */
> +struct cpmac_control_regs {
> + u32 revision;
> + u32 control;
> + u32 teardown;
> + u32 unused;
> +} __attribute__ ((packed));
> +
> +struct cpmac_int_regs {
> + u32 stat_raw;
> + u32 stat_masked;
> + u32 enable;
> + u32 clear;
> +} __attribute__ ((packed));
> +
> +struct cpmac_stats {
> + u32 good;
> + u32 bcast;
> + u32 mcast;
> + u32 pause;
> + u32 crc_error;
> + u32 align_error;
> + u32 oversized;
> + u32 jabber;
> + u32 undersized;
> + u32 fragment;
> + u32 filtered;
> + u32 qos_filtered;
> + u32 octets;
> +} __attribute__ ((packed));
All struct members here are sized such that there is no padding needed, so
the packed attribute doesn't buy you anything - unless of course the
entire structure is missaligned but I don't see how that would be possible
in this driver so the __attribute__ ((packed)) should go - it result in
somwhat larger and slower code.
In any case, the __packed attribute is prefered over __attribute__ ((packed))
for readability sake.
> +
> +struct cpmac_regs {
> + struct cpmac_control_regs tx_ctrl;
> + struct cpmac_control_regs rx_ctrl;
> + u32 unused1[56];
> + u32 mbp;
> +/* MBP bits */
> +#define MBP_RXPASSCRC 0x40000000
> +#define MBP_RXQOS 0x20000000
> +#define MBP_RXNOCHAIN 0x10000000
> +#define MBP_RXCMF 0x01000000
> +#define MBP_RXSHORT 0x00800000
> +#define MBP_RXCEF 0x00400000
> +#define MBP_RXPROMISC 0x00200000
> +#define MBP_PROMISCCHAN(chan) (((chan) & 0x7) << 16)
> +#define MBP_RXBCAST 0x00002000
> +#define MBP_BCASTCHAN(chan) (((chan) & 0x7) << 8)
> +#define MBP_RXMCAST 0x00000020
> +#define MBP_MCASTCHAN(chan) ((chan) & 0x7)
> + u32 unicast_enable;
> + u32 unicast_clear;
> + u32 max_len;
> + u32 buffer_offset;
> + u32 filter_flow_threshold;
> + u32 unused2[2];
> + u32 flow_thre[8];
> + u32 free_buffer[8];
> + u32 mac_control;
> +#define MAC_TXPTYPE 0x00000200
> +#define MAC_TXPACE 0x00000040
> +#define MAC_MII 0x00000020
> +#define MAC_TXFLOW 0x00000010
> +#define MAC_RXFLOW 0x00000008
> +#define MAC_MTEST 0x00000004
> +#define MAC_LOOPBACK 0x00000002
> +#define MAC_FDX 0x00000001
> + u32 mac_status;
> +#define MACST_QOS 0x4
> +#define MACST_RXFLOW 0x2
> +#define MACST_TXFLOW 0x1
> + u32 emc_control;
> + u32 unused3;
> + struct cpmac_int_regs tx_int;
> + u32 mac_int_vector;
> +/* Int Status bits */
> +#define INTST_STATUS 0x80000
> +#define INTST_HOST 0x40000
> +#define INTST_RX 0x20000
> +#define INTST_TX 0x10000
> + u32 mac_eoi_vector;
> + u32 unused4[2];
> + struct cpmac_int_regs rx_int;
> + u32 mac_int_stat_raw;
> + u32 mac_int_stat_masked;
> + u32 mac_int_enable;
> + u32 mac_int_clear;
> + u32 mac_addr_low[8];
> + u32 mac_addr_mid;
> + u32 mac_addr_high;
> + u32 mac_hash_low;
> + u32 mac_hash_high;
> + u32 boff_test;
> + u32 pac_test;
> + u32 rx_pause;
> + u32 tx_pause;
> + u32 unused5[2];
> + struct cpmac_stats rx_stats;
> + struct cpmac_stats tx_stats;
> + u32 unused6[232];
> + u32 tx_ptr[8];
> + u32 rx_ptr[8];
> + u32 tx_ack[8];
> + u32 rx_ack[8];
> +
> +} __attribute__ ((packed));
> +
> +struct cpmac_mdio_regs {
> + u32 version;
> + u32 control;
> +#define MDIOC_IDLE 0x80000000
> +#define MDIOC_ENABLE 0x40000000
> +#define MDIOC_PREAMBLE 0x00100000
> +#define MDIOC_FAULT 0x00080000
> +#define MDIOC_FAULTDETECT 0x00040000
> +#define MDIOC_INTTEST 0x00020000
> +#define MDIOC_CLKDIV(div) ((div) & 0xff)
> + u32 alive;
> + u32 link;
> + struct cpmac_int_regs link_int;
> + struct cpmac_int_regs user_int;
> + u32 unused[20];
> + volatile u32 access;
> +#define MDIO_BUSY 0x80000000
> +#define MDIO_WRITE 0x40000000
> +#define MDIO_REG(reg) (((reg) & 0x1f) << 21)
> +#define MDIO_PHY(phy) (((phy) & 0x1f) << 16)
> +#define MDIO_DATA(data) ((data) & 0xffff)
> + u32 physel;
> +} __attribute__ ((packed));
> +
> +/* Descriptor */
> +struct cpmac_desc {
> + u32 hw_next;
> + u32 hw_data;
> + u16 buflen;
> + u16 bufflags;
> + u16 datalen;
> + u16 dataflags;
> +/* Flags bits */
> +#define CPMAC_SOP 0x8000
> +#define CPMAC_EOP 0x4000
> +#define CPMAC_OWN 0x2000
> +#define CPMAC_EOQ 0x1000
> + struct sk_buff *skb;
> + struct cpmac_desc *next;
> +} __attribute__ ((packed));
> +
> +struct cpmac_priv {
> + struct net_device_stats stats;
> + spinlock_t lock; /* irq{save,restore} */
> + struct sk_buff *skb_pool;
> + int free_skbs;
> + struct cpmac_desc *rx_head;
> + int tx_head, tx_tail;
> + struct cpmac_desc *desc_ring;
> + struct cpmac_regs *regs;
> + struct mii_bus *mii_bus;
> + struct phy_device *phy;
> + char phy_name[BUS_ID_SIZE];
> + struct plat_cpmac_data *config;
> + int oldlink, oldspeed, oldduplex;
> + u32 msg_enable;
> + struct net_device *dev;
> + struct work_struct alloc_work;
> +};
> +
> +static irqreturn_t cpmac_irq(int, void *);
> +static void cpmac_reset(struct net_device *dev);
> +static void cpmac_hw_init(struct net_device *dev);
> +static int cpmac_stop(struct net_device *dev);
> +static int cpmac_open(struct net_device *dev);
> +
> +#undef CPMAC_DEBUG
> +#define CPMAC_LOW_THRESH 32
> +#define CPMAC_ALLOC_SIZE 64
> +#define CPMAC_SKB_SIZE 1518
> +#define CPMAC_TX_RING_SIZE 8
> +
> +#ifdef CPMAC_DEBUG
> +static void cpmac_dump_regs(u32 *base, int count)
> +{
> + int i;
> + for (i = 0; i < (count + 3) / 4; i++) {
> + if (i % 4 == 0) printk(KERN_DEBUG "\nCPMAC[0x%04x]:", i * 4);
> + printk(KERN_DEBUG " 0x%08x", *(base + i));
> + }
> + printk(KERN_DEBUG "\n");
> +}
> +
> +static const char *cpmac_dump_buf(const uint8_t *buf, unsigned size)
> +{
> + static char buffer[3 * 25 + 1];
> + char *p = &buffer[0];
> + if (size > 20)
> + size = 20;
> + while (size-- > 0)
> + p += sprintf(p, " %02x", *buf++);
> + return buffer;
> +}
> +#endif
> +
> +static int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
> +{
> + struct cpmac_mdio_regs *regs = bus->priv;
> + u32 val;
> +
> + while ((val = regs->access) & MDIO_BUSY);
> + regs->access = MDIO_BUSY | MDIO_REG(regnum & 0x1f) |
> + MDIO_PHY(phy_id & 0x1f);
> + while ((val = regs->access) & MDIO_BUSY);
> +
> + return val & 0xffff;
> +}
> +
> +static int cpmac_mdio_write(struct mii_bus *bus, int phy_id,
> + int regnum, u16 val)
> +{
> + struct cpmac_mdio_regs *regs = bus->priv;
> +
> + while (regs->access & MDIO_BUSY);
> + regs->access = MDIO_BUSY | MDIO_WRITE |
> + MDIO_REG(regnum & 0x1f) | MDIO_PHY(phy_id & 0x1f) | val;
> +
> + return 0;
> +}
> +
> +static int cpmac_mdio_reset(struct mii_bus *bus)
> +{
> + ar7_device_reset(AR7_RESET_BIT_MDIO);
> + ((struct cpmac_mdio_regs *)bus->priv)->control = MDIOC_ENABLE |
> + MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1);
> +
> + return 0;
> +}
> +
> +static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
> +
> +static struct mii_bus cpmac_mii = {
> + .name = "cpmac-mii",
> + .read = cpmac_mdio_read,
> + .write = cpmac_mdio_write,
> + .reset = cpmac_mdio_reset,
> + .irq = mii_irqs,
> +};
> +
> +static int cpmac_config(struct net_device *dev, struct ifmap *map)
> +{
> + if (dev->flags & IFF_UP)
> + return -EBUSY;
> +
> + /* Don't allow changing the I/O address */
> + if (map->base_addr != dev->base_addr)
> + return -EOPNOTSUPP;
> +
> + /* ignore other fields */
> + return 0;
> +}
> +
> +static int cpmac_set_mac_address(struct net_device *dev, void *addr)
> +{
> + struct sockaddr *sa = addr;
> +
> + if (dev->flags & IFF_UP)
> + return -EBUSY;
> +
> + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
> +
> + return 0;
> +}
> +
> +static void cpmac_set_multicast_list(struct net_device *dev)
> +{
> + struct dev_mc_list *iter;
> + int i;
> + int hash, tmp;
> + int hashlo = 0, hashhi = 0;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + if (dev->flags & IFF_PROMISC) {
> + priv->regs->mbp &= ~MBP_PROMISCCHAN(0); /* promisc channel 0 */
> + priv->regs->mbp |= MBP_RXPROMISC;
> + } else {
> + priv->regs->mbp &= ~MBP_RXPROMISC;
> + if (dev->flags & IFF_ALLMULTI) {
> + /* enable all multicast mode */
> + priv->regs->mac_hash_low = 0xffffffff;
> + priv->regs->mac_hash_high = 0xffffffff;
> + } else {
> + for (i = 0, iter = dev->mc_list; i < dev->mc_count;
> + i++, iter = iter->next) {
> + hash = 0;
> + tmp = iter->dmi_addr[0];
> + hash ^= (tmp >> 2) ^ (tmp << 4);
> + tmp = iter->dmi_addr[1];
> + hash ^= (tmp >> 4) ^ (tmp << 2);
> + tmp = iter->dmi_addr[2];
> + hash ^= (tmp >> 6) ^ tmp;
> + tmp = iter->dmi_addr[4];
> + hash ^= (tmp >> 2) ^ (tmp << 4);
> + tmp = iter->dmi_addr[5];
> + hash ^= (tmp >> 4) ^ (tmp << 2);
> + tmp = iter->dmi_addr[6];
> + hash ^= (tmp >> 6) ^ tmp;
> + hash &= 0x3f;
> + if (hash < 32) {
> + hashlo |= 1<<hash;
> + } else {
> + hashhi |= 1<<(hash - 32);
> + }
> + }
> +
> + priv->regs->mac_hash_low = hashlo;
> + priv->regs->mac_hash_high = hashhi;
> + }
> + }
> +}
> +
> +static struct sk_buff *cpmac_get_skb(struct net_device *dev)
> +{
> + struct sk_buff *skb;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + skb = priv->skb_pool;
> + if (likely(skb))
> + priv->skb_pool = skb->next;
> + else {
> + skb = dev_alloc_skb(CPMAC_SKB_SIZE + 2);
> + if (skb) {
> + skb->next = NULL;
> + skb_reserve(skb, 2);
> + skb->dev = priv->dev;
> + }
> + }
> +
> + if (likely(priv->free_skbs))
> + priv->free_skbs--;
> +
> + if (priv->free_skbs < CPMAC_LOW_THRESH)
> + schedule_work(&priv->alloc_work);
> +
> + return skb;
> +}
> +
> +static struct sk_buff *cpmac_rx_one(struct net_device *dev,
> + struct cpmac_priv *priv,
> + struct cpmac_desc *desc)
> +{
> + unsigned long flags;
> + char *data;
> + struct sk_buff *skb, *result = NULL;
> +
> + priv->regs->rx_ack[0] = virt_to_phys(desc);
> + if (unlikely(!desc->datalen)) {
> + if (printk_ratelimit())
> + printk(KERN_WARNING "%s: rx: spurious interrupt\n",
> + dev->name);
> + priv->stats.rx_errors++;
> + return NULL;
> + }
> +
> + spin_lock_irqsave(&priv->lock, flags);
> + skb = cpmac_get_skb(dev);
> + if (likely(skb)) {
> + data = (char *)phys_to_virt(desc->hw_data);
> + dma_cache_inv((u32)data, desc->datalen);
> + skb_put(desc->skb, desc->datalen);
> + desc->skb->protocol = eth_type_trans(desc->skb, dev);
> + desc->skb->ip_summed = CHECKSUM_NONE;
> + priv->stats.rx_packets++;
> + priv->stats.rx_bytes += desc->datalen;
> + result = desc->skb;
> + desc->skb = skb;
> + } else {
> +#ifdef CPMAC_DEBUG
> + if (printk_ratelimit())
> + printk(KERN_NOTICE "%s: low on skbs, dropping packet\n",
> + dev->name);
> +#endif
> + priv->stats.rx_dropped++;
> + }
> + spin_unlock_irqrestore(&priv->lock, flags);
> +
> + desc->hw_data = virt_to_phys(desc->skb->data);
> + desc->buflen = CPMAC_SKB_SIZE;
> + desc->dataflags = CPMAC_OWN;
> + dma_cache_wback((u32)desc, 16);
> +
> + return result;
> +}
> +
> +static void cpmac_rx(struct net_device *dev)
> +{
> + struct sk_buff *skb;
> + struct cpmac_desc *desc;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + spin_lock(&priv->lock);
> + if (unlikely(!priv->rx_head)) {
> + spin_unlock(&priv->lock);
> + return;
> + }
> +
> + desc = priv->rx_head;
> + dma_cache_inv((u32)desc, 16);
> +#ifdef CPMAC_DEBUG
> + printk(KERN_DEBUG "%s: len=%d, %s\n", __func__, pkt->datalen,
> + cpmac_dump_buf(data, pkt->datalen));
> +#endif
> +
> + while ((desc->dataflags & CPMAC_OWN) == 0) {
> + skb = cpmac_rx_one(dev, priv, desc);
> + if (likely(skb))
> + netif_rx(skb);
> + desc = desc->next;
> + dma_cache_inv((u32)desc, 16);
> + }
> +
> + priv->rx_head = desc;
> + priv->regs->rx_ptr[0] = virt_to_phys(desc);
> + spin_unlock(&priv->lock);
> +}
> +
> +static int cpmac_poll(struct net_device *dev, int *budget)
> +{
> + struct sk_buff *skb;
> + struct cpmac_desc *desc;
> + int received = 0, quota = min(dev->quota, *budget);
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + if (unlikely(!priv->rx_head)) {
> + if (printk_ratelimit())
> + printk(KERN_NOTICE "%s: rx: polling, but no queue\n",
> + dev->name);
> + netif_rx_complete(dev);
> + return 0;
> + }
> +
> + desc = priv->rx_head;
> + dma_cache_inv((u32)desc, 16);
> +
> + while ((received < quota) && ((desc->dataflags & CPMAC_OWN) == 0)) {
> + skb = cpmac_rx_one(dev, priv, desc);
> + if (likely(skb)) {
> + netif_receive_skb(skb);
> + received++;
> + }
> + desc = desc->next;
> + priv->rx_head = desc;
> + dma_cache_inv((u32)desc, 16);
> + }
> +
> + *budget -= received;
> + dev->quota -= received;
> +#ifdef CPMAC_DEBUG
> + printk(KERN_DEBUG "%s: processed %d packets\n", dev->name, received);
> +#endif
> + if (desc->dataflags & CPMAC_OWN) {
> + priv->regs->rx_ptr[0] = virt_to_phys(desc);
> + netif_rx_complete(dev);
> + priv->regs->rx_int.enable = 0x1;
> + priv->regs->rx_int.clear = 0xfe;
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +static void
> +cpmac_alloc_skbs(struct work_struct *work)
> +{
> + struct cpmac_priv *priv = container_of(work, struct cpmac_priv,
> + alloc_work);
> + unsigned long flags;
> + int i, num_skbs = 0;
> + struct sk_buff *skb, *skbs = NULL;
> +
> + for (i = 0; i < CPMAC_ALLOC_SIZE; i++) {
> + skb = alloc_skb(CPMAC_SKB_SIZE + 2, GFP_KERNEL);
> + if (!skb)
> + break;
> + skb->next = skbs;
> + skb_reserve(skb, 2);
> + skb->dev = priv->dev;
> + num_skbs++;
> + skbs = skb;
> + }
> +
> + if (skbs) {
> + spin_lock_irqsave(&priv->lock, flags);
> + for (skb = priv->skb_pool; skb && skb->next; skb = skb->next);
> + if (!skb)
> + priv->skb_pool = skbs;
> + else
> + skb->next = skbs;
> + priv->free_skbs += num_skbs;
> + spin_unlock_irqrestore(&priv->lock, flags);
> +#ifdef CPMAC_DEBUG
> + printk(KERN_DEBUG "%s: allocated %d skbs\n",
> + priv->dev->name, num_skbs);
> +#endif
> + }
> +}
> +
> +static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + unsigned long flags;
> + int len, chan;
> + struct cpmac_desc *desc;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + len = skb->len;
> +#ifdef CPMAC_DEBUG
> + printk(KERN_DEBUG "%s: len=%d\n", __func__, len);
> + /* cpmac_dump_buf(const uint8_t * buf, unsigned size) */
> +#endif
> + if (unlikely(len < ETH_ZLEN)) {
> + if (unlikely(skb_padto(skb, ETH_ZLEN))) {
> + if (printk_ratelimit())
> + printk(KERN_NOTICE
> + "%s: padding failed, dropping\n",
> + dev->name);
> + spin_lock_irqsave(&priv->lock, flags);
> + priv->stats.tx_dropped++;
> + spin_unlock_irqrestore(&priv->lock, flags);
> + return -ENOMEM;
> + }
> + len = ETH_ZLEN;
> + }
> + spin_lock_irqsave(&priv->lock, flags);
> + chan = priv->tx_tail++;
> + priv->tx_tail %= 8;
> + if (priv->tx_tail == priv->tx_head)
> + netif_stop_queue(dev);
> +
> + desc = &priv->desc_ring[chan];
> + dma_cache_inv((u32)desc, 16);
> + if (desc->dataflags & CPMAC_OWN) {
> + printk(KERN_NOTICE "%s: tx dma ring full, dropping\n",
> + dev->name);
> + priv->stats.tx_dropped++;
> + spin_unlock_irqrestore(&priv->lock, flags);
> + return -ENOMEM;
> + }
> +
> + dev->trans_start = jiffies;
> + desc->dataflags = CPMAC_SOP | CPMAC_EOP | CPMAC_OWN;
> + desc->skb = skb;
> + desc->hw_data = virt_to_phys(skb->data);
> + dma_cache_wback((u32)skb->data, len);
> + desc->buflen = len;
> + desc->datalen = len;
> + desc->hw_next = 0;
> + dma_cache_wback((u32)desc, 16);
> + priv->regs->tx_ptr[chan] = virt_to_phys(desc);
> + spin_unlock_irqrestore(&priv->lock, flags);
> +
> + return 0;
> +}
> +
> +static void cpmac_end_xmit(struct net_device *dev, int channel)
> +{
> + struct cpmac_desc *desc;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + spin_lock(&priv->lock);
> + desc = &priv->desc_ring[channel];
> + priv->regs->tx_ack[channel] = virt_to_phys(desc);
> + if (likely(desc->skb)) {
> + priv->stats.tx_packets++;
> + priv->stats.tx_bytes += desc->skb->len;
> + dev_kfree_skb_irq(desc->skb);
> + if (netif_queue_stopped(dev))
> + netif_wake_queue(dev);
> + } else
> + if (printk_ratelimit())
> + printk(KERN_NOTICE "%s: end_xmit: spurious interrupt\n",
> + dev->name);
> + spin_unlock(&priv->lock);
> +}
> +
> +static void cpmac_reset(struct net_device *dev)
> +{
> + int i;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + ar7_device_reset(priv->config->reset_bit);
> + priv->regs->rx_ctrl.control &= ~1;
> + priv->regs->tx_ctrl.control &= ~1;
> + for (i = 0; i < 8; i++) {
> + priv->regs->tx_ptr[i] = 0;
> + priv->regs->rx_ptr[i] = 0;
> + }
> + priv->regs->mac_control &= ~MAC_MII; /* disable mii */
> +}
> +
> +static inline void cpmac_free_rx_ring(struct net_device *dev)
> +{
> + struct cpmac_desc *desc;
> + int i;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + if (unlikely(!priv->rx_head))
> + return;
> +
> + desc = priv->rx_head;
> + dma_cache_inv((u32)desc, 16);
> +
> + for (i = 0; i < rx_ring_size; i++) {
> + desc->buflen = CPMAC_SKB_SIZE;
> + if ((desc->dataflags & CPMAC_OWN) == 0) {
> + desc->dataflags = CPMAC_OWN;
> + priv->stats.rx_dropped++;
> + }
> + dma_cache_wback((u32)desc, 16);
> + desc = desc->next;
> + dma_cache_inv((u32)desc, 16);
> + }
> +}
> +
> +static irqreturn_t cpmac_irq(int irq, void *dev_id)
> +{
> + struct net_device *dev = dev_id;
> + struct cpmac_priv *priv = netdev_priv(dev);
> + u32 status;
> +
> + if (!dev)
> + return IRQ_NONE;
> +
> + status = priv->regs->mac_int_vector;
> +
> + if (status & INTST_TX)
> + cpmac_end_xmit(dev, (status & 7));
> +
> + if (status & INTST_RX) {
> + if (disable_napi)
> + cpmac_rx(dev);
> + else {
> + priv->regs->rx_int.enable = 0;
> + priv->regs->rx_int.clear = 0xff;
> + netif_rx_schedule(dev);
> + }
> + }
> +
> + priv->regs->mac_eoi_vector = 0;
> +
> + if (unlikely(status & (INTST_HOST | INTST_STATUS))) {
> + if (printk_ratelimit())
> + printk(KERN_ERR "%s: hw error, resetting...\n",
> + dev->name);
> + spin_lock(&priv->lock);
> + phy_stop(priv->phy);
> + cpmac_reset(dev);
> + cpmac_free_rx_ring(dev);
> + cpmac_hw_init(dev);
> + spin_unlock(&priv->lock);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void cpmac_tx_timeout(struct net_device *dev)
> +{
> + struct cpmac_priv *priv = netdev_priv(dev);
> + struct cpmac_desc *desc;
> +
> + priv->stats.tx_errors++;
> + desc = &priv->desc_ring[priv->tx_head++];
> + priv->tx_head %= 8;
> + printk(KERN_NOTICE "%s: transmit timeout\n", dev->name);
> + if (desc->skb)
> + dev_kfree_skb(desc->skb);
> + netif_wake_queue(dev);
> +}
> +
> +static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
> +{
> + struct cpmac_priv *priv = netdev_priv(dev);
> + if (!(netif_running(dev)))
> + return -EINVAL;
> + if (!priv->phy)
> + return -EINVAL;
> + if ((cmd == SIOCGMIIPHY) || (cmd == SIOCGMIIREG) ||
> + (cmd == SIOCSMIIREG))
> + return phy_mii_ioctl(priv->phy, if_mii(ifr), cmd);
> +
> + return -EINVAL;
> +}
> +
> +static int cpmac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + if (priv->phy)
> + return phy_ethtool_gset(priv->phy, cmd);
> +
> + return -EINVAL;
> +}
> +
> +static int cpmac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + if (!capable(CAP_NET_ADMIN))
> + return -EPERM;
> +
> + if (priv->phy)
> + return phy_ethtool_sset(priv->phy, cmd);
> +
> + return -EINVAL;
> +}
> +
> +static void cpmac_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + strcpy(info->driver, "cpmac");
> + strcpy(info->version, "0.0.3");
> + info->fw_version[0] = '\0';
> + sprintf(info->bus_info, "%s", "cpmac");
> + info->regdump_len = 0;
> +}
> +
> +static const struct ethtool_ops cpmac_ethtool_ops = {
> + .get_settings = cpmac_get_settings,
> + .set_settings = cpmac_set_settings,
> + .get_drvinfo = cpmac_get_drvinfo,
> + .get_link = ethtool_op_get_link,
> +};
> +
> +static struct net_device_stats *cpmac_stats(struct net_device *dev)
> +{
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + if (netif_device_present(dev))
> + return &priv->stats;
> +
> + return NULL;
> +}
> +
> +static int cpmac_change_mtu(struct net_device *dev, int mtu)
> +{
> + unsigned long flags;
> + struct cpmac_priv *priv = netdev_priv(dev);
> + spinlock_t *lock = &priv->lock;
> +
> + if ((mtu < 68) || (mtu > 1500))
> + return -EINVAL;
> +
> + spin_lock_irqsave(lock, flags);
> + dev->mtu = mtu;
> + spin_unlock_irqrestore(lock, flags);
> +
> + return 0;
> +}
> +
> +static void cpmac_adjust_link(struct net_device *dev)
> +{
> + struct cpmac_priv *priv = netdev_priv(dev);
> + unsigned long flags;
> + int new_state = 0;
> +
> + spin_lock_irqsave(&priv->lock, flags);
> + if (priv->phy->link) {
> + if (priv->phy->duplex != priv->oldduplex) {
> + new_state = 1;
> + priv->oldduplex = priv->phy->duplex;
> + }
> +
> + if (priv->phy->speed != priv->oldspeed) {
> + new_state = 1;
> + priv->oldspeed = priv->phy->speed;
> + }
> +
> + if (!priv->oldlink) {
> + new_state = 1;
> + priv->oldlink = 1;
> + netif_schedule(dev);
> + }
> + } else if (priv->oldlink) {
> + new_state = 1;
> + priv->oldlink = 0;
> + priv->oldspeed = 0;
> + priv->oldduplex = -1;
> + }
> +
> + if (new_state)
> + phy_print_status(priv->phy);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> +}
> +
> +static void cpmac_hw_init(struct net_device *dev)
> +{
> + int i;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + for (i = 0; i < 8; i++)
> + priv->regs->tx_ptr[i] = 0;
> + priv->regs->rx_ptr[0] = virt_to_phys(priv->rx_head);
> +
> + priv->regs->mbp = MBP_RXSHORT | MBP_RXBCAST | MBP_RXMCAST;
> + priv->regs->unicast_enable = 0x1;
> + priv->regs->unicast_clear = 0xfe;
> + priv->regs->buffer_offset = 0;
> + for (i = 0; i < 8; i++)
> + priv->regs->mac_addr_low[i] = dev->dev_addr[5];
> + priv->regs->mac_addr_mid = dev->dev_addr[4];
> + priv->regs->mac_addr_high = dev->dev_addr[0] | (dev->dev_addr[1] << 8)
> + | (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
> + priv->regs->max_len = CPMAC_SKB_SIZE;
> + priv->regs->rx_int.enable = 0x1;
> + priv->regs->rx_int.clear = 0xfe;
> + priv->regs->tx_int.enable = 0xff;
> + priv->regs->tx_int.clear = 0;
> + priv->regs->mac_int_enable = 3;
> + priv->regs->mac_int_clear = 0xfc;
> +
> + priv->regs->rx_ctrl.control |= 1;
> + priv->regs->tx_ctrl.control |= 1;
> + priv->regs->mac_control |= MAC_MII | MAC_FDX;
> +
> + priv->phy->state = PHY_CHANGELINK;
> + phy_start(priv->phy);
> +}
> +
> +static int cpmac_open(struct net_device *dev)
> +{
> + int i, size, res;
> + struct cpmac_priv *priv = netdev_priv(dev);
> + struct cpmac_desc *desc;
> + struct sk_buff *skb;
> +
> + priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link,
> + 0, PHY_INTERFACE_MODE_MII);
> + if (IS_ERR(priv->phy)) {
> + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
> + return PTR_ERR(priv->phy);
> + }
> +
> + if (!request_mem_region(dev->mem_start, dev->mem_end -
> + dev->mem_start, dev->name)) {
> + printk(KERN_ERR "%s: failed to request registers\n",
> + dev->name);
> + res = -ENXIO;
> + goto fail_reserve;
> + }
> +
> + priv->regs = ioremap_nocache(dev->mem_start, dev->mem_end -
> + dev->mem_start);
> + if (!priv->regs) {
> + printk(KERN_ERR "%s: failed to remap registers\n", dev->name);
> + res = -ENXIO;
> + goto fail_remap;
> + }
> +
> + priv->rx_head = NULL;
> + size = sizeof(struct cpmac_desc) * (rx_ring_size +
> + CPMAC_TX_RING_SIZE);
> + priv->desc_ring = (struct cpmac_desc *)kmalloc(size, GFP_KERNEL);
kmalloc returns void * so no cast to another pointer type necessary.
> + if (!priv->desc_ring) {
> + res = -ENOMEM;
> + goto fail_alloc;
> + }
> +
> + memset((char *)priv->desc_ring, 0, size);
Use kzalloc instead of kmalloc. kzalloc is like kmalloc but returns
zero'd memory.
> + priv->skb_pool = NULL;
> + priv->free_skbs = 0;
> + priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
> +
> + INIT_WORK(&priv->alloc_work, cpmac_alloc_skbs);
> + schedule_work(&priv->alloc_work);
> + flush_scheduled_work();
> +
> + for (i = 0; i < rx_ring_size; i++) {
> + desc = &priv->rx_head[i];
> + skb = cpmac_get_skb(dev);
> + if (!skb) {
> + res = -ENOMEM;
> + goto fail_desc;
> + }
> + desc->skb = skb;
> + desc->hw_data = virt_to_phys(skb->data);
> + desc->buflen = CPMAC_SKB_SIZE;
> + desc->dataflags = CPMAC_OWN;
> + desc->next = &priv->rx_head[(i + 1) % rx_ring_size];
> + desc->hw_next = virt_to_phys(desc->next);
> + dma_cache_wback((u32)desc, 16);
> + }
> +
> + if ((res = request_irq(dev->irq, cpmac_irq, SA_INTERRUPT,
> + dev->name, dev))) {
> + printk(KERN_ERR "%s: failed to obtain irq\n", dev->name);
> + goto fail_irq;
> + }
> +
> + cpmac_reset(dev);
> + cpmac_hw_init(dev);
> +
> + netif_start_queue(dev);
> + return 0;
> +
> +fail_irq:
> +fail_desc:
> + for (i = 0; i < rx_ring_size; i++)
> + if (priv->rx_head[i].skb)
> + kfree_skb(priv->rx_head[i].skb);
> +fail_alloc:
> + kfree(priv->desc_ring);
> +
> + for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
> + priv->skb_pool = skb->next;
> + kfree_skb(skb);
> + }
> +
> + iounmap(priv->regs);
> +
> +fail_remap:
> + release_mem_region(dev->mem_start, dev->mem_end -
> + dev->mem_start);
> +
> +fail_reserve:
> + phy_disconnect(priv->phy);
> +
> + return res;
> +}
> +
> +static int cpmac_stop(struct net_device *dev)
> +{
> + int i;
> + struct sk_buff *skb;
> + struct cpmac_priv *priv = netdev_priv(dev);
> +
> + netif_stop_queue(dev);
> +
> + phy_stop(priv->phy);
> + phy_disconnect(priv->phy);
> + priv->phy = NULL;
> +
> + cpmac_reset(dev);
> +
> + for (i = 0; i < 8; i++) {
> + priv->regs->rx_ptr[i] = 0;
> + priv->regs->tx_ptr[i] = 0;
> + priv->regs->mbp = 0;
> + }
> +
> + free_irq(dev->irq, dev);
> + release_mem_region(dev->mem_start, dev->mem_end -
> + dev->mem_start);
> +
> + cancel_delayed_work(&priv->alloc_work);
> + flush_scheduled_work();
> +
> + priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
> + for (i = 0; i < rx_ring_size; i++)
> + if (priv->rx_head[i].skb)
> + kfree_skb(priv->rx_head[i].skb);
> +
> + kfree(priv->desc_ring);
> +
> + for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
> + priv->skb_pool = skb->next;
> + kfree_skb(skb);
> + }
> +
> + return 0;
> +}
> +
> +static int external_switch;
> +
> +static int __devinit cpmac_probe(struct platform_device *pdev)
> +{
> + int i, rc, phy_id;
> + struct resource *res;
> + struct cpmac_priv *priv;
> + struct net_device *dev;
> + struct plat_cpmac_data *pdata;
> +
> + pdata = pdev->dev.platform_data;
> +
> + for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
> + if (!(pdata->phy_mask & (1 << phy_id)))
> + continue;
> + if (!cpmac_mii.phy_map[phy_id])
> + continue;
> + break;
> + }
> +
> + if (phy_id == PHY_MAX_ADDR) {
> + if (external_switch)
> + phy_id = 0;
> + else {
> + printk(KERN_ERR "cpmac: no PHY present\n");
> + return -ENODEV;
> + }
> + }
> +
> + dev = alloc_etherdev(sizeof(struct cpmac_priv));
> +
> + if (!dev) {
> + printk(KERN_ERR
> + "cpmac: Unable to allocate net_device structure!\n");
> + return -ENOMEM;
> + }
> +
> + SET_MODULE_OWNER(dev);
Set SET_MODULE_OWNER is a useless nop which only exists in 2.6 for
driver source compatibility with 2.4. So you can remove this call.
I used the opportunity to send out a patch to remove SET_MODULE_OWNER
from the kernel entirely.
> + platform_set_drvdata(pdev, dev);
> + priv = netdev_priv(dev);
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> + if (!res) {
> + rc = -ENODEV;
> + goto fail;
> + }
> +
> + dev->mem_start = res->start;
> + dev->mem_end = res->end;
> + dev->irq = platform_get_irq_byname(pdev, "irq");
> +
> + dev->mtu = 1500;
Initializing this field is redundant; alloc_etherdev has already done it,
so remove this line.
> + dev->open = cpmac_open;
> + dev->stop = cpmac_stop;
> + dev->set_config = cpmac_config;
> + dev->hard_start_xmit = cpmac_start_xmit;
> + dev->do_ioctl = cpmac_ioctl;
> + dev->get_stats = cpmac_stats;
> + dev->change_mtu = cpmac_change_mtu;
> + dev->set_mac_address = cpmac_set_mac_address;
> + dev->set_multicast_list = cpmac_set_multicast_list;
> + dev->tx_timeout = cpmac_tx_timeout;
> + dev->ethtool_ops = &cpmac_ethtool_ops;
> + if (!disable_napi) {
> + dev->poll = cpmac_poll;
> + dev->weight = min(rx_ring_size, 64);
> + }
> +
> + memset(priv, 0, sizeof(struct cpmac_priv));
Useless, alloc_etherdev does that already.
> + spin_lock_init(&priv->lock);
> + priv->msg_enable = netif_msg_init(NETIF_MSG_WOL, 0x3fff);
> + priv->config = pdata;
> + priv->dev = dev;
> + memcpy(dev->dev_addr, priv->config->dev_addr, sizeof(dev->dev_addr));
> + if (phy_id == 31)
> + snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT,
> + cpmac_mii.id, phy_id);
> + else
> + snprintf(priv->phy_name, BUS_ID_SIZE, "fixed@%d:%d", 100, 1);
> +
> + if ((rc = register_netdev(dev))) {
> + printk(KERN_ERR "cpmac: error %i registering device %s\n",
> + rc, dev->name);
> + goto fail;
> + }
> +
> + printk(KERN_INFO "cpmac: device %s (regs: %p, irq: %d, phy: %s, mac: ",
> + dev->name, (u32 *)dev->mem_start, dev->irq,
> + priv->phy_name);
> + for (i = 0; i < 6; i++)
> + printk("%02x%s", dev->dev_addr[i], i < 5 ? ":" : ")\n");
> +
> + return 0;
> +
> +fail:
> + free_netdev(dev);
> + return rc;
> +}
> +
> +static int __devexit cpmac_remove(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + unregister_netdev(dev);
> + free_netdev(dev);
> + return 0;
> +}
> +
> +static struct platform_driver cpmac_driver = {
> + .driver.name = "cpmac",
> + .probe = cpmac_probe,
> + .remove = cpmac_remove,
This should be:
.remove = __devexit_p(cpmac_remove),
to avoid the final link from blowing up when the driver is built into the
kernel.
> +};
> +
> +int __devinit cpmac_init(void)
Make this function static; no need to export.
> +{
> + u32 mask;
> + int i, res;
> + cpmac_mii.priv =
> + ioremap_nocache(AR7_REGS_MDIO, sizeof(struct cpmac_mdio_regs));
> +
> + if (!cpmac_mii.priv) {
> + printk(KERN_ERR "Can't ioremap mdio registers\n");
> + return -ENXIO;
> + }
> +
> +#warning FIXME: unhardcode gpio&reset bits
Seeing such warnings always gives me a warm fuzzy feeling ;-)
> + ar7_gpio_disable(26);
> + ar7_gpio_disable(27);
> + ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
> + ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
> + ar7_device_reset(AR7_RESET_BIT_EPHY);
> +
> + cpmac_mii.reset(&cpmac_mii);
> +
> + for (i = 0; i < 300000; i++) {
> + mask = ((struct cpmac_mdio_regs *)cpmac_mii.priv)->alive;
> + if (mask)
> + break;
> + }
> +
> +/* mask &= 0x7fffffff;
> + if (mask & (mask - 1)) {*/
> + external_switch = 1;
> + mask = 0;
> +/* }*/
> +
> + cpmac_mii.phy_mask = ~(mask | 0x80000000);
> +
> + res = mdiobus_register(&cpmac_mii);
> + if (res)
> + goto fail_mii;
> +
> + res = platform_driver_register(&cpmac_driver);
> + if (res)
> + goto fail_cpmac;
> +
> + return 0;
> +
> +fail_cpmac:
> + mdiobus_unregister(&cpmac_mii);
> +
> +fail_mii:
> + iounmap(cpmac_mii.priv);
> +
> + return res;
> +}
> +
> +void __devexit cpmac_exit(void)
> +{
> + platform_driver_unregister(&cpmac_driver);
> + mdiobus_unregister(&cpmac_mii);
> +}
> +
> +module_init(cpmac_init);
> +module_exit(cpmac_exit);
Time to run ...
Ralf
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][7/7] AR7: ethernet
2007-09-12 16:50 ` Ralf Baechle
@ 2007-09-13 1:42 ` Thiemo Seufer
2007-09-13 11:35 ` Ralf Baechle
0 siblings, 1 reply; 30+ messages in thread
From: Thiemo Seufer @ 2007-09-13 1:42 UTC (permalink / raw)
To: Ralf Baechle
Cc: Matteo Croce, linux-mips, Eugene Konev, netdev, davem, kuznet,
pekkas, jmorris, yoshfuji, kaber, openwrt-devel, Andrew Morton,
Jeff Garzik
Ralf Baechle wrote:
> On Sat, Sep 08, 2007 at 02:23:00AM +0200, Matteo Croce wrote:
[snip]
> > +/* Register definitions */
> > +struct cpmac_control_regs {
> > + u32 revision;
> > + u32 control;
> > + u32 teardown;
> > + u32 unused;
> > +} __attribute__ ((packed));
> > +
> > +struct cpmac_int_regs {
> > + u32 stat_raw;
> > + u32 stat_masked;
> > + u32 enable;
> > + u32 clear;
> > +} __attribute__ ((packed));
> > +
> > +struct cpmac_stats {
> > + u32 good;
> > + u32 bcast;
> > + u32 mcast;
> > + u32 pause;
> > + u32 crc_error;
> > + u32 align_error;
> > + u32 oversized;
> > + u32 jabber;
> > + u32 undersized;
> > + u32 fragment;
> > + u32 filtered;
> > + u32 qos_filtered;
> > + u32 octets;
> > +} __attribute__ ((packed));
>
> All struct members here are sized such that there is no padding needed, so
> the packed attribute doesn't buy you anything - unless of course the
> entire structure is missaligned but I don't see how that would be possible
> in this driver so the __attribute__ ((packed)) should go - it result in
> somwhat larger and slower code.
FWIW, a modern gcc will warn about such superfluous packed attributes,
that's another reason to remove those.
Thiemo
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][7/7] AR7: ethernet
2007-09-13 1:42 ` Thiemo Seufer
@ 2007-09-13 11:35 ` Ralf Baechle
0 siblings, 0 replies; 30+ messages in thread
From: Ralf Baechle @ 2007-09-13 11:35 UTC (permalink / raw)
To: Thiemo Seufer
Cc: Matteo Croce, linux-mips, Eugene Konev, netdev, davem, kuznet,
pekkas, jmorris, yoshfuji, kaber, openwrt-devel, Andrew Morton,
Jeff Garzik
On Thu, Sep 13, 2007 at 02:42:46AM +0100, Thiemo Seufer wrote:
> > All struct members here are sized such that there is no padding needed, so
> > the packed attribute doesn't buy you anything - unless of course the
> > entire structure is missaligned but I don't see how that would be possible
> > in this driver so the __attribute__ ((packed)) should go - it result in
> > somwhat larger and slower code.
>
> FWIW, a modern gcc will warn about such superfluous packed attributes,
> that's another reason to remove those.
I doubt it will in this case; the packed structure is dereferenced by a
pointer so no way for gcc to know the alignment.
Ralf
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH][MIPS][0/7] AR7: 4th effort
@ 2007-09-20 15:28 Matteo Croce
2007-09-20 16:06 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
0 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-20 15:28 UTC (permalink / raw)
To: linux-mips; +Cc: nico, nbd, florian, openwrt-devel, Andrew Morton
These are patch for the AR7 router board:
core, serial hack, mtd partition map, ethernet, gpio pins, watchdog and leds
Here is a live session log:
ADAM2 Revision 0.22.12
(C) Copyright 1996-2003 Texas Instruments Inc. All Rights Reserved.
(C) Copyright 2003 Telogy Networks, Inc.
Usage: setmfreq [-d] [-s sys_freq, in MHz] [cpu_freq, in MHz]
Memory optimization Complete!
mac_init(): Find mac [00:13:10:01:C5:2A] in location 1
Find mac [00:13:10:01:C5:2A] in location 1
mac_value:00:13:10:01:C5:2A
tftpserver initialized
Adam2_AR7WRD >
Press any key to abort OS load, or wait 5 seconds for OS to boot...
Linux version 2.6.22.4 (matteo@raver) (gcc version 4.2.1) #1 Thu Sep 20 16:41:39 CEST 2007
CPU revision is: 00018448
TI AR7 (TNETD7300), ID: 0x0005, Revision: 0x02
Determined physical RAM map:
memory: 01000000 @ 14000000 (usable)
Built 1 zonelists. Total pages: 4064
Kernel command line: init=/etc/preinit rootfstype=squashfs,jffs2, console=ttyS0,38400n8r
Primary instruction cache 16kB, physically tagged, 4-way, linesize 16 bytes.
Primary data cache 16kB, 4-way, linesize 16 bytes.
Synthesized TLB refill handler (20 instructions).
Synthesized TLB load handler fastpath (32 instructions).
Synthesized TLB store handler fastpath (32 instructions).
Synthesized TLB modify handler fastpath (31 instructions).
PID hash table entries: 64 (order: 6, 256 bytes)
Using 75.000 MHz high precision timer.
Dentry cache hash table entries: 2048 (order: 1, 8192 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 12228k/16384k available (2315k kernel code, 4156k reserved, 423k data, 112k init, 0k highmem)
SLUB: Genslabs=17, HWalign=32, Order=0-1, MinObjects=4, CPUs=1, Nodes=1
Mount-cache hash table entries: 512
NET: Registered protocol family 16
vlynq0: regs 0x08611800, irq 29, mem 0x04000000
vlynq1: regs 0x08611c00, irq 33, mem 0x0c000000
vlynq-pci: attaching device TI TNETW1130 at vlynq0
registering PCI controller with io_map_base unset
Generic PHY: Registered new driver
NET: Registered protocol family 8
NET: Registered protocol family 20
Time: MIPS clocksource has been installed.
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 512 (order: 0, 4096 bytes)
TCP bind hash table entries: 512 (order: -1, 2048 bytes)
TCP: Hash tables configured (established 512 bind 512)
TCP reno registered
squashfs: version 3.0 (2006/03/15) Phillip Lougher
Registering mini_fo version $Id$
JFFS2 version 2.2. (NAND) (SUMMARY) © 2001-2006 Red Hat, Inc.
io scheduler noop registered
io scheduler deadline registered (default)
ar7_wdt: timer margin 59 seconds (prescale 65535, change 57180, freq 62500000)
Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled
serial8250: ttyS0 at MMIO 0x8610e00 (irq = 15) is a TI-AR7
console handover: boot [early0] -> real [ttyS0]
serial8250: ttyS1 at MMIO 0x8610f00 (irq = 16) is a TI-AR7
Fixed PHY: Registered new driver
PPP generic driver version 2.4.2
cpmac-mii: probed
cpmac: device eth0 (regs: 08612800, irq: 41, phy: fixed@100:1, mac: 00:13:10:01:c5:2a)
cpmac: device eth1 (regs: 08610000, irq: 27, phy: 0:1f, mac: 00:13:10:01:c5:2a)
physmap platform flash device: 00800000 at 10000000
physmap-flash.0: Found 1 x16 devices at 0x0 in 16-bit bank
Amd/Fujitsu Extended Query Table at 0x0040
physmap-flash.0: Swapping erase regions for broken CFI table.
number of CFI chips: 1
cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.
cmdlinepart partition parsing not available
RedBoot partition parsing not available
Parsing AR7 partition map...
4 ar7part partitions found on MTD device physmap-flash.0
Creating 4 MTD partitions on "physmap-flash.0":
0x00000000-0x00010000 : "loader"
0x003f0000-0x00400000 : "config"
0x00020000-0x003f0000 : "linux"
0x0010008e-0x003f0000 : "rootfs"
mtd: partition "rootfs" doesn't start on an erase block boundary -- force read-only
mtd: partition "rootfs" set to be root filesystem
mtd: partition "rootfs_data" created automatically, ofs=280000, len=170000
0x00280000-0x003f0000 : "rootfs_data"
Registered led device: ar7:status
u32 classifier
Actions configured
nf_conntrack version 0.5.0 (128 buckets, 1024 max)
ip_tables: (C) 2000-2006 Netfilter Core Team
TCP cubic registered
NET: Registered protocol family 1
NET: Registered protocol family 10
IPv6 over IPv4 tunneling driver
NET: Registered protocol family 17
VFS: Mounted root (squashfs filesystem) readonly.
Freeing unused kernel memory: 112k freed
Warning: unable to open an initial console.
Algorithmics/MIPS FPU Emulator v1.5
mini_fo: using base directory: /
mini_fo: using storage directory: /tmp/root
init started: BusyBox v1.4.2 (2007-09-16 03:20:09 CEST) multi-call binary
PHY: fixed@100:1 - Link is Up - 10/Half
registered device TI Avalanche SAR
Sangam detected
requesting firmware image "ar0700xx.bin"
Creating new root folder avalanche in the proc for the driver stats
Texas Instruments ATM driver: version:[7.02.01.00]
jffs2_scan_eraseblock(): End of filesystem marker found at 0x0
jffs2_build_filesystem(): unlocking the mtd device... done.
jffs2_build_filesystem(): erasing all blocks after the end marker... done.
eth0: no IPv6 routers present
mini_fo: using base directory: /
mini_fo: using storage directory: /jffs
[/]# uname -a
Linux OpenWrt 2.6.22.4 #1 Thu Sep 20 16:41:39 CEST 2007 mips unknown
[/]# cat /proc/cpuinfo
system type : TI AR7 (TNETD7300)
processor : 0
cpu model : MIPS 4KEc V4.8
BogoMIPS : 149.50
wait instruction : yes
microsecond timers : yes
tlb_entries : 16
extra interrupt vector : yes
hardware watchpoint : yes
ASEs implemented :
VCED exceptions : not available
VCEI exceptions : not available
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-20 15:28 [PATCH][MIPS][0/7] AR7: 4th effort Matteo Croce
@ 2007-09-20 16:06 ` Matteo Croce
2007-10-03 19:24 ` Wim Van Sebroeck
0 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-20 16:06 UTC (permalink / raw)
To: linux-mips
Cc: Nicolas Thill, Enrik Berkhan, Christer Weinigel, wim,
Andrew Morton
Driver for the watchdog timer. Still doesn't reboots the machine
on some boards, but we have improved and cleaned it
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
Signed-off-by: Enrik Berkhan <Enrik.Berkhan@akk.org>
Signed-off-by: Christer Weinigel <wingel@nano-system.com>
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 37bddc1..78d4940 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -583,6 +583,12 @@ config SBC_EPX_C3_WATCHDOG
# MIPS Architecture
+config AR7_WDT
+ tristate "TI AR7 Watchdog Timer"
+ depends on WATCHDOG && AR7
+ help
+ Hardware driver for the TI AR7 Watchdog Timer.
+
config INDYDOG
tristate "Indy/I2 Hardware Watchdog"
depends on SGI_IP22
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 389f8b1..76424f2 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
# M68KNOMMU Architecture
# MIPS Architecture
+obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
obj-$(CONFIG_INDYDOG) += indydog.o
obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
diff --git a/drivers/char/watchdog/ar7_wdt.c b/drivers/char/watchdog/ar7_wdt.c
new file mode 100644
index 0000000..93c6c16
--- /dev/null
+++ b/drivers/char/watchdog/ar7_wdt.c
@@ -0,0 +1,351 @@
+/*
+ * linux/drivers/char/ar7_wdt.c
+ *
+ * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
+ * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
+ *
+ * Some code taken from:
+ * National Semiconductor SCx200 Watchdog support
+ * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+ *
+ * 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 <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/addrspace.h>
+#include <asm/ar7/ar7.h>
+
+#define DRVNAME "ar7_wdt"
+#define LONGNAME "TI AR7 Watchdog Timer"
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int margin = 60;
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+#define READ_REG(x) readl((void __iomem *)&(x))
+#define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
+
+struct ar7_wdt {
+ u32 kick_lock;
+ u32 kick;
+ u32 change_lock;
+ u32 change;
+ u32 disable_lock;
+ u32 disable;
+ u32 prescale_lock;
+ u32 prescale;
+};
+
+static struct semaphore open_semaphore;
+static unsigned expect_close;
+
+/* XXX currently fixed, allows max margin ~68.72 secs */
+#define prescale_value 0xffff
+
+/* Offset of the WDT registers */
+static unsigned long ar7_regs_wdt;
+/* Pointer to the remapped WDT IO space */
+static struct ar7_wdt *ar7_wdt;
+static void ar7_wdt_get_regs(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7100:
+ case AR7_CHIP_7200:
+ ar7_regs_wdt = AR7_REGS_WDT;
+ break;
+ default:
+ ar7_regs_wdt = UR8_REGS_WDT;
+ break;
+ }
+}
+
+
+static void ar7_wdt_kick(u32 value)
+{
+ WRITE_REG(ar7_wdt->kick_lock, 0x5555);
+ if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
+ if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->kick, value);
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
+}
+
+static void ar7_wdt_prescale(u32 value)
+{
+ WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
+ if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
+ if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->prescale, value);
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
+}
+
+static void ar7_wdt_change(u32 value)
+{
+ WRITE_REG(ar7_wdt->change_lock, 0x6666);
+ if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
+ if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->change, value);
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
+}
+
+static void ar7_wdt_disable(u32 value)
+{
+ WRITE_REG(ar7_wdt->disable_lock, 0x7777);
+ if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
+ if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
+ WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
+ if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->disable, value);
+ return;
+ }
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
+}
+
+static void ar7_wdt_update_margin(int new_margin)
+{
+ u32 change;
+
+ change = new_margin * (ar7_vbus_freq() / prescale_value);
+ if (change < 1) change = 1;
+ if (change > 0xffff) change = 0xffff;
+ ar7_wdt_change(change);
+ margin = change * prescale_value / ar7_vbus_freq();
+ printk(KERN_INFO DRVNAME
+ ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
+ margin, prescale_value, change, ar7_vbus_freq());
+}
+
+static void ar7_wdt_enable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
+ ar7_wdt_disable(1);
+ ar7_wdt_kick(1);
+}
+
+static void ar7_wdt_disable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
+ ar7_wdt_disable(0);
+}
+
+static int ar7_wdt_open(struct inode *inode, struct file *file)
+{
+ /* only allow one at a time */
+ if (down_trylock(&open_semaphore))
+ return -EBUSY;
+ ar7_wdt_enable_wdt();
+ expect_close = 0;
+
+ return 0;
+}
+
+static int ar7_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!expect_close)
+ printk(KERN_WARNING DRVNAME
+ ": watchdog device closed unexpectedly,"
+ "will not disable the watchdog timer\n");
+ else if (!nowayout)
+ ar7_wdt_disable_wdt();
+
+ up(&open_semaphore);
+
+ return 0;
+}
+
+static int ar7_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_HALT || code == SYS_POWER_OFF)
+ if (!nowayout)
+ ar7_wdt_disable_wdt();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ar7_wdt_notifier = {
+ .notifier_call = ar7_wdt_notify_sys
+};
+
+static ssize_t ar7_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (*ppos != file->f_pos)
+ return -ESPIPE;
+
+ /* check for a magic close character */
+ if (len) {
+ size_t i;
+
+ ar7_wdt_kick(1);
+
+ expect_close = 0;
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data+i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 1;
+ }
+
+ }
+ return len;
+}
+
+static int ar7_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info ident = {
+ .identity = LONGNAME,
+ .firmware_version = 1,
+ .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
+ };
+ int new_margin;
+
+ switch (cmd) {
+ default:
+ return -ENOTTY;
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int *)arg))
+ return -EFAULT;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ ar7_wdt_kick(1);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int *)arg))
+ return -EFAULT;
+ if (new_margin < 1)
+ return -EINVAL;
+
+ ar7_wdt_update_margin(new_margin);
+ ar7_wdt_kick(1);
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(margin, (int *)arg))
+ return -EFAULT;
+ return 0;
+ }
+}
+
+static struct file_operations ar7_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ar7_wdt_write,
+ .ioctl = ar7_wdt_ioctl,
+ .open = ar7_wdt_open,
+ .release = ar7_wdt_release,
+};
+
+static struct miscdevice ar7_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ar7_wdt_fops,
+};
+
+static int __init ar7_wdt_init(void)
+{
+ int rc;
+
+ ar7_wdt_get_regs();
+
+ if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt),
+ LONGNAME)) {
+ printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
+ return -EBUSY;
+ }
+
+ ar7_wdt = (struct ar7_wdt *)
+ ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt));
+
+ ar7_wdt_disable_wdt();
+ ar7_wdt_prescale(prescale_value);
+ ar7_wdt_update_margin(margin);
+
+ sema_init(&open_semaphore, 1);
+
+ rc = misc_register(&ar7_wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR DRVNAME ": unable to register misc device\n");
+ goto out_alloc;
+ }
+
+ rc = register_reboot_notifier(&ar7_wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR DRVNAME
+ ": unable to register reboot notifier\n");
+ goto out_register;
+ }
+ goto out;
+
+out_register:
+ misc_deregister(&ar7_wdt_miscdev);
+out_alloc:
+ release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
+out:
+ return rc;
+}
+
+static void __exit ar7_wdt_cleanup(void)
+{
+ unregister_reboot_notifier(&ar7_wdt_notifier);
+ misc_deregister(&ar7_wdt_miscdev);
+ iounmap(ar7_wdt);
+ release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
+}
+
+module_init(ar7_wdt_init);
+module_exit(ar7_wdt_cleanup);
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-20 16:06 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
@ 2007-10-03 19:24 ` Wim Van Sebroeck
2007-10-03 20:17 ` Matteo Croce
0 siblings, 1 reply; 30+ messages in thread
From: Wim Van Sebroeck @ 2007-10-03 19:24 UTC (permalink / raw)
To: Matteo Croce
Cc: linux-mips, Nicolas Thill, Enrik Berkhan, Christer Weinigel,
Andrew Morton
Hi Matteo,
Sorry for the late response. Some personal/work issues
prevented me in reacting faster.
> +static int ar7_wdt_open(struct inode *inode, struct file *file)
> +{
> + /* only allow one at a time */
> + if (down_trylock(&open_semaphore))
> + return -EBUSY;
> + ar7_wdt_enable_wdt();
> + expect_close = 0;
> +
> + return 0;
> +}
The /dev/watchdog device is a VFS (Virtual File System). We thus
use a: return nonseekable_open(inode, file);
> +static ssize_t ar7_wdt_write(struct file *file, const char *data,
> + size_t len, loff_t *ppos)
> +{
> + if (*ppos != file->f_pos)
> + return -ESPIPE;
> +
Since we use the nonseekable_open we don't need the
if (*ppos != file->f_pos) return -ESPIPE;
> +static int __init ar7_wdt_init(void)
> +{
...
> + rc = misc_register(&ar7_wdt_miscdev);
> + if (rc) {
> + printk(KERN_ERR DRVNAME ": unable to register misc device\n");
> + goto out_alloc;
> + }
> +
> + rc = register_reboot_notifier(&ar7_wdt_notifier);
> + if (rc) {
> + printk(KERN_ERR DRVNAME
> + ": unable to register reboot notifier\n");
> + goto out_register;
> + }
> + goto out;
> +
> +out_register:
> + misc_deregister(&ar7_wdt_miscdev);
> +out_alloc:
> + release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
> +out:
> + return rc;
> +}
It's better to first register the reboot-notifier instead of
registering the misc-device. The misc-device gives userspace
allready access to the device and that's something that you
want to do as the last action to prevent problems.
For the rest: all OK.
If you want I'll add it to the linux-2.6-watchdog-mm tree with
the above mentioned changes.
Greetings,
Wim.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-10-03 19:24 ` Wim Van Sebroeck
@ 2007-10-03 20:17 ` Matteo Croce
2007-10-07 20:31 ` Wim Van Sebroeck
0 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-10-03 20:17 UTC (permalink / raw)
To: Wim Van Sebroeck
Cc: linux-mips, Nicolas Thill, Enrik Berkhan, Christer Weinigel,
Andrew Morton
> If you want I'll add it to the linux-2.6-watchdog-mm tree with
> the above mentioned changes.
Yes, please
Cheers
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-10-03 20:17 ` Matteo Croce
@ 2007-10-07 20:31 ` Wim Van Sebroeck
0 siblings, 0 replies; 30+ messages in thread
From: Wim Van Sebroeck @ 2007-10-07 20:31 UTC (permalink / raw)
To: Matteo Croce
Cc: linux-mips, Nicolas Thill, Enrik Berkhan, Christer Weinigel,
Andrew Morton
Hi Matteo,
> > If you want I'll add it to the linux-2.6-watchdog-mm tree with
> > the above mentioned changes.
> Yes, please
It's in the linux-2.6-watchdog-mm tree. I added an extra iounmap in the
errorhandling of the init procedure.
Please test.
Thanks,
Wim.
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 1/1] AR7 port
@ 2007-08-20 15:04 Matteo Croce
2007-09-06 15:31 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
0 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-08-20 15:04 UTC (permalink / raw)
To: linux-mips
Hi,
I made a diff for the AR7 port, hoping that it will go in the mainstream
kernel.
I cared about don't including buggy or non free drivers and code.
Also we have to surround the code in arch/mips/kernel/traps.c by some #ifdef
since
the actual code will break other archs.
The code was taken from the OpenWrt project, is quite good, many people uses
it daily.
Cheers
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
diff -urN linux-2.6.22.1/arch/mips/ar7/clock.c linux-ar7/arch/mips/ar7/clock.c
--- linux-2.6.22.1/arch/mips/ar7/clock.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/clock.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,472 @@
+/*
+ * $Id: clock.c 8036 2007-07-18 13:42:24Z florian $
+ *
+ * Copyright (C) 2007 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/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 {
+ volatile u32 ctrl;
+#define PREDIV_MASK 0x001f0000
+#define PREDIV_SHIFT 16
+#define POSTDIV_MASK 0x0000001f
+ u32 unused1[3];
+ volatile 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];
+} __attribute__ ((packed));
+
+struct tnetd7300_clocks {
+ struct tnetd7300_clock bus;
+ struct tnetd7300_clock cpu;
+ struct tnetd7300_clock usb;
+ struct tnetd7300_clock dsp;
+} __attribute__ ((packed));
+
+struct tnetd7200_clock {
+ volatile u32 ctrl;
+ u32 unused1[3];
+#define DIVISOR_ENABLE_MASK 0x00008000
+ volatile u32 mul;
+ volatile u32 prediv;
+ volatile u32 postdiv;
+ volatile u32 postdiv2;
+ u32 unused2[6];
+ volatile u32 cmd;
+ volatile u32 status;
+ volatile u32 cmden;
+ u32 padding[15];
+} __attribute__ ((packed));
+
+struct tnetd7200_clocks {
+ struct tnetd7200_clock cpu;
+ struct tnetd7200_clock dsp;
+ struct tnetd7200_clock usb;
+} __attribute__ ((packed));
+
+int ar7_afe_clock = 35328000;
+int ar7_ref_clock = 25000000;
+int ar7_xtal_clock = 24000000;
+
+int ar7_cpu_clock = 150000000;
+EXPORT_SYMBOL(ar7_cpu_clock);
+int ar7_bus_clock = 125000000;
+EXPORT_SYMBOL(ar7_bus_clock);
+int ar7_dsp_clock = 0;
+EXPORT_SYMBOL(ar7_dsp_clock);
+
+static int gcd(int x, int y)
+{
+ if (x > y)
+ return (x % y) ? gcd(y, x % y) : y;
+ return (y % x) ? gcd(x, y % x) : x;
+}
+
+static inline int ABS(int x)
+{
+ return (x >= 0) ? x : -x;
+}
+
+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 = clock->ctrl;
+ u32 pll = 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)
+{
+ u32 status;
+ 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);
+
+ clock->ctrl = ((prediv - 1) << PREDIV_SHIFT) | (postdiv - 1);
+ mdelay(1);
+ clock->pll = 4;
+ do {
+ status = clock->pll;
+ } while (status & PLL_STATUS);
+ clock->pll = ((mul - 1) << MUL_SHIFT) | (0xff << 3) | 0x0e;
+ 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 = ((clock->prediv & 0x1f) + 1) *
+ ((clock->postdiv & 0x1f) + 1);
+
+ if (*bootcr & BOOT_PLL_BYPASS)
+ return base / divisor;
+
+ return base * ((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("Clocks: base = %d, frequency = %u, prediv = %d, postdiv = %d,
postdiv2 = %d, mul = %d\n",
+ base, frequency, prediv, postdiv, postdiv2, mul);
+
+ clock->ctrl = 0;
+ clock->prediv = DIVISOR_ENABLE_MASK | ((prediv - 1) & 0x1F);
+ clock->mul = ((mul - 1) & 0xF);
+
+ for(mul = 0; mul < 2000; mul++) /* nop */;
+
+ while(clock->status & 0x1) /* nop */;
+
+ clock->postdiv = DIVISOR_ENABLE_MASK | ((postdiv - 1) & 0x1F);
+
+ clock->cmden |= 1;
+ clock->cmd |= 1;
+
+ while(clock->status & 0x1) /* nop */;
+
+ clock->postdiv2 = DIVISOR_ENABLE_MASK | ((postdiv2 - 1) & 0x1F);
+
+ clock->cmden |= 1;
+ clock->cmd |= 1;
+
+ while(clock->status & 0x1) /* nop */;
+
+ clock->ctrl |= 1;
+}
+
+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("Clocks: Async mode\n");
+
+ printk("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("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("Clocks: Sync 2:1 mode\n");
+
+ printk("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("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("Clocks: Sync 1:1 mode\n");
+
+ printk("Clocks: Setting DSP clock\n");
+ calculate(dsp_base, TNETD7200_DEF_CPU_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("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: ????! Hrmm
+ 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 -urN linux-2.6.22.1/arch/mips/ar7/gpio.c linux-ar7/arch/mips/ar7/gpio.c
--- linux-2.6.22.1/arch/mips/ar7/gpio.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/gpio.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,56 @@
+/*
+ * $Id: gpio.c 6600 2007-03-18 09:40:51Z ejka $
+ *
+ * Copyright (C) 2007 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 <linux/platform_device.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/gpio.h>
+
+static char *ar7_gpio_list[AR7_GPIO_MAX] = { 0, };
+
+int gpio_request(unsigned gpio, 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 -urN linux-2.6.22.1/arch/mips/ar7/irq.c linux-ar7/arch/mips/ar7/irq.c
--- linux-2.6.22.1/arch/mips/ar7/irq.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/irq.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,205 @@
+/*
+ * $Id: irq.c 7487 2007-06-04 09:46:30Z nbd $
+ *
+ * Copyright (C) 2006, 2007 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/interrupt.h>
+#include <linux/ioport.h>
+
+#include <asm/irq.h>
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/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) (*(volatile 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_unmask_secondary_irq(unsigned int irq_nr);
+static void ar7_mask_secondary_irq(unsigned int irq_nr);
+static irqreturn_t ar7_cascade(int interrupt, void *dev);
+static irqreturn_t ar7_secondary_cascade(int interrupt, void *dev);
+static void ar7_irq_init(int base);
+static int ar7_irq_base;
+
+static struct irq_chip ar7_irq_type = {
+ .typename = "AR7",
+ .name = "AR7",
+ .unmask = ar7_unmask_irq,
+ .mask = ar7_mask_irq,
+};
+
+static struct irq_chip ar7_secondary_irq_type = {
+ .name = "AR7",
+ .unmask = ar7_unmask_secondary_irq,
+ .mask = ar7_mask_secondary_irq,
+};
+
+static struct irqaction ar7_cascade_action = {
+ .handler = ar7_cascade,
+ .name = "AR7 cascade interrupt"
+};
+
+static struct irqaction ar7_secondary_cascade_action = {
+ .handler = ar7_secondary_cascade,
+ .name = "AR7 secondary cascade interrupt"
+};
+
+static void ar7_unmask_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* enable the interrupt channel bit */
+ REG(ESR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
+ local_irq_restore(flags);
+}
+
+static void ar7_mask_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* disable the interrupt channel bit */
+ REG(ECR_OFFSET(irq)) = 1 << ((irq - ar7_irq_base) % 32);
+ local_irq_restore(flags);
+}
+
+static void ar7_unmask_secondary_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* enable the interrupt channel bit */
+ REG(SEC_ESR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
+ local_irq_restore(flags);
+}
+
+static void ar7_mask_secondary_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ /* disable the interrupt channel bit */
+ REG(SEC_ECR_OFFSET) = 1 << (irq - ar7_irq_base - 40);
+ local_irq_restore(flags);
+}
+
+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
+ */
+ REG(ECR_OFFSET(0)) = 0xffffffff;
+ REG(ECR_OFFSET(32)) = 0xff;
+ REG(SEC_ECR_OFFSET) = 0xffffffff;
+ REG(CR_OFFSET(0)) = 0xffffffff;
+ REG(CR_OFFSET(32)) = 0xff;
+ REG(SEC_CR_OFFSET) = 0xffffffff;
+
+ ar7_irq_base = base;
+
+ for(i = 0; i < 40; i++) {
+ REG(CHNL_OFFSET(i)) = i;
+ /* Primary IRQ's */
+ irq_desc[i + base].status = IRQ_DISABLED;
+ irq_desc[i + base].action = NULL;
+ irq_desc[i + base].depth = 1;
+ irq_desc[i + base].chip = &ar7_irq_type;
+ /* Secondary IRQ's */
+ if (i < 32) {
+ irq_desc[i + base + 40].status = IRQ_DISABLED;
+ irq_desc[i + base + 40].action = NULL;
+ irq_desc[i + base + 40].depth = 1;
+ irq_desc[i + base + 40].chip = &ar7_secondary_irq_type;
+ }
+ }
+
+ setup_irq(2, &ar7_cascade_action);
+ setup_irq(ar7_irq_base, &ar7_secondary_cascade_action);
+ set_c0_status(IE_IRQ0);
+}
+
+static irqreturn_t ar7_cascade(int interrupt, void *dev)
+{
+ int irq;
+
+ irq = (REG(PIR_OFFSET) & 0x3F);
+ REG(CR_OFFSET(irq)) = 1 << (irq % 32);
+
+ do_IRQ(irq + ar7_irq_base);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ar7_secondary_cascade(int interrupt, void *dev)
+{
+ int irq = 0, i;
+ unsigned long status;
+
+ status = REG(SEC_SR_OFFSET);
+ if (unlikely(!status)) {
+ spurious_interrupt();
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < 32; i++)
+ if (status & (i << 1)) {
+ irq = i + 40;
+ REG(SEC_CR_OFFSET) = 1 << i;
+ break;
+ }
+
+ do_IRQ(irq + ar7_irq_base);
+
+ return IRQ_HANDLED;
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int pending = read_c0_status() & read_c0_cause();
+ if (pending & STATUSF_IP7) /* cpu timer */
+ do_IRQ(7);
+ else if (pending & STATUSF_IP2) /* int0 hardware line */
+ do_IRQ(2);
+ else
+ spurious_interrupt();
+}
diff -urN linux-2.6.22.1/arch/mips/ar7/Makefile
linux-ar7/arch/mips/ar7/Makefile
--- linux-2.6.22.1/arch/mips/ar7/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/Makefile 2007-08-09 16:27:10.000000000 +0200
@@ -0,0 +1,13 @@
+
+obj-y := \
+ prom.o \
+ setup.o \
+ memory.o \
+ irq.o \
+ time.o \
+ platform.o \
+ gpio.o \
+ clock.o \
+ vlynq.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff -urN linux-2.6.22.1/arch/mips/ar7/memory.c
linux-ar7/arch/mips/ar7/memory.c
--- linux-2.6.22.1/arch/mips/ar7/memory.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/memory.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,75 @@
+/*
+ * $Id: memory.c 8189 2007-07-27 03:07:52Z nico $
+ *
+ * Copyright (C) 2007 OpenWrt.org
+ *
+ * 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);
+ volatile u32 *addr = (u32 *)KSEG1ADDR(0x14000000 + size - 4);
+ u32 *kernel_end = (u32 *)KSEG1ADDR(CPHYSADDR((u32)&_end));
+
+ while (addr > kernel_end) {
+ *addr = (u32)addr;
+ size >>= 1;
+ addr -= size >> 2;
+ }
+
+ do {
+ addr += size >> 2;
+ if (*addr != (u32)addr)
+ break;
+ size <<= 1;
+ } while (size < (64 << 20));
+
+ return size;
+}
+
+void __init prom_meminit(void)
+{
+ unsigned long pages;
+
+ pages = memsize() >> PAGE_SHIFT;
+ add_memory_region(ARCH_PFN_OFFSET << PAGE_SHIFT, pages <<
+ PAGE_SHIFT, BOOT_MEM_RAM);
+}
+
+void __init prom_free_prom_memory(void)
+{
+ return;
+}
diff -urN linux-2.6.22.1/arch/mips/ar7/platform.c
linux-ar7/arch/mips/ar7/platform.c
--- linux-2.6.22.1/arch/mips/ar7/platform.c 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/arch/mips/ar7/platform.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,412 @@
+/*
+ * $Id: platform.c 8280 2007-07-31 15:52:06Z florian $
+ *
+ * Copyright (C) 2006, 2007 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/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/ioport.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/gpio.h>
+#include <asm/ar7/prom.h>
+#include <asm/ar7/vlynq.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)))
+ goto out_gpio_enabled;
+
+ gpio_set_value(pdata->gpio_bit, 0);
+ 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 = 0x103fffff,
+};
+
+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 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 struct platform_device cpmac_low = {
+ .id = 0,
+ .name = "cpmac",
+ .dev.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.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 inline unsigned char char2hex(char h)
+{
+ switch (h) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return h - '0';
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return h - 'A' + 10;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return h - 'a' + 10;
+ default:
+ return 0;
+ }
+}
+
+static void cpmac_get_mac(int instance, unsigned char *dev_addr)
+{
+ int i;
+ char name[5], default_mac[] = "00:00:00:12:34:56", *mac;
+
+ mac = NULL;
+ sprintf(name, "mac%c", 'a' + instance);
+ mac = prom_getenv(name);
+ if (!mac) {
+ sprintf(name, "mac%c", 'a');
+ mac = prom_getenv(name);
+ }
+ if (!mac)
+ mac = default_mac;
+ for (i = 0; i < 6; i++)
+ dev_addr[i] = (char2hex(mac[i * 3]) << 4) +
+ char2hex(mac[i * 3 + 1]);
+}
+
+static int __init ar7_register_devices(void)
+{
+ int res;
+
+#ifdef CONFIG_SERIAL_8250
+
+ static struct uart_port uart_port[2];
+
+ memset(uart_port, 0, sizeof(struct uart_port) * 2);
+
+ uart_port[0].type = PORT_AR7;
+ uart_port[0].line = 0;
+ uart_port[0].irq = AR7_IRQ_UART0;
+ uart_port[0].uartclk = ar7_bus_freq() / 2;
+ uart_port[0].iotype = UPIO_MEM;
+ uart_port[0].mapbase = AR7_REGS_UART0;
+ uart_port[0].membase = ioremap(uart_port[0].mapbase, 256);
+ uart_port[0].regshift = 2;
+ res = early_serial_setup(&uart_port[0]);
+ if (res)
+ return res;
+
+
+ // Only TNETD73xx have a second serial port
+ if (ar7_has_second_uart()) {
+ uart_port[1].type = PORT_AR7;
+ uart_port[1].line = 1;
+ uart_port[1].irq = AR7_IRQ_UART1;
+ uart_port[1].uartclk = ar7_bus_freq() / 2;
+ uart_port[1].iotype = UPIO_MEM;
+ uart_port[1].mapbase = UR8_REGS_UART1;
+ uart_port[1].membase = ioremap(uart_port[1].mapbase, 256);
+ uart_port[1].regshift = 2;
+ res = early_serial_setup(&uart_port[1]);
+ if (res)
+ return res;
+ }
+
+#else // !CONFIG_SERIAL_8250
+
+ uart_data[0].uartclk = ar7_bus_freq() / 2;
+ uart_data[1].uartclk = uart_data[0].uartclk;
+
+ // Only TNETD73xx have a second serial port
+ if (ar7_has_second_uart()) {
+ uart.dev.platform_data = uart_data;
+ }
+
+ res = platform_device_register(&uart);
+ if (res)
+ return res;
+
+#endif // CONFIG_SERIAL_8250
+
+ res = platform_device_register(&physmap_flash);
+ if (res)
+ return res;
+
+ res = platform_device_register(&vlynq_low);
+ if (res)
+ return res;
+
+ ar7_device_disable(vlynq_low_data.reset_bit);
+ if (ar7_has_high_vlynq()) {
+ ar7_device_disable(vlynq_high_data.reset_bit);
+ res = platform_device_register(&vlynq_high);
+ if (res)
+ return res;
+ }
+
+ if (ar7_has_high_cpmac()) {
+ cpmac_get_mac(1, cpmac_high_data.dev_addr);
+ res = platform_device_register(&cpmac_high);
+ if (res)
+ return res;
+ } else {
+ cpmac_low_data.phy_mask = 0xffffffff;
+ }
+
+ cpmac_get_mac(0, cpmac_low_data.dev_addr);
+ res = platform_device_register(&cpmac_low);
+
+ return res;
+}
+
+
+arch_initcall(ar7_register_devices);
diff -urN linux-2.6.22.1/arch/mips/ar7/prom.c linux-ar7/arch/mips/ar7/prom.c
--- linux-2.6.22.1/arch/mips/ar7/prom.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/prom.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,327 @@
+/*
+ * $Id: prom.c 8189 2007-07-27 03:07:52Z nico $
+ *
+ * Copyright (C) 2006, 2007 OpenWrt.org
+ *
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute 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 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.
+ *
+ * Putting things on the screen/serial line using YAMONs facilities.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/mips-boards/prom.h>
+#include <asm/gdb-stub.h>
+
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/prom.h>
+
+#define MAX_ENTRY 80
+
+struct env_var {
+ char *name;
+ char *value;
+};
+
+static struct env_var adam2_env[MAX_ENTRY] = { { 0, }, };
+
+char * prom_getenv(char *name)
+{
+ int i;
+ for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++)
+ if (!strcmp(name, adam2_env[i].name))
+ return adam2_env[i].value;
+
+ return NULL;
+}
+
+char * __init prom_getcmdline(void)
+{
+ return &(arcs_cmdline[0]);
+}
+
+static void __init ar7_init_cmdline(int argc, char *argv[])
+{
+ char *cp;
+ int actr;
+
+ actr = 1; /* Always ignore argv[0] */
+
+ cp = &(arcs_cmdline[0]);
+ while(actr < argc) {
+ strcpy(cp, argv[actr]);
+ cp += strlen(argv[actr]);
+ *cp++ = ' ';
+ actr++;
+ }
+ if (cp != &(arcs_cmdline[0])) {
+ /* get rid of trailing space */
+ --cp;
+ *cp = '\0';
+ }
+}
+
+struct psbl_rec {
+ u32 psbl_size;
+ u32 env_base;
+ u32 env_size;
+ u32 ffs_base;
+ u32 ffs_size;
+};
+
+static __initdata char psp_env_version[] = "TIENV0.8";
+
+struct psp_env_chunk {
+ u8 num;
+ u8 ctrl;
+ u16 csum;
+ u8 len;
+ char data[11];
+} __attribute__ ((packed));
+
+struct psp_var_map_entry {
+ u8 num;
+ char *value;
+};
+
+static struct psp_var_map_entry psp_var_map[] = {
+ { 1, "cpufrequency" },
+ { 2, "memsize" },
+ { 3, "flashsize" },
+ { 4, "modetty0" },
+ { 5, "modetty1" },
+ { 8, "maca" },
+ { 9, "macb" },
+ { 28, "sysfrequency" },
+ { 38, "mipsfrequency" },
+};
+
+/*
+
+Well-known variable (num is looked up in table above for matching variable
name)
+Example: cpufrequency=211968000
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+| 01 |CTRL|CHECKSUM | 01 | _2 | _1 | _1 | _9 | _6 | _8 | _0 | _0 | _0 | \0 |
FF |
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+
+Name=Value pair in a single chunk
+Example: NAME=VALUE
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+| 00 |CTRL|CHECKSUM | 01 | _N | _A | _M | _E | _0 | _V | _A | _L | _U | _E |
\0 |
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+
+Name=Value pair in 2 chunks (len is the number of chunks)
+Example: bootloaderVersion=1.3.7.15
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+| 00 |CTRL|CHECKSUM | 02 | _b | _o | _o | _t | _l | _o | _a | _d | _e | _r |
_V |
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+| _e | _r | _s | _i | _o | _n | \0 | _1 | _. | _3 | _. | _7 | _. | _1 | _5 |
\0 |
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+
+Data is padded with 0xFF
+
+*/
+
+#define PSP_ENV_SIZE 4096
+
+static char psp_env_data[PSP_ENV_SIZE] = { 0, };
+
+static char * __init lookup_psp_var_map(u8 num)
+{
+ int i;
+
+ for (i = 0; i < sizeof(psp_var_map); i++)
+ if (psp_var_map[i].num == num)
+ return psp_var_map[i].value;
+
+ return NULL;
+}
+
+static void __init add_adam2_var(char *name, char *value)
+{
+ int i;
+ for (i = 0; i < MAX_ENTRY; i++) {
+ if (!adam2_env[i].name) {
+ adam2_env[i].name = name;
+ adam2_env[i].value = value;
+ return;
+ } else if (!strcmp(adam2_env[i].name, name)) {
+ adam2_env[i].value = value;
+ return;
+ }
+ }
+}
+
+static int __init parse_psp_env(void *psp_env_base)
+{
+ int i, n;
+ char *name, *value;
+ struct psp_env_chunk *chunks = (struct psp_env_chunk *)psp_env_data;
+
+ memcpy_fromio(chunks, psp_env_base, PSP_ENV_SIZE);
+
+ i = 1;
+ n = PSP_ENV_SIZE / sizeof(struct psp_env_chunk);
+ while (i < n) {
+ if ((chunks[i].num == 0xff) || ((i + chunks[i].len) > n))
+ break;
+ value = chunks[i].data;
+ if (chunks[i].num) {
+ name = lookup_psp_var_map(chunks[i].num);
+ } else {
+ name = value;
+ value += strlen(name) + 1;
+ }
+ if (name)
+ add_adam2_var(name, value);
+ i += chunks[i].len;
+ }
+ return 0;
+}
+
+static void __init ar7_init_env(struct env_var *env)
+{
+ int i;
+ struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300));
+ void *psp_env = (void *)KSEG1ADDR(psbl->env_base);
+
+ if(strcmp(psp_env, psp_env_version) == 0) {
+ parse_psp_env(psp_env);
+ } else {
+ for (i = 0; i < MAX_ENTRY; i++, env++)
+ if (env->name)
+ add_adam2_var(env->name, env->value);
+ }
+}
+
+static void __init console_config(void)
+{
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+ char console_string[40];
+ int baud = 0;
+ char parity = '\0', bits = '\0', flow = '\0';
+ char *s, *p;
+
+ if (strstr(prom_getcmdline(), "console="))
+ return;
+
+#ifdef CONFIG_KGDB
+ if (!strstr(prom_getcmdline(), "nokgdb"))
+ {
+ strcat(prom_getcmdline(), " console=kgdb");
+ kgdb_enabled = 1;
+ return;
+ }
+#endif
+
+ if ((s = prom_getenv("modetty0"))) {
+ baud = simple_strtoul(s, &p, 10);
+ s = p;
+ if (*s == ',') s++;
+ if (*s) parity = *s++;
+ if (*s == ',') s++;
+ if (*s) bits = *s++;
+ if (*s == ',') s++;
+ if (*s == 'h') flow = 'r';
+ }
+
+ if (baud == 0)
+ baud = 38400;
+ if (parity != 'n' && parity != 'o' && parity != 'e')
+ parity = 'n';
+ if (bits != '7' && bits != '8')
+ bits = '8';
+ if (flow == '\0')
+ flow = 'r';
+
+ sprintf(console_string, " console=ttyS0,%d%c%c%c", baud,
+ parity, bits, flow);
+ strcat(prom_getcmdline(), console_string);
+#endif
+}
+
+void __init prom_init(void)
+{
+ ar7_init_cmdline(fw_arg0, (char **)fw_arg1);
+ ar7_init_env((struct env_var *)fw_arg2);
+ console_config();
+}
+
+#define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4)))
+static inline unsigned int serial_in(int offset)
+{
+ return readb((void *)PORT(offset));
+}
+
+static inline void serial_out(int offset, int value)
+{
+ writeb(value, (void *)PORT(offset));
+}
+
+char prom_getchar(void)
+{
+ while (!(serial_in(UART_LSR) & UART_LSR_DR));
+ return serial_in(UART_RX);
+}
+
+int prom_putchar(char c)
+{
+ while ((serial_in(UART_LSR) & UART_LSR_TEMT) == 0);
+ serial_out(UART_TX, c);
+ return 1;
+}
+
+// from adm5120/prom.c
+void prom_printf(char *fmt, ...)
+{
+ va_list args;
+ int l;
+ char *p, *buf_end;
+ char buf[1024];
+
+ va_start(args, fmt);
+ l = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf) */
+ va_end(args);
+
+ buf_end = buf + l;
+
+ for (p = buf; p < buf_end; p++) {
+ /* Crude cr/nl handling is better than none */
+ if (*p == '\n')
+ prom_putchar('\r');
+ prom_putchar(*p);
+ }
+}
+
+#ifdef CONFIG_KGDB
+int putDebugChar(char c)
+{
+ return prom_putchar(c);
+}
+
+char getDebugChar(void)
+{
+ return prom_getchar();
+}
+#endif
+
+EXPORT_SYMBOL(prom_getenv);
diff -urN linux-2.6.22.1/arch/mips/ar7/setup.c linux-ar7/arch/mips/ar7/setup.c
--- linux-2.6.22.1/arch/mips/ar7/setup.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/setup.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,124 @@
+/*
+ * $Id: setup.c 8189 2007-07-27 03:07:52Z nico $
+ *
+ * Copyright (C) 2006, 2007 OpenWrt.org
+ *
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute 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 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.
+ */
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/pm.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#include <asm/cpu.h>
+#include <asm/irq.h>
+#include <asm/mips-boards/generic.h>
+#include <asm/mips-boards/prom.h>
+#include <asm/dma.h>
+#include <asm/time.h>
+#include <asm/traps.h>
+#include <asm/io.h>
+#include <asm/reboot.h>
+#include <asm/gdb-stub.h>
+#include <asm/ar7/ar7.h>
+
+extern void ar7_time_init(void);
+static void ar7_machine_restart(char *command);
+static void ar7_machine_halt(void);
+static void ar7_machine_power_off(void);
+
+static void ar7_machine_restart(char *command)
+{
+ volatile u32 *softres_reg = (u32 *)ioremap(AR7_REGS_RESET +
+ AR7_RESET_SOFTWARE, 1);
+ *softres_reg = 1;
+}
+
+static void ar7_machine_halt(void)
+{
+ while (1);
+}
+
+static void ar7_machine_power_off(void)
+{
+ volatile u32 *power_reg = (u32 *)ioremap(AR7_REGS_POWER, 1);
+ u32 power_state = *power_reg | (3 << 30);
+ *power_reg = power_state;
+ ar7_machine_halt();
+}
+
+const char *get_system_type(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7300:
+ return "TI AR7 (TNETD7300)";
+ case AR7_CHIP_7100:
+ return "TI AR7 (TNETD7100)";
+ case AR7_CHIP_7200:
+ return "TI AR7 (TNETD7200)";
+ default:
+ return "TI AR7 (Unknown)";
+ }
+}
+
+static int __init ar7_init_console(void)
+{
+ return 0;
+}
+
+/*
+ * Initializes basic routines and structures pointers, memory size (as
+ * given by the bios and saves the command line.
+ */
+
+extern void ar7_init_clocks(void);
+
+void __init plat_mem_setup(void)
+{
+ unsigned long io_base;
+
+ _machine_restart = ar7_machine_restart;
+ _machine_halt = ar7_machine_halt;
+ pm_power_off = ar7_machine_power_off;
+ board_time_init = ar7_time_init;
+ panic_timeout = 3;
+
+ io_base = (unsigned long)ioremap(AR7_REGS_BASE, 0x10000);
+ if (!io_base) panic("Can't remap IO base!\n");
+ set_io_port_base(io_base);
+
+ prom_meminit();
+#warning FIXME: clock initialisation
+ ar7_init_clocks();
+
+ ioport_resource.start = 0;
+ ioport_resource.end = ~0;
+ iomem_resource.start = 0;
+ iomem_resource.end = ~0;
+
+ printk("%s, ID: 0x%04x, Revision: 0x%02x\n", get_system_type(),
+ ar7_chip_id(), ar7_chip_rev());
+}
+
+console_initcall(ar7_init_console);
diff -urN linux-2.6.22.1/arch/mips/ar7/time.c linux-ar7/arch/mips/ar7/time.c
--- linux-2.6.22.1/arch/mips/ar7/time.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/time.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,59 @@
+/*
+ * $Id: time.c 6600 2007-03-18 09:40:51Z ejka $
+ *
+ * Copyright (C) 2006, 2007 OpenWrt.org
+ *
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can distribute 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 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.
+ *
+ * Setting up the clock on the MIPS boards.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/mc146818rtc.h>
+
+#include <asm/mipsregs.h>
+#include <asm/ptrace.h>
+#include <asm/hardirq.h>
+#include <asm/irq.h>
+#include <asm/div64.h>
+#include <asm/cpu.h>
+#include <asm/time.h>
+#include <asm/mc146818-time.h>
+#include <asm/msc01_ic.h>
+
+#include <asm/mips-boards/generic.h>
+#include <asm/mips-boards/prom.h>
+#include <asm/mips-boards/maltaint.h>
+#include <asm/mc146818-time.h>
+#include <asm/ar7/ar7.h>
+
+void __init ar7_time_init(void)
+{
+ mips_hpt_frequency = ar7_cpu_freq() / 2;
+}
+
+void __init plat_timer_setup(struct irqaction *irq)
+{
+ setup_irq(7, irq);
+}
diff -urN linux-2.6.22.1/arch/mips/ar7/vlynq.c linux-ar7/arch/mips/ar7/vlynq.c
--- linux-2.6.22.1/arch/mips/ar7/vlynq.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/arch/mips/ar7/vlynq.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,549 @@
+/*
+ * $Id: vlynq.c 8188 2007-07-27 00:59:50Z nico $
+ *
+ * Copyright (C) 2006, 2007 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/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+#include <asm/ar7/ar7.h>
+#include <asm/ar7/vlynq.h>
+
+#define PER_DEVICE_IRQS 32
+
+#define VLYNQ_CTRL_PM_ENABLE 0x80000000
+#define VLYNQ_CTRL_CLOCK_INT 0x00008000
+#define VLYNQ_CTRL_CLOCK_DIV(x) ((x & 7) << 16)
+#define VLYNQ_CTRL_INT_LOCAL 0x00004000
+#define VLYNQ_CTRL_INT_ENABLE 0x00002000
+#define VLYNQ_CTRL_INT_VECTOR(x) ((x & 0x1f) << 8)
+#define VLYNQ_CTRL_INT2CFG 0x00000080
+#define VLYNQ_CTRL_RESET 0x00000001
+
+#define VLYNQ_STATUS_RERROR 0x00000100
+#define VLYNQ_STATUS_LERROR 0x00000080
+#define VLYNQ_STATUS_LINK 0x00000001
+
+#define VINT_ENABLE 0x00000100
+#define VINT_TYPE_EDGE 0x00000080
+#define VINT_LEVEL_LOW 0x00000040
+#define VINT_VECTOR(x) (x & 0x1f)
+#define VINT_OFFSET(irq) (8 * ((irq) % 4))
+
+#define VLYNQ_AUTONEGO_V2 0x00010000
+
+struct vlynq_regs {
+ volatile u32 revision;
+ volatile u32 control;
+ volatile u32 status;
+ volatile u32 int_prio;
+ volatile u32 int_status;
+ volatile u32 int_pending;
+ volatile u32 int_ptr;
+ volatile u32 tx_offset;
+ volatile struct vlynq_mapping rx_mapping[4];
+ volatile u32 chip;
+ volatile u32 autonego;
+ volatile u32 unused[6];
+ volatile u32 int_device[8];
+} __attribute__ ((packed));
+
+#ifdef VLYNQ_DEBUG
+static void vlynq_dump_regs(struct vlynq_device *dev)
+{
+ int i;
+ printk("VLYNQ local=%p remote=%p\n", dev->local, dev->remote);
+ for (i = 0; i < 32; i++) {
+ printk("VLYNQ: local %d: %08x\n", i + 1, ((u32 *)dev->local)[i]);
+ printk("VLYNQ: remote %d: %08x\n", i + 1, ((u32 *)dev->remote)[i]);
+ }
+}
+
+static void vlynq_dump_mem(u32 *base, int count)
+{
+ int i;
+ for (i = 0; i < (count + 3) / 4; i++) {
+ if (i % 4 == 0) printk("\nMEM[0x%04x]:", i * 4);
+ printk(" 0x%08x", *(base + i));
+ }
+ printk("\n");
+}
+#endif
+
+int vlynq_linked(struct vlynq_device *dev)
+{
+ int i;
+ for (i = 0; i < 10; i++)
+ if (dev->local->status & VLYNQ_STATUS_LINK) {
+ printk("%s: linked\n", dev->dev.bus_id);
+ return 1;
+ } else {
+ mdelay(1);
+ }
+ return 0;
+}
+
+static void vlynq_irq_unmask(unsigned int irq)
+{
+ volatile u32 val;
+ struct vlynq_device *dev = irq_desc[irq].chip_data;
+ int virq;
+
+ BUG_ON(!dev);
+ virq = irq - dev->irq_start;
+ val = dev->remote->int_device[virq >> 2];
+ val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq);
+ dev->remote->int_device[virq >> 2] = val;
+}
+
+static void vlynq_irq_mask(unsigned int irq)
+{
+ volatile u32 val;
+ struct vlynq_device *dev = irq_desc[irq].chip_data;
+ int virq;
+
+ BUG_ON(!dev);
+ virq = irq - dev->irq_start;
+ val = dev->remote->int_device[virq >> 2];
+ val &= ~(VINT_ENABLE << VINT_OFFSET(virq));
+ dev->remote->int_device[virq >> 2] = val;
+}
+
+static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
+{
+ volatile u32 val;
+ struct vlynq_device *dev = irq_desc[irq].chip_data;
+ int virq;
+
+ BUG_ON(!dev);
+ virq = irq - dev->irq_start;
+ val = dev->remote->int_device[virq >> 2];
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ val |= VINT_TYPE_EDGE << VINT_OFFSET(virq);
+ val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
+ val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
+ val |= VINT_LEVEL_LOW << VINT_OFFSET(virq);
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev->remote->int_device[virq >> 2] = val;
+ return 0;
+}
+
+static irqreturn_t vlynq_irq(int irq, void *dev_id)
+{
+ struct vlynq_device *dev = dev_id;
+ u32 status, ack;
+ int virq = 0;
+
+ status = dev->local->int_status;
+ dev->local->int_status = status;
+
+ if (status & (1 << dev->local_irq)) { /* Local vlynq IRQ. Ack */
+ ack = dev->local->status;
+ dev->local->status = ack;
+ }
+
+ if (status & (1 << dev->remote_irq)) { /* Remote vlynq IRQ. Ack */
+ ack = dev->remote->status;
+ dev->remote->status = ack;
+ }
+
+ status &= ~((1 << dev->local_irq) | (1 << dev->remote_irq));
+ while (status) {
+ if (status & 1) /* Remote device IRQ. Pass. */
+ do_IRQ(dev->irq_start + virq);
+ status >>= 1;
+ virq++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct irq_chip vlynq_irq_chip = {
+ .typename = "VLYNQ",
+ .name = "vlynq",
+ .unmask = vlynq_irq_unmask,
+ .mask = vlynq_irq_mask,
+ .set_type = vlynq_irq_type,
+};
+
+static int vlynq_setup_irq(struct vlynq_device *dev)
+{
+ u32 val;
+ int i;
+
+ if (dev->local_irq == dev->remote_irq) {
+ printk("%s: local vlynq irq should be different from remote\n",
+ dev->dev.bus_id);
+ return -EINVAL;
+ }
+
+ val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq);
+ val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
+ VLYNQ_CTRL_INT2CFG;
+ dev->local->int_ptr = 0x14;
+ dev->local->control |= val;
+
+ val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
+ val |= VLYNQ_CTRL_INT_ENABLE;
+ dev->remote->int_ptr = 0x14;
+ dev->remote->control |= val;
+
+ for (i = 0; i < PER_DEVICE_IRQS; i++) {
+ if ((i == dev->local_irq) || (i == dev->remote_irq))
+ continue;
+ irq_desc[dev->irq_start + i].status = IRQ_DISABLED;
+ irq_desc[dev->irq_start + i].action = 0;
+ irq_desc[dev->irq_start + i].depth = 1;
+ irq_desc[dev->irq_start + i].chip = &vlynq_irq_chip;
+ irq_desc[dev->irq_start + i].chip_data = dev;
+ dev->remote->int_device[i >> 2] = 0;
+ }
+
+ if (request_irq(dev->irq, vlynq_irq, SA_SHIRQ, "vlynq", dev)) {
+ printk("%s: request_irq failed\n", dev->dev.bus_id);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static void vlynq_free_irq(struct vlynq_device *dev)
+{
+ free_irq(dev->irq, dev);
+}
+
+static void vlynq_device_release(struct device *dev)
+{
+ struct vlynq_device *vdev = to_vlynq_device(dev);
+ kfree(vdev);
+}
+
+static int vlynq_device_probe(struct device *dev)
+{
+ struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
+ if (drv->probe)
+ return drv->probe(to_vlynq_device(dev));
+ return 0;
+}
+
+static int vlynq_device_remove(struct device *dev)
+{
+ struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
+ if (drv->remove)
+ return drv->remove(to_vlynq_device(dev));
+ return 0;
+}
+
+int __vlynq_register_driver(struct vlynq_driver *driver, struct module
*owner)
+{
+ driver->driver.name = driver->name;
+ driver->driver.bus = &vlynq_bus_type;
+/* driver->driver.owner = owner;*/
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL(__vlynq_register_driver);
+
+void vlynq_unregister_driver(struct vlynq_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(vlynq_unregister_driver);
+
+int vlynq_device_enable(struct vlynq_device *dev)
+{
+ u32 val;
+ u32 div;
+ int result;
+ struct plat_vlynq_ops *ops = dev->dev.platform_data;
+
+ result = ops->on(dev);
+ if (result)
+ return result;
+
+ dev->local->control = 0;
+ dev->remote->control = 0;
+
+ printk("ar7_dsp_freq() = %d\n", ar7_dsp_freq());
+ if (vlynq_linked(dev))
+ return vlynq_setup_irq(dev);
+
+ for (val = 0; val < 8; val++) {
+ dev->local->control = VLYNQ_CTRL_CLOCK_DIV(val) |
+ VLYNQ_CTRL_CLOCK_INT;
+ if (vlynq_linked(dev))
+ return vlynq_setup_irq(dev);
+ }
+
+ return -ENODEV;
+}
+
+void vlynq_device_disable(struct vlynq_device *dev)
+{
+ struct plat_vlynq_ops *ops = dev->dev.platform_data;
+
+ vlynq_free_irq(dev);
+ ops->off(dev);
+}
+
+u32 vlynq_local_id(struct vlynq_device *dev)
+{
+ return dev->local->chip;
+}
+
+u32 vlynq_remote_id(struct vlynq_device *dev)
+{
+ return dev->remote->chip;
+}
+
+void vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset,
+ struct vlynq_mapping *mapping)
+{
+ int i;
+
+ dev->local->tx_offset = tx_offset;
+ for (i = 0; i < 4; i++) {
+ dev->local->rx_mapping[i].offset = mapping[i].offset;
+ dev->local->rx_mapping[i].size = mapping[i].size;
+ }
+}
+
+void vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset,
+ struct vlynq_mapping *mapping)
+{
+ int i;
+
+ dev->remote->tx_offset = tx_offset;
+ for (i = 0; i < 4; i++) {
+ dev->remote->rx_mapping[i].offset = mapping[i].offset;
+ dev->remote->rx_mapping[i].size = mapping[i].size;
+ }
+}
+
+int vlynq_virq_to_irq(struct vlynq_device *dev, int virq)
+{
+ if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ if ((virq == dev->local_irq) || (virq == dev->remote_irq))
+ return -EINVAL;
+
+ return dev->irq_start + virq;
+}
+
+int vlynq_irq_to_virq(struct vlynq_device *dev, int irq)
+{
+ if ((irq < dev->irq_start) || (irq >= dev->irq_start + PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ return irq - dev->irq_start;
+}
+
+int vlynq_set_local_irq(struct vlynq_device *dev, int virq)
+{
+ if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ if (virq == dev->remote_irq)
+ return -EINVAL;
+
+ dev->local_irq = virq;
+
+ return 0;
+}
+
+int vlynq_set_remote_irq(struct vlynq_device *dev, int virq)
+{
+ if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
+ return -EINVAL;
+
+ if (virq == dev->local_irq)
+ return -EINVAL;
+
+ dev->remote_irq = virq;
+
+ return 0;
+}
+
+static int vlynq_probe(struct platform_device *pdev)
+{
+ struct vlynq_device *dev;
+ struct resource *regs_res, *mem_res, *irq_res;
+ int len, result;
+
+ if (strcmp(pdev->name, "vlynq"))
+ return -ENODEV;
+
+ regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!regs_res)
+ return -ENODEV;
+
+ mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
+ if (!mem_res)
+ return -ENODEV;
+
+ irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq");
+ if (!irq_res)
+ return -ENODEV;
+
+ dev = kmalloc(sizeof(struct vlynq_device), GFP_KERNEL);
+ if (!dev) {
+ printk(KERN_ERR "vlynq: failed to allocate device structure\n");
+ return -ENOMEM;
+ }
+
+ memset(dev, 0, sizeof(struct vlynq_device));
+
+ dev->id = pdev->id;
+ dev->dev.bus = &vlynq_bus_type;
+ dev->dev.parent = &pdev->dev;
+ snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id);
+ dev->dev.bus_id[BUS_ID_SIZE - 1] = 0;
+ dev->dev.platform_data = pdev->dev.platform_data;
+ dev->dev.release = vlynq_device_release;
+
+ dev->regs_start = regs_res->start;
+ dev->regs_end = regs_res->end;
+ dev->mem_start = mem_res->start;
+ dev->mem_end = mem_res->end;
+
+ len = regs_res->end - regs_res->start;
+ if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) {
+ printk("%s: Can't request vlynq registers\n", dev->dev.bus_id);
+ result = -ENXIO;
+ goto fail_request;
+ }
+
+ dev->local = ioremap_nocache(regs_res->start, len);
+ if (!dev->local) {
+ printk("%s: Can't remap vlynq registers\n", dev->dev.bus_id);
+ result = -ENXIO;
+ goto fail_remap;
+ }
+
+ dev->remote = (struct vlynq_regs *)((u32)dev->local + 128);
+
+ dev->irq = platform_get_irq_byname(pdev, "irq");
+ dev->irq_start = irq_res->start;
+ dev->irq_end = irq_res->end;
+ dev->local_irq = 31;
+ dev->remote_irq = 30;
+
+ if (device_register(&dev->dev))
+ goto fail_register;
+ platform_set_drvdata(pdev, dev);
+
+ printk("%s: regs 0x%p, irq %d, mem 0x%p\n",
+ dev->dev.bus_id, (void *)dev->regs_start, dev->irq,
+ (void *)dev->mem_start);
+
+ return 0;
+
+fail_register:
+fail_remap:
+ iounmap(dev->local);
+fail_request:
+ release_mem_region(regs_res->start, len);
+ kfree(dev);
+ return result;
+}
+
+static int vlynq_remove(struct platform_device *pdev)
+{
+ struct vlynq_device *dev = platform_get_drvdata(pdev);
+
+ device_unregister(&dev->dev);
+ release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start);
+
+ kfree(dev);
+
+ return 0;
+}
+
+static struct platform_driver vlynq_driver = {
+ .driver.name = "vlynq",
+ .probe = vlynq_probe,
+ .remove = vlynq_remove,
+};
+
+struct bus_type vlynq_bus_type = {
+ .name = "vlynq",
+ .probe = vlynq_device_probe,
+ .remove = vlynq_device_remove,
+};
+EXPORT_SYMBOL(vlynq_bus_type);
+
+#ifdef CONFIG_PCI
+extern void vlynq_pci_init(void);
+#endif
+int __init vlynq_init(void)
+{
+ int res = 0;
+
+ res = bus_register(&vlynq_bus_type);
+ if (res)
+ goto fail_bus;
+
+ res = platform_driver_register(&vlynq_driver);
+ if (res)
+ goto fail_platform;
+
+#ifdef CONFIG_PCI
+ vlynq_pci_init();
+#endif
+
+ return 0;
+
+fail_platform:
+ bus_unregister(&vlynq_bus_type);
+fail_bus:
+ return res;
+}
+
+/*
+void __devexit vlynq_exit(void)
+{
+ platform_driver_unregister(&vlynq_driver);
+ bus_unregister(&vlynq_bus_type);
+}
+*/
+
+
+subsys_initcall(vlynq_init);
diff -urN linux-2.6.22.1/arch/mips/Kconfig linux-ar7/arch/mips/Kconfig
--- linux-2.6.22.1/arch/mips/Kconfig 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/arch/mips/Kconfig 2007-08-09 16:21:48.000000000 +0200
@@ -15,6 +15,20 @@
prompt "System type"
default SGI_IP22
+config AR7
+ bool "Texas Instruments AR7"
+ select BOOT_ELF32
+ select DMA_NONCOHERENT
+ select HW_HAS_PCI
+ select IRQ_CPU
+ select SWAP_IO_SPACE
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_HAS_EARLY_PRINTK
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_KGDB
+ select SYS_SUPPORTS_LITTLE_ENDIAN
+ select GENERIC_GPIO
+
config MACH_ALCHEMY
bool "Alchemy processor based machines"
diff -urN linux-2.6.22.1/arch/mips/kernel/traps.c
linux-ar7/arch/mips/kernel/traps.c
--- linux-2.6.22.1/arch/mips/kernel/traps.c 2007-07-10 20:56:30.000000000
+0200
+++ linux-ar7/arch/mips/kernel/traps.c 2007-08-09 16:21:48.000000000 +0200
@@ -1051,11 +1051,6 @@
unsigned long exception_handlers[32];
unsigned long vi_handlers[64];
-/*
- * As a side effect of the way this is implemented we're limited
- * to interrupt handlers in the address range from
- * KSEG0 <= x < KSEG0 + 256mb on the Nevada. Oh well ...
- */
void *set_except_vector(int n, void *addr)
{
unsigned long handler = (unsigned long) addr;
@@ -1063,9 +1058,15 @@
exception_handlers[n] = handler;
if (n == 0 && cpu_has_divec) {
- *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
- (0x03ffffff & (handler >> 2));
- flush_icache_range(ebase + 0x200, ebase + 0x204);
+ /* lui k0, 0x0000 */
+ *(volatile u32 *)(CAC_BASE+0x200) = 0x3c1a0000 | (handler >> 16);
+ /* ori k0, 0x0000 */
+ *(volatile u32 *)(CAC_BASE+0x204) = 0x375a0000 | (handler & 0xffff);
+ /* jr k0 */
+ *(volatile u32 *)(CAC_BASE+0x208) = 0x03400008;
+ /* nop */
+ *(volatile u32 *)(CAC_BASE+0x20C) = 0x00000000;
+ flush_icache_range(CAC_BASE+0x200, CAC_BASE+0x210);
}
return (void *)old_handler;
}
diff -urN linux-2.6.22.1/arch/mips/Makefile linux-ar7/arch/mips/Makefile
--- linux-2.6.22.1/arch/mips/Makefile 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/arch/mips/Makefile 2007-08-09 16:21:48.000000000 +0200
@@ -158,6 +158,13 @@
#
#
+# Texas Instruments AR7
+#
+core-$(CONFIG_AR7) += arch/mips/ar7/
+cflags-$(CONFIG_AR7) += -Iinclude/asm-mips/ar7
+load-$(CONFIG_AR7) += 0xffffffff94100000
+
+#
# Acer PICA 61, Mips Magnum 4000 and Olivetti M700.
#
core-$(CONFIG_MACH_JAZZ) += arch/mips/jazz/
diff -urN linux-2.6.22.1/drivers/char/ar7_gpio.c
linux-ar7/drivers/char/ar7_gpio.c
--- linux-2.6.22.1/drivers/char/ar7_gpio.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/drivers/char/ar7_gpio.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,161 @@
+/*
+ * linux/drivers/char/ar7_gpio.c
+ *
+ * Copyright (C) 2007 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/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <gpio.h>
+
+#define DRVNAME "ar7_gpio"
+#define LONGNAME "TI AR7 GPIOs Driver"
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+
+static int ar7_gpio_major = 0;
+
+static ssize_t ar7_gpio_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ int pin = iminor(file->f_dentry->d_inode);
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ gpio_set_value(pin, 0);
+ break;
+ case '1':
+ gpio_set_value(pin, 1);
+ break;
+ case 'd':
+ case 'D':
+ ar7_gpio_disable(pin);
+ break;
+ case 'e':
+ case 'E':
+ ar7_gpio_enable(pin);
+ break;
+ case 'i':
+ case 'I':
+ case '<':
+ gpio_direction_input(pin);
+ break;
+ case 'o':
+ case 'O':
+ case '>':
+ gpio_direction_output(pin);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return len;
+}
+
+static ssize_t ar7_gpio_read(struct file *file, char __user * buf,
+ size_t len, loff_t * ppos)
+{
+ int pin = iminor(file->f_dentry->d_inode);
+ int value;
+
+ value = gpio_get_value(pin);
+ if (put_user(value ? '1' : '0', buf))
+ return -EFAULT;
+
+ return 1;
+}
+
+static int ar7_gpio_open(struct inode *inode, struct file *file)
+{
+ int m = iminor(inode);
+
+ if (m >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ return nonseekable_open(inode, file);
+}
+
+static int ar7_gpio_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations ar7_gpio_fops = {
+ .owner = THIS_MODULE,
+ .write = ar7_gpio_write,
+ .read = ar7_gpio_read,
+ .open = ar7_gpio_open,
+ .release = ar7_gpio_release,
+ .llseek = no_llseek,
+};
+
+static struct platform_device *ar7_gpio_device;
+
+static int __init ar7_gpio_init(void)
+{
+ int rc;
+
+ ar7_gpio_device = platform_device_alloc(DRVNAME, -1);
+ if (!ar7_gpio_device)
+ return -ENOMEM;
+
+ rc = platform_device_add(ar7_gpio_device);
+ if (rc < 0)
+ goto out_put;
+
+ rc = register_chrdev(ar7_gpio_major, DRVNAME, &ar7_gpio_fops);
+ if (rc < 0)
+ goto out_put;
+
+ ar7_gpio_major = rc;
+
+ rc = 0;
+
+ goto out;
+
+out_put:
+ platform_device_put(ar7_gpio_device);
+out:
+ return rc;
+}
+
+static void __exit ar7_gpio_exit(void)
+{
+ unregister_chrdev(ar7_gpio_major, DRVNAME);
+ platform_device_unregister(ar7_gpio_device);
+}
+
+module_init(ar7_gpio_init);
+module_exit(ar7_gpio_exit);
diff -urN linux-2.6.22.1/drivers/char/Kconfig linux-ar7/drivers/char/Kconfig
--- linux-2.6.22.1/drivers/char/Kconfig 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/drivers/char/Kconfig 2007-08-09 16:48:22.000000000 +0200
@@ -946,6 +946,15 @@
To compile this driver as a module, choose M here: the
module will be called mwave.
+config AR7_GPIO
+ tristate "TI AR7 GPIO Support"
+ depends on AR7
+ help
+ Give userspace access to the GPIO pins on the Texas Instruments AR7
+ processors.
+
+ If compiled as a module, it will be called ar7_gpio.
+
config SCx200_GPIO
tristate "NatSemi SCx200 GPIO Support"
depends on SCx200
diff -urN linux-2.6.22.1/drivers/char/Makefile linux-ar7/drivers/char/Makefile
--- linux-2.6.22.1/drivers/char/Makefile 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/drivers/char/Makefile 2007-08-09 16:48:22.000000000 +0200
@@ -86,6 +86,7 @@
obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
+obj-$(CONFIG_AR7_GPIO) += ar7_gpio.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
diff -urN linux-2.6.22.1/drivers/leds/Kconfig linux-ar7/drivers/leds/Kconfig
--- linux-2.6.22.1/drivers/leds/Kconfig 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/drivers/leds/Kconfig 2007-08-09 16:48:37.000000000 +0200
@@ -20,6 +20,12 @@
comment "LED drivers"
+config LEDS_AR7
+ tristate "LED Support for the TI AR7"
+ depends LEDS_CLASS && AR7
+ help
+ This option enables support for the LEDs on TI AR7.
+
config LEDS_CORGI
tristate "LED Support for the Sharp SL-C7x0 series"
depends on LEDS_CLASS && PXA_SHARP_C7xx
diff -urN linux-2.6.22.1/drivers/leds/leds-ar7.c
linux-ar7/drivers/leds/leds-ar7.c
--- linux-2.6.22.1/drivers/leds/leds-ar7.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/drivers/leds/leds-ar7.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,132 @@
+/*
+ * linux/drivers/leds/leds-ar7.c
+ *
+ * Copyright (C) 2007 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <gpio.h>
+
+#define DRVNAME "ar7-leds"
+#define LONGNAME "TI AR7 LEDs driver"
+#define AR7_GPIO_BIT_STATUS_LED 8
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+
+static void ar7_status_led_set(struct led_classdev *pled,
+ enum led_brightness value)
+{
+ gpio_set_value(AR7_GPIO_BIT_STATUS_LED, value ? 0 : 1);
+}
+
+static struct led_classdev ar7_status_led = {
+ .name = "ar7:status",
+ .brightness_set = ar7_status_led_set,
+};
+
+#ifdef CONFIG_PM
+static int ar7_leds_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ led_classdev_suspend(&ar7_status_led);
+ return 0;
+}
+
+static int ar7_leds_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&ar7_status_led);
+ return 0;
+}
+#else /* CONFIG_PM */
+#define ar7_leds_suspend NULL
+#define ar7_leds_resume NULL
+#endif /* CONFIG_PM */
+
+static int ar7_leds_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ rc = led_classdev_register(&pdev->dev, &ar7_status_led);
+ if (rc < 0 )
+ goto out;
+
+ ar7_gpio_enable(AR7_GPIO_BIT_STATUS_LED);
+ gpio_direction_output(AR7_GPIO_BIT_STATUS_LED);
+
+out:
+ return rc;
+}
+
+static int ar7_leds_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&ar7_status_led);
+
+ return 0;
+}
+
+static struct platform_device *ar7_leds_device;
+
+static struct platform_driver ar7_leds_driver = {
+ .probe = ar7_leds_probe,
+ .remove = ar7_leds_remove,
+ .suspend = ar7_leds_suspend,
+ .resume = ar7_leds_resume,
+ .driver = {
+ .name = DRVNAME,
+ },
+};
+
+static int __init ar7_leds_init(void)
+{
+ int rc;
+
+ ar7_leds_device = platform_device_alloc(DRVNAME, -1);
+ if (!ar7_leds_device)
+ return -ENOMEM;
+
+ rc = platform_device_add(ar7_leds_device);
+ if (rc < 0)
+ goto out_put;
+
+ rc = platform_driver_register(&ar7_leds_driver);
+ if (rc < 0)
+ goto out_put;
+
+ goto out;
+
+out_put:
+ platform_device_put(ar7_leds_device);
+out:
+ return rc;
+}
+
+static void __exit ar7_leds_exit(void)
+{
+ platform_driver_unregister(&ar7_leds_driver);
+ platform_device_unregister(ar7_leds_device);
+}
+
+module_init(ar7_leds_init);
+module_exit(ar7_leds_exit);
diff -urN linux-2.6.22.1/drivers/leds/Makefile linux-ar7/drivers/leds/Makefile
--- linux-2.6.22.1/drivers/leds/Makefile 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/drivers/leds/Makefile 2007-08-09 16:48:37.000000000 +0200
@@ -5,6 +5,7 @@
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
+obj-$(CONFIG_LEDS_AR7) += leds-ar7.o
obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o
diff -urN linux-2.6.22.1/drivers/mtd/ar7part.c linux-ar7/drivers/mtd/ar7part.c
--- linux-2.6.22.1/drivers/mtd/ar7part.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/drivers/mtd/ar7part.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,140 @@
+/*
+ * $Id: ar7part.c 6948 2007-04-15 04:01:45Z ejka $
+ *
+ * Copyright (C) 2007 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
+ *
+ * TI AR7 flash partition table.
+ * Based on ar7 map by Felix Fietkau.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/bootmem.h>
+#include <linux/squashfs_fs.h>
+
+struct ar7_bin_rec {
+ unsigned int checksum;
+ unsigned int length;
+ unsigned int address;
+};
+
+static struct mtd_partition ar7_parts[5];
+
+static int create_mtd_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ unsigned long origin)
+{
+ struct ar7_bin_rec header;
+ unsigned int offset, len;
+ unsigned int pre_size = master->erasesize, post_size = 0,
+ root_offset = 0xe0000;
+ int retries = 10;
+
+ printk("Parsing AR7 partition map...\n");
+
+ ar7_parts[0].name = "loader";
+ ar7_parts[0].offset = 0;
+ ar7_parts[0].size = master->erasesize;
+ ar7_parts[0].mask_flags = MTD_WRITEABLE;
+
+ ar7_parts[1].name = "config";
+ ar7_parts[1].offset = 0;
+ ar7_parts[1].size = master->erasesize;
+ ar7_parts[1].mask_flags = 0;
+
+ do {
+ offset = pre_size;
+ master->read(master, offset, sizeof(header), &len, (u_char *)&header);
+ if (!strncmp((char *)&header, "TIENV0.8", 8))
+ ar7_parts[1].offset = pre_size;
+ if (header.checksum == 0xfeedfa42)
+ break;
+ if (header.checksum == 0xfeed1281)
+ break;
+ pre_size += master->erasesize;
+ } while (retries--);
+
+ pre_size = offset;
+
+ if (!ar7_parts[1].offset) {
+ ar7_parts[1].offset = master->size - master->erasesize;
+ post_size = master->erasesize;
+ }
+
+ switch (header.checksum) {
+ case 0xfeedfa42:
+ while (header.length) {
+ offset += sizeof(header) + header.length;
+ master->read(master, offset, sizeof(header),
+ &len, (u_char *)&header);
+ }
+ root_offset = offset + sizeof(header) + 4;
+ break;
+ case 0xfeed1281:
+ while (header.length) {
+ offset += sizeof(header) + header.length;
+ master->read(master, offset, sizeof(header),
+ &len, (u_char *)&header);
+ }
+ root_offset = offset + sizeof(header) + 4 + 0xff;
+ root_offset &= ~(u32)0xff;
+ break;
+ default:
+ printk("Unknown magic: %08x\n", header.checksum);
+ break;
+ }
+
+ master->read(master, root_offset, sizeof(header), &len, (u_char *)&header);
+ if (header.checksum != SQUASHFS_MAGIC) {
+ root_offset += master->erasesize - 1;
+ root_offset &= ~(master->erasesize - 1);
+ }
+
+ ar7_parts[2].name = "linux";
+ ar7_parts[2].offset = pre_size;
+ ar7_parts[2].size = master->size - pre_size - post_size;
+ ar7_parts[2].mask_flags = 0;
+
+ ar7_parts[3].name = "rootfs";
+ ar7_parts[3].offset = root_offset;
+ ar7_parts[3].size = master->size - root_offset - post_size;
+ ar7_parts[3].mask_flags = 0;
+
+ *pparts = ar7_parts;
+ return 4;
+}
+
+static struct mtd_part_parser ar7_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = create_mtd_partitions,
+ .name = "ar7part",
+};
+
+static int __init ar7_parser_init(void)
+{
+ return register_mtd_parser(&ar7_parser);
+}
+
+module_init(ar7_parser_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Felix Fietkau, Eugene Konev");
+MODULE_DESCRIPTION("MTD partitioning for TI AR7");
diff -urN linux-2.6.22.1/drivers/mtd/Kconfig linux-ar7/drivers/mtd/Kconfig
--- linux-2.6.22.1/drivers/mtd/Kconfig 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/drivers/mtd/Kconfig 2007-08-09 16:48:08.000000000 +0200
@@ -150,6 +150,12 @@
for your particular device. It won't happen automatically. The
'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
+config MTD_AR7_PARTS
+ tristate "TI AR7 partitioning support"
+ depends on MTD_PARTITIONS
+ ---help---
+ TI AR7 partitioning support
+
comment "User Modules And Translation Layers"
config MTD_CHAR
diff -urN linux-2.6.22.1/drivers/mtd/Makefile linux-ar7/drivers/mtd/Makefile
--- linux-2.6.22.1/drivers/mtd/Makefile 2007-07-10 20:56:30.000000000 +0200
+++ linux-ar7/drivers/mtd/Makefile 2007-08-09 16:48:08.000000000 +0200
@@ -11,6 +11,7 @@
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
diff -urN linux-2.6.22.1/drivers/mtd/maps/physmap.c
linux-ar7/drivers/mtd/maps/physmap.c
--- linux-2.6.22.1/drivers/mtd/maps/physmap.c 2007-07-10 20:56:30.000000000
+0200
+++ linux-ar7/drivers/mtd/maps/physmap.c 2007-08-09 16:48:08.000000000 +0200
@@ -74,7 +74,7 @@
static const char *rom_probe_types[] =
{ "cfi_probe", "jedec_probe", "map_rom", NULL };
#ifdef CONFIG_MTD_PARTITIONS
-static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
+static const char *part_probe_types[] =
{ "cmdlinepart", "RedBoot", "ar7part", NULL };
#endif
static int physmap_flash_probe(struct platform_device *dev)
diff -urN linux-2.6.22.1/drivers/net/cpmac.c linux-ar7/drivers/net/cpmac.c
--- linux-2.6.22.1/drivers/net/cpmac.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-ar7/drivers/net/cpmac.c 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,1222 @@
+/*
+ * $Id: cpmac.c 8364 2007-08-07 07:37:47Z florian $
+ *
+ * Copyright (C) 2006, 2007 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 <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <asm/ar7/ar7.h>
+#include <gpio.h>
+
+MODULE_AUTHOR("Eugene Konev");
+MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
+MODULE_LICENSE("GPL");
+
+static int rx_ring_size = 64;
+static int disable_napi = 0;
+module_param(rx_ring_size, int, 64);
+module_param(disable_napi, int, 0);
+MODULE_PARM_DESC(rx_ring_size, "Size of rx ring (in skbs)");
+MODULE_PARM_DESC(disable_napi, "Disable NAPI polling");
+
+/* Register definitions */
+struct cpmac_control_regs {
+ volatile u32 revision;
+ volatile u32 control;
+ volatile u32 teardown;
+ volatile u32 unused;
+} __attribute__ ((packed));
+
+struct cpmac_int_regs {
+ volatile u32 stat_raw;
+ volatile u32 stat_masked;
+ volatile u32 enable;
+ volatile u32 clear;
+} __attribute__ ((packed));
+
+struct cpmac_stats {
+ volatile u32 good;
+ volatile u32 bcast;
+ volatile u32 mcast;
+ volatile u32 pause;
+ volatile u32 crc_error;
+ volatile u32 align_error;
+ volatile u32 oversized;
+ volatile u32 jabber;
+ volatile u32 undersized;
+ volatile u32 fragment;
+ volatile u32 filtered;
+ volatile u32 qos_filtered;
+ volatile u32 octets;
+} __attribute__ ((packed));
+
+struct cpmac_regs {
+ struct cpmac_control_regs tx_ctrl;
+ struct cpmac_control_regs rx_ctrl;
+ volatile u32 unused1[56];
+ volatile u32 mbp;
+/* MBP bits */
+#define MBP_RXPASSCRC 0x40000000
+#define MBP_RXQOS 0x20000000
+#define MBP_RXNOCHAIN 0x10000000
+#define MBP_RXCMF 0x01000000
+#define MBP_RXSHORT 0x00800000
+#define MBP_RXCEF 0x00400000
+#define MBP_RXPROMISC 0x00200000
+#define MBP_PROMISCCHAN(chan) (((chan) & 0x7) << 16)
+#define MBP_RXBCAST 0x00002000
+#define MBP_BCASTCHAN(chan) (((chan) & 0x7) << 8)
+#define MBP_RXMCAST 0x00000020
+#define MBP_MCASTCHAN(chan) ((chan) & 0x7)
+ volatile u32 unicast_enable;
+ volatile u32 unicast_clear;
+ volatile u32 max_len;
+ volatile u32 buffer_offset;
+ volatile u32 filter_flow_threshold;
+ volatile u32 unused2[2];
+ volatile u32 flow_thre[8];
+ volatile u32 free_buffer[8];
+ volatile u32 mac_control;
+#define MAC_TXPTYPE 0x00000200
+#define MAC_TXPACE 0x00000040
+#define MAC_MII 0x00000020
+#define MAC_TXFLOW 0x00000010
+#define MAC_RXFLOW 0x00000008
+#define MAC_MTEST 0x00000004
+#define MAC_LOOPBACK 0x00000002
+#define MAC_FDX 0x00000001
+ volatile u32 mac_status;
+#define MACST_QOS 0x4
+#define MACST_RXFLOW 0x2
+#define MACST_TXFLOW 0x1
+ volatile u32 emc_control;
+ volatile u32 unused3;
+ struct cpmac_int_regs tx_int;
+ volatile u32 mac_int_vector;
+/* Int Status bits */
+#define INTST_STATUS 0x80000
+#define INTST_HOST 0x40000
+#define INTST_RX 0x20000
+#define INTST_TX 0x10000
+ volatile u32 mac_eoi_vector;
+ volatile u32 unused4[2];
+ struct cpmac_int_regs rx_int;
+ volatile u32 mac_int_stat_raw;
+ volatile u32 mac_int_stat_masked;
+ volatile u32 mac_int_enable;
+ volatile u32 mac_int_clear;
+ volatile u32 mac_addr_low[8];
+ volatile u32 mac_addr_mid;
+ volatile u32 mac_addr_high;
+ volatile u32 mac_hash_low;
+ volatile u32 mac_hash_high;
+ volatile u32 boff_test;
+ volatile u32 pac_test;
+ volatile u32 rx_pause;
+ volatile u32 tx_pause;
+ volatile u32 unused5[2];
+ struct cpmac_stats rx_stats;
+ struct cpmac_stats tx_stats;
+ volatile u32 unused6[232];
+ volatile u32 tx_ptr[8];
+ volatile u32 rx_ptr[8];
+ volatile u32 tx_ack[8];
+ volatile u32 rx_ack[8];
+
+} __attribute__ ((packed));
+
+struct cpmac_mdio_regs {
+ volatile u32 version;
+ volatile u32 control;
+#define MDIOC_IDLE 0x80000000
+#define MDIOC_ENABLE 0x40000000
+#define MDIOC_PREAMBLE 0x00100000
+#define MDIOC_FAULT 0x00080000
+#define MDIOC_FAULTDETECT 0x00040000
+#define MDIOC_INTTEST 0x00020000
+#define MDIOC_CLKDIV(div) ((div) & 0xff)
+ volatile u32 alive;
+ volatile u32 link;
+ struct cpmac_int_regs link_int;
+ struct cpmac_int_regs user_int;
+ u32 unused[20];
+ volatile u32 access;
+#define MDIO_BUSY 0x80000000
+#define MDIO_WRITE 0x40000000
+#define MDIO_REG(reg) (((reg) & 0x1f) << 21)
+#define MDIO_PHY(phy) (((phy) & 0x1f) << 16)
+#define MDIO_DATA(data) ((data) & 0xffff)
+ volatile u32 physel;
+} __attribute__ ((packed));
+
+/* Descriptor */
+struct cpmac_desc {
+ u32 hw_next;
+ u32 hw_data;
+ u16 buflen;
+ u16 bufflags;
+ u16 datalen;
+ u16 dataflags;
+/* Flags bits */
+#define CPMAC_SOP 0x8000
+#define CPMAC_EOP 0x4000
+#define CPMAC_OWN 0x2000
+#define CPMAC_EOQ 0x1000
+ struct sk_buff *skb;
+ struct cpmac_desc *next;
+} __attribute__ ((packed));
+
+struct cpmac_priv {
+ struct net_device_stats stats;
+ spinlock_t lock;
+ struct sk_buff *skb_pool;
+ int free_skbs;
+ struct cpmac_desc *rx_head;
+ int tx_head, tx_tail;
+ struct cpmac_desc *desc_ring;
+ struct cpmac_regs *regs;
+ struct mii_bus *mii_bus;
+ struct phy_device *phy;
+ char phy_name[BUS_ID_SIZE];
+ struct plat_cpmac_data *config;
+ int oldlink, oldspeed, oldduplex;
+ u32 msg_enable;
+ struct net_device *dev;
+ struct work_struct alloc_work;
+};
+
+static irqreturn_t cpmac_irq(int, void *);
+static void cpmac_reset(struct net_device *dev);
+static void cpmac_hw_init(struct net_device *dev);
+static int cpmac_stop(struct net_device *dev);
+static int cpmac_open(struct net_device *dev);
+
+#undef CPMAC_DEBUG
+#define CPMAC_LOW_THRESH 32
+#define CPMAC_ALLOC_SIZE 64
+#define CPMAC_SKB_SIZE 1518
+#define CPMAC_TX_RING_SIZE 8
+
+#ifdef CPMAC_DEBUG
+static void cpmac_dump_regs(u32 *base, int count)
+{
+ int i;
+ for (i = 0; i < (count + 3) / 4; i++) {
+ if (i % 4 == 0) printk("\nCPMAC[0x%04x]:", i * 4);
+ printk(" 0x%08x", *(base + i));
+ }
+ printk("\n");
+}
+
+static const char *cpmac_dump_buf(const uint8_t * buf, unsigned size)
+{
+ static char buffer[3 * 25 + 1];
+ char *p = &buffer[0];
+ if (size > 20)
+ size = 20;
+ while (size-- > 0) {
+ p += sprintf(p, " %02x", *buf++);
+ }
+ return buffer;
+}
+#endif
+
+static int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct cpmac_mdio_regs *regs = (struct cpmac_mdio_regs *)bus->priv;
+ volatile u32 val;
+
+ while ((val = regs->access) & MDIO_BUSY);
+ regs->access = MDIO_BUSY | MDIO_REG(regnum & 0x1f) |
+ MDIO_PHY(phy_id & 0x1f);
+ while ((val = regs->access) & MDIO_BUSY);
+
+ return val & 0xffff;
+}
+
+static int cpmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16
val)
+{
+ struct cpmac_mdio_regs *regs = (struct cpmac_mdio_regs *)bus->priv;
+ volatile u32 tmp;
+
+ while ((tmp = regs->access) & MDIO_BUSY);
+ regs->access = MDIO_BUSY | MDIO_WRITE |
+ MDIO_REG(regnum & 0x1f) | MDIO_PHY(phy_id & 0x1f) |
+ val;
+
+ return 0;
+}
+
+static int cpmac_mdio_reset(struct mii_bus *bus)
+{
+ struct cpmac_mdio_regs *regs = (struct cpmac_mdio_regs *)bus->priv;
+
+ ar7_device_reset(AR7_RESET_BIT_MDIO);
+ regs->control = MDIOC_ENABLE |
+ MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1);
+
+ return 0;
+}
+
+static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
+
+static struct mii_bus cpmac_mii = {
+ .name = "cpmac-mii",
+ .read = cpmac_mdio_read,
+ .write = cpmac_mdio_write,
+ .reset = cpmac_mdio_reset,
+ .irq = mii_irqs,
+};
+
+static int cpmac_config(struct net_device *dev, struct ifmap *map)
+{
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ /* Don't allow changing the I/O address */
+ if (map->base_addr != dev->base_addr)
+ return -EOPNOTSUPP;
+
+ /* ignore other fields */
+ return 0;
+}
+
+static int cpmac_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+ return 0;
+}
+
+static void cpmac_set_multicast_list(struct net_device *dev)
+{
+ struct dev_mc_list *iter;
+ int i;
+ int hash, tmp;
+ int hashlo = 0, hashhi = 0;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if(dev->flags & IFF_PROMISC) {
+ priv->regs->mbp &= ~MBP_PROMISCCHAN(0); /* promisc channel 0 */
+ priv->regs->mbp |= MBP_RXPROMISC;
+ } else {
+ priv->regs->mbp &= ~MBP_RXPROMISC;
+ if(dev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ priv->regs->mac_hash_low = 0xffffffff;
+ priv->regs->mac_hash_high = 0xffffffff;
+ } else {
+ for(i = 0, iter = dev->mc_list; i < dev->mc_count;
+ i++, iter = iter->next) {
+ hash = 0;
+ tmp = iter->dmi_addr[0];
+ hash ^= (tmp >> 2) ^ (tmp << 4);
+ tmp = iter->dmi_addr[1];
+ hash ^= (tmp >> 4) ^ (tmp << 2);
+ tmp = iter->dmi_addr[2];
+ hash ^= (tmp >> 6) ^ tmp;
+ tmp = iter->dmi_addr[4];
+ hash ^= (tmp >> 2) ^ (tmp << 4);
+ tmp = iter->dmi_addr[5];
+ hash ^= (tmp >> 4) ^ (tmp << 2);
+ tmp = iter->dmi_addr[6];
+ hash ^= (tmp >> 6) ^ tmp;
+ hash &= 0x3f;
+ if(hash < 32) {
+ hashlo |= 1<<hash;
+ } else {
+ hashhi |= 1<<(hash - 32);
+ }
+ }
+
+ priv->regs->mac_hash_low = hashlo;
+ priv->regs->mac_hash_high = hashhi;
+ }
+ }
+}
+
+static struct sk_buff *cpmac_get_skb(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ skb = priv->skb_pool;
+ if (likely(skb)) {
+ priv->skb_pool = skb->next;
+ } else {
+ skb = dev_alloc_skb(CPMAC_SKB_SIZE + 2);
+ if (skb) {
+ skb->next = NULL;
+ skb_reserve(skb, 2);
+ skb->dev = priv->dev;
+ }
+ }
+
+ if (likely(priv->free_skbs))
+ priv->free_skbs--;
+
+ if (priv->free_skbs < CPMAC_LOW_THRESH)
+ schedule_work(&priv->alloc_work);
+
+ return skb;
+}
+
+static inline struct sk_buff *cpmac_rx_one(struct net_device *dev,
+ struct cpmac_priv *priv,
+ struct cpmac_desc *desc)
+{
+ unsigned long flags;
+ char *data;
+ struct sk_buff *skb, *result = NULL;
+
+ priv->regs->rx_ack[0] = virt_to_phys(desc);
+ if (unlikely(!desc->datalen)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING "%s: rx: spurious interrupt\n",
+ dev->name);
+ priv->stats.rx_errors++;
+ return NULL;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ skb = cpmac_get_skb(dev);
+ if (likely(skb)) {
+ data = (char *)phys_to_virt(desc->hw_data);
+ dma_cache_inv((u32)data, desc->datalen);
+ skb_put(desc->skb, desc->datalen);
+ desc->skb->protocol = eth_type_trans(desc->skb, dev);
+ desc->skb->ip_summed = CHECKSUM_NONE;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += desc->datalen;
+ result = desc->skb;
+ desc->skb = skb;
+ } else {
+#ifdef CPMAC_DEBUG
+ if (printk_ratelimit())
+ printk("%s: low on skbs, dropping packet\n",
+ dev->name);
+#endif
+ priv->stats.rx_dropped++;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ desc->hw_data = virt_to_phys(desc->skb->data);
+ desc->buflen = CPMAC_SKB_SIZE;
+ desc->dataflags = CPMAC_OWN;
+ dma_cache_wback((u32)desc, 16);
+
+ return result;
+}
+
+static void cpmac_rx(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct cpmac_desc *desc;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+ if (unlikely(!priv->rx_head)) {
+ spin_unlock(&priv->lock);
+ return;
+ }
+
+ desc = priv->rx_head;
+ dma_cache_inv((u32)desc, 16);
+#ifdef CPMAC_DEBUG
+ printk(KERN_DEBUG "%s: len=%d, %s\n", __func__, pkt->datalen,
+ cpmac_dump_buf(data, pkt->datalen));
+#endif
+
+ while ((desc->dataflags & CPMAC_OWN) == 0) {
+ skb = cpmac_rx_one(dev, priv, desc);
+ if (likely(skb)) {
+ netif_rx(skb);
+ }
+ desc = desc->next;
+ dma_cache_inv((u32)desc, 16);
+ }
+
+ priv->rx_head = desc;
+ priv->regs->rx_ptr[0] = virt_to_phys(desc);
+ spin_unlock(&priv->lock);
+}
+
+static int cpmac_poll(struct net_device *dev, int *budget)
+{
+ struct sk_buff *skb;
+ struct cpmac_desc *desc;
+ int received = 0, quota = min(dev->quota, *budget);
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (unlikely(!priv->rx_head)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING "%s: rx: polling, but no queue\n",
+ dev->name);
+ netif_rx_complete(dev);
+ return 0;
+ }
+
+ desc = priv->rx_head;
+ dma_cache_inv((u32)desc, 16);
+
+ while ((received < quota) && ((desc->dataflags & CPMAC_OWN) == 0)) {
+ skb = cpmac_rx_one(dev, priv, desc);
+ if (likely(skb)) {
+ netif_receive_skb(skb);
+ received++;
+ }
+ desc = desc->next;
+ priv->rx_head = desc;
+ dma_cache_inv((u32)desc, 16);
+ }
+
+ *budget -= received;
+ dev->quota -= received;
+#ifdef CPMAC_DEBUG
+ printk("%s: processed %d packets\n", dev->name, received);
+#endif
+ if (desc->dataflags & CPMAC_OWN) {
+ priv->regs->rx_ptr[0] = virt_to_phys(desc);
+ netif_rx_complete(dev);
+ priv->regs->rx_int.enable = 0x1;
+ priv->regs->rx_int.clear = 0xfe;
+ return 0;
+ }
+
+ return 1;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+static void
+cpmac_alloc_skbs(struct work_struct *work)
+{
+ struct cpmac_priv *priv = container_of(work, struct cpmac_priv,
+ alloc_work);
+#else
+static void
+cpmac_alloc_skbs(void *data)
+{
+ struct net_device *dev = (struct net_device*)data;
+ struct cpmac_priv *priv = netdev_priv(dev);
+#endif
+ unsigned long flags;
+ int i, num_skbs = 0;
+ struct sk_buff *skb, *skbs = NULL;
+
+ for (i = 0; i < CPMAC_ALLOC_SIZE; i++) {
+ skb = alloc_skb(CPMAC_SKB_SIZE + 2, GFP_KERNEL);
+ if (!skb)
+ break;
+ skb->next = skbs;
+ skb_reserve(skb, 2);
+ skb->dev = priv->dev;
+ num_skbs++;
+ skbs = skb;
+ }
+
+ if (skbs) {
+ spin_lock_irqsave(&priv->lock, flags);
+ for (skb = priv->skb_pool; skb && skb->next; skb = skb->next);
+ if (!skb) {
+ priv->skb_pool = skbs;
+ } else {
+ skb->next = skbs;
+ }
+ priv->free_skbs += num_skbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+#ifdef CPMAC_DEBUG
+ printk("%s: allocated %d skbs\n", priv->dev->name, num_skbs);
+#endif
+ }
+}
+
+static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned long flags;
+ int len, chan;
+ struct cpmac_desc *desc;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ len = skb->len;
+#ifdef CPMAC_DEBUG
+ printk(KERN_DEBUG "%s: len=%d\n", __func__,
len); //cpmac_dump_buf(const uint8_t * buf, unsigned size)
+#endif
+ if (unlikely(len < ETH_ZLEN)) {
+ if (unlikely(skb_padto(skb, ETH_ZLEN))) {
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "%s: padding failed, dropping\n",
+ dev->name);
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+ len = ETH_ZLEN;
+ }
+ spin_lock_irqsave(&priv->lock, flags);
+ chan = priv->tx_tail++;
+ priv->tx_tail %= 8;
+ if (priv->tx_tail == priv->tx_head)
+ netif_stop_queue(dev);
+
+ desc = &priv->desc_ring[chan];
+ dma_cache_inv((u32)desc, 16);
+ if (desc->dataflags & CPMAC_OWN) {
+ printk(KERN_NOTICE "%s: tx dma ring full, dropping\n", dev->name);
+ priv->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+
+ dev->trans_start = jiffies;
+ desc->dataflags = CPMAC_SOP | CPMAC_EOP | CPMAC_OWN;
+ desc->skb = skb;
+ desc->hw_data = virt_to_phys(skb->data);
+ dma_cache_wback((u32)skb->data, len);
+ desc->buflen = len;
+ desc->datalen = len;
+ desc->hw_next = 0;
+ dma_cache_wback((u32)desc, 16);
+ priv->regs->tx_ptr[chan] = virt_to_phys(desc);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void cpmac_end_xmit(struct net_device *dev, int channel)
+{
+ struct cpmac_desc *desc;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+ desc = &priv->desc_ring[channel];
+ priv->regs->tx_ack[channel] = virt_to_phys(desc);
+ if (likely(desc->skb)) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += desc->skb->len;
+ dev_kfree_skb_irq(desc->skb);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ } else {
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "%s: end_xmit: spurious interrupt\n",
+ dev->name);
+ }
+ spin_unlock(&priv->lock);
+}
+
+static void cpmac_reset(struct net_device *dev)
+{
+ int i;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ ar7_device_reset(priv->config->reset_bit);
+ priv->regs->rx_ctrl.control &= ~1;
+ priv->regs->tx_ctrl.control &= ~1;
+ for (i = 0; i < 8; i++) {
+ priv->regs->tx_ptr[i] = 0;
+ priv->regs->rx_ptr[i] = 0;
+ }
+ priv->regs->mac_control &= ~MAC_MII; /* disable mii */
+}
+
+static inline void cpmac_free_rx_ring(struct net_device *dev)
+{
+ struct cpmac_desc *desc;
+ int i;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (unlikely(!priv->rx_head))
+ return;
+
+ desc = priv->rx_head;
+ dma_cache_inv((u32)desc, 16);
+
+ for (i = 0; i < rx_ring_size; i++) {
+ desc->buflen = CPMAC_SKB_SIZE;
+ if ((desc->dataflags & CPMAC_OWN) == 0) {
+ desc->dataflags = CPMAC_OWN;
+ priv->stats.rx_dropped++;
+ }
+ dma_cache_wback((u32)desc, 16);
+ desc = desc->next;
+ dma_cache_inv((u32)desc, 16);
+ }
+}
+
+static irqreturn_t cpmac_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct cpmac_priv *priv = netdev_priv(dev);
+ u32 status;
+
+ if (!dev)
+ return IRQ_NONE;
+
+ status = priv->regs->mac_int_vector;
+
+ if (status & INTST_TX) {
+ cpmac_end_xmit(dev, (status & 7));
+ }
+
+ if (status & INTST_RX) {
+ if (disable_napi) {
+ cpmac_rx(dev);
+ } else {
+ priv->regs->rx_int.enable = 0;
+ priv->regs->rx_int.clear = 0xff;
+ netif_rx_schedule(dev);
+ }
+ }
+
+ priv->regs->mac_eoi_vector = 0;
+
+ if (unlikely(status & (INTST_HOST | INTST_STATUS))) {
+ if (printk_ratelimit()) {
+ printk(KERN_ERR "%s: hw error, resetting...\n", dev->name);
+ }
+ spin_lock(&priv->lock);
+ phy_stop(priv->phy);
+ cpmac_reset(dev);
+ cpmac_free_rx_ring(dev);
+ cpmac_hw_init(dev);
+ spin_unlock(&priv->lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void cpmac_tx_timeout(struct net_device *dev)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+ struct cpmac_desc *desc;
+
+ priv->stats.tx_errors++;
+ desc = &priv->desc_ring[priv->tx_head++];
+ priv->tx_head %= 8;
+ printk("%s: transmit timeout\n", dev->name);
+ if (desc->skb)
+ dev_kfree_skb(desc->skb);
+ netif_wake_queue(dev);
+}
+
+static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+ if (!(netif_running(dev)))
+ return -EINVAL;
+ if (!priv->phy)
+ return -EINVAL;
+ if ((cmd == SIOCGMIIPHY) || (cmd == SIOCGMIIREG) ||
+ (cmd == SIOCSMIIREG))
+ return phy_mii_ioctl(priv->phy, if_mii(ifr), cmd);
+
+ return -EINVAL;
+}
+
+static int cpmac_get_settings(struct net_device *dev, struct ethtool_cmd
*cmd)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (priv->phy)
+ return phy_ethtool_gset(priv->phy, cmd);
+
+ return -EINVAL;
+}
+
+static int cpmac_set_settings(struct net_device *dev, struct ethtool_cmd
*cmd)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (priv->phy)
+ return phy_ethtool_sset(priv->phy, cmd);
+
+ return -EINVAL;
+}
+
+static void cpmac_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, "cpmac");
+ strcpy(info->version, "0.0.3");
+ info->fw_version[0] = '\0';
+ sprintf(info->bus_info, "%s", "cpmac");
+ info->regdump_len = 0;
+}
+
+static const struct ethtool_ops cpmac_ethtool_ops = {
+ .get_settings = cpmac_get_settings,
+ .set_settings = cpmac_set_settings,
+ .get_drvinfo = cpmac_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+static struct net_device_stats *cpmac_stats(struct net_device *dev)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ if (netif_device_present(dev))
+ return &priv->stats;
+
+ return NULL;
+}
+
+static int cpmac_change_mtu(struct net_device *dev, int mtu)
+{
+ unsigned long flags;
+ struct cpmac_priv *priv = netdev_priv(dev);
+ spinlock_t *lock = &priv->lock;
+
+ if ((mtu < 68) || (mtu > 1500))
+ return -EINVAL;
+
+ spin_lock_irqsave(lock, flags);
+ dev->mtu = mtu;
+ spin_unlock_irqrestore(lock, flags);
+
+ return 0;
+}
+
+static void cpmac_adjust_link(struct net_device *dev)
+{
+ struct cpmac_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ int new_state = 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->phy->link) {
+ if (priv->phy->duplex != priv->oldduplex) {
+ new_state = 1;
+ priv->oldduplex = priv->phy->duplex;
+ }
+
+ if (priv->phy->speed != priv->oldspeed) {
+ new_state = 1;
+ priv->oldspeed = priv->phy->speed;
+ }
+
+ if (!priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 1;
+ netif_schedule(dev);
+ }
+ } else if (priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+ }
+
+ if (new_state)
+ phy_print_status(priv->phy);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void cpmac_hw_init(struct net_device *dev)
+{
+ int i;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ for (i = 0; i < 8; i++)
+ priv->regs->tx_ptr[i] = 0;
+ priv->regs->rx_ptr[0] = virt_to_phys(priv->rx_head);
+
+ priv->regs->mbp = MBP_RXSHORT | MBP_RXBCAST | MBP_RXMCAST;
+ priv->regs->unicast_enable = 0x1;
+ priv->regs->unicast_clear = 0xfe;
+ priv->regs->buffer_offset = 0;
+ for (i = 0; i < 8; i++)
+ priv->regs->mac_addr_low[i] = dev->dev_addr[5];
+ priv->regs->mac_addr_mid = dev->dev_addr[4];
+ priv->regs->mac_addr_high = dev->dev_addr[0] | (dev->dev_addr[1] << 8)
+ | (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+ priv->regs->max_len = CPMAC_SKB_SIZE;
+ priv->regs->rx_int.enable = 0x1;
+ priv->regs->rx_int.clear = 0xfe;
+ priv->regs->tx_int.enable = 0xff;
+ priv->regs->tx_int.clear = 0;
+ priv->regs->mac_int_enable = 3;
+ priv->regs->mac_int_clear = 0xfc;
+
+ priv->regs->rx_ctrl.control |= 1;
+ priv->regs->tx_ctrl.control |= 1;
+ priv->regs->mac_control |= MAC_MII | MAC_FDX;
+
+ priv->phy->state = PHY_CHANGELINK;
+ phy_start(priv->phy);
+}
+
+static int cpmac_open(struct net_device *dev)
+{
+ int i, size, res;
+ struct cpmac_priv *priv = netdev_priv(dev);
+ struct cpmac_desc *desc;
+ struct sk_buff *skb;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+ priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link,
+ 0, PHY_INTERFACE_MODE_MII);
+#else
+ priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0);
+#endif
+ if (IS_ERR(priv->phy)) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(priv->phy);
+ }
+
+ if (!request_mem_region(dev->mem_start, dev->mem_end -
+ dev->mem_start, dev->name)) {
+ printk("%s: failed to request registers\n",
+ dev->name);
+ res = -ENXIO;
+ goto fail_reserve;
+ }
+
+ priv->regs = ioremap_nocache(dev->mem_start, dev->mem_end -
+ dev->mem_start);
+ if (!priv->regs) {
+ printk("%s: failed to remap registers\n", dev->name);
+ res = -ENXIO;
+ goto fail_remap;
+ }
+
+ priv->rx_head = NULL;
+ size = sizeof(struct cpmac_desc) * (rx_ring_size +
+ CPMAC_TX_RING_SIZE);
+ priv->desc_ring = (struct cpmac_desc *)kmalloc(size, GFP_KERNEL);
+ if (!priv->desc_ring) {
+ res = -ENOMEM;
+ goto fail_alloc;
+ }
+
+ memset((char *)priv->desc_ring, 0, size);
+
+ priv->skb_pool = NULL;
+ priv->free_skbs = 0;
+ priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&priv->alloc_work, cpmac_alloc_skbs);
+#else
+ INIT_WORK(&priv->alloc_work, cpmac_alloc_skbs, dev);
+#endif
+ schedule_work(&priv->alloc_work);
+ flush_scheduled_work();
+
+ for (i = 0; i < rx_ring_size; i++) {
+ desc = &priv->rx_head[i];
+ skb = cpmac_get_skb(dev);
+ if (!skb) {
+ res = -ENOMEM;
+ goto fail_desc;
+ }
+ desc->skb = skb;
+ desc->hw_data = virt_to_phys(skb->data);
+ desc->buflen = CPMAC_SKB_SIZE;
+ desc->dataflags = CPMAC_OWN;
+ desc->next = &priv->rx_head[(i + 1) % rx_ring_size];
+ desc->hw_next = virt_to_phys(desc->next);
+ dma_cache_wback((u32)desc, 16);
+ }
+
+ if((res = request_irq(dev->irq, cpmac_irq, SA_INTERRUPT,
+ dev->name, dev))) {
+ printk("%s: failed to obtain irq\n", dev->name);
+ goto fail_irq;
+ }
+
+ cpmac_reset(dev);
+ cpmac_hw_init(dev);
+
+ netif_start_queue(dev);
+ return 0;
+
+fail_irq:
+fail_desc:
+ for (i = 0; i < rx_ring_size; i++)
+ if (priv->rx_head[i].skb)
+ kfree_skb(priv->rx_head[i].skb);
+fail_alloc:
+ kfree(priv->desc_ring);
+
+ for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
+ priv->skb_pool = skb->next;
+ kfree_skb(skb);
+ }
+
+ iounmap(priv->regs);
+
+fail_remap:
+ release_mem_region(dev->mem_start, dev->mem_end -
+ dev->mem_start);
+
+fail_reserve:
+ phy_disconnect(priv->phy);
+
+ return res;
+}
+
+static int cpmac_stop(struct net_device *dev)
+{
+ int i;
+ struct sk_buff *skb;
+ struct cpmac_priv *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ phy_stop(priv->phy);
+ phy_disconnect(priv->phy);
+ priv->phy = NULL;
+
+ cpmac_reset(dev);
+
+ for (i = 0; i < 8; i++) {
+ priv->regs->rx_ptr[i] = 0;
+ priv->regs->tx_ptr[i] = 0;
+ priv->regs->mbp = 0;
+ }
+
+ free_irq(dev->irq, dev);
+ release_mem_region(dev->mem_start, dev->mem_end -
+ dev->mem_start);
+
+ cancel_delayed_work(&priv->alloc_work);
+ flush_scheduled_work();
+
+ priv->rx_head = &priv->desc_ring[CPMAC_TX_RING_SIZE];
+ for (i = 0; i < rx_ring_size; i++)
+ if (priv->rx_head[i].skb)
+ kfree_skb(priv->rx_head[i].skb);
+
+ kfree(priv->desc_ring);
+
+ for (skb = priv->skb_pool; skb; skb = priv->skb_pool) {
+ priv->skb_pool = skb->next;
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static int external_switch = 0;
+
+static int __devinit cpmac_probe(struct platform_device *pdev)
+{
+ int i, rc, phy_id;
+ struct resource *res;
+ struct cpmac_priv *priv;
+ struct net_device *dev;
+ struct plat_cpmac_data *pdata;
+
+ if (strcmp(pdev->name, "cpmac") != 0)
+ return -ENODEV;
+
+ pdata = pdev->dev.platform_data;
+
+ for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
+ if (!(pdata->phy_mask & (1 << phy_id)))
+ continue;
+ if (!cpmac_mii.phy_map[phy_id])
+ continue;
+ break;
+ }
+
+ if (phy_id == PHY_MAX_ADDR) {
+ if (external_switch) {
+ phy_id = 0;
+ } else {
+ printk("cpmac: no PHY present\n");
+ return -ENODEV;
+ }
+ }
+
+ dev = alloc_etherdev(sizeof(struct cpmac_priv));
+
+ if (!dev) {
+ printk(KERN_ERR "cpmac: Unable to allocate net_device structure!\n");
+ return -ENOMEM;
+ }
+
+ SET_MODULE_OWNER(dev);
+ platform_set_drvdata(pdev, dev);
+ priv = netdev_priv(dev);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!res) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ dev->mem_start = res->start;
+ dev->mem_end = res->end;
+ dev->irq = platform_get_irq_byname(pdev, "irq");
+
+ dev->mtu = 1500;
+ dev->open = cpmac_open;
+ dev->stop = cpmac_stop;
+ dev->set_config = cpmac_config;
+ dev->hard_start_xmit = cpmac_start_xmit;
+ dev->do_ioctl = cpmac_ioctl;
+ dev->get_stats = cpmac_stats;
+ dev->change_mtu = cpmac_change_mtu;
+ dev->set_mac_address = cpmac_set_mac_address;
+ dev->set_multicast_list = cpmac_set_multicast_list;
+ dev->tx_timeout = cpmac_tx_timeout;
+ dev->ethtool_ops = &cpmac_ethtool_ops;
+ if (!disable_napi) {
+ dev->poll = cpmac_poll;
+ dev->weight = min(rx_ring_size, 64);
+ }
+
+ memset(priv, 0, sizeof(struct cpmac_priv));
+ spin_lock_init(&priv->lock);
+ priv->msg_enable = netif_msg_init(NETIF_MSG_WOL, 0x3fff);
+ priv->config = pdata;
+ priv->dev = dev;
+ memcpy(dev->dev_addr, priv->config->dev_addr, sizeof(dev->dev_addr));
+ if (phy_id == 31) {
+ snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT,
+ cpmac_mii.id, phy_id);
+ } else {
+ snprintf(priv->phy_name, BUS_ID_SIZE, "fixed@%d:%d", 100, 1);
+ }
+
+ if ((rc = register_netdev(dev))) {
+ printk("cpmac: error %i registering device %s\n",
+ rc, dev->name);
+ goto fail;
+ }
+
+ printk("cpmac: device %s (regs: %p, irq: %d, phy: %s, mac: ",
+ dev->name, (u32 *)dev->mem_start, dev->irq,
+ priv->phy_name);
+ for (i = 0; i < 6; i++) {
+ printk("%02x", dev->dev_addr[i]);
+ if (i < 5) printk(":");
+ else printk(")\n");
+ }
+
+ return 0;
+
+fail:
+ free_netdev(dev);
+ return rc;
+}
+
+static int __devexit cpmac_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ unregister_netdev(dev);
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver cpmac_driver = {
+ .driver.name = "cpmac",
+ .probe = cpmac_probe,
+ .remove = cpmac_remove,
+};
+
+int __devinit cpmac_init(void)
+{
+ volatile u32 mask;
+ int i, res;
+ cpmac_mii.priv = (struct cpmac_mdio_regs *)
+ ioremap_nocache(AR7_REGS_MDIO, sizeof(struct cpmac_mdio_regs));
+
+ if (!cpmac_mii.priv) {
+ printk("Can't ioremap mdio registers\n");
+ return -ENXIO;
+ }
+
+#warning FIXME: unhardcode gpio&reset bits
+ ar7_gpio_disable(26);
+ ar7_gpio_disable(27);
+ ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
+ ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
+ ar7_device_reset(AR7_RESET_BIT_EPHY);
+
+ cpmac_mii.reset(&cpmac_mii);
+
+ for (i = 0; i < 300000; i++) {
+ mask = ((struct cpmac_mdio_regs *)cpmac_mii.priv)->alive;
+ if (mask)
+ break;
+ }
+
+ mask &= 0x7fffffff;
+ if (mask & (mask - 1)) {
+ external_switch = 1;
+ mask = 0;
+ }
+
+ cpmac_mii.phy_mask = ~(mask | 0x80000000);
+
+ res = mdiobus_register(&cpmac_mii);
+ if (res)
+ goto fail_mii;
+
+ res = platform_driver_register(&cpmac_driver);
+ if (res)
+ goto fail_cpmac;
+
+ return 0;
+
+fail_cpmac:
+ mdiobus_unregister(&cpmac_mii);
+
+fail_mii:
+ iounmap(cpmac_mii.priv);
+
+ return res;
+}
+
+void __devexit cpmac_exit(void)
+{
+ platform_driver_unregister(&cpmac_driver);
+ mdiobus_unregister(&cpmac_mii);
+}
+
+module_init(cpmac_init);
+module_exit(cpmac_exit);
diff -urN linux-2.6.22.1/include/asm-mips/ar7/ar7.h
linux-ar7/include/asm-mips/ar7/ar7.h
--- linux-2.6.22.1/include/asm-mips/ar7/ar7.h 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/include/asm-mips/ar7/ar7.h 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,162 @@
+/*
+ * $Id: ar7.h 8280 2007-07-31 15:52:06Z florian $
+ *
+ * Copyright (C) 2006, 2007 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
+ */
+
+#ifndef __AR7_H__
+#define __AR7_H__
+
+#include <linux/delay.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+
+#define AR7_REGS_BASE 0x08610000
+
+#define AR7_REGS_MAC0 (AR7_REGS_BASE + 0x0000)
+#define AR7_REGS_GPIO (AR7_REGS_BASE + 0x0900)
+#define AR7_REGS_POWER (AR7_REGS_BASE + 0x0a00) // 0x08610A00 - 0x08610BFF
(512 bytes, 128 bytes / clock)
+#define AR7_REGS_UART0 (AR7_REGS_BASE + 0x0e00)
+#define AR7_REGS_RESET (AR7_REGS_BASE + 0x1600)
+#define AR7_REGS_VLYNQ0 (AR7_REGS_BASE + 0x1800)
+#define AR7_REGS_DCL (AR7_REGS_BASE + 0x1A00)
+#define AR7_REGS_VLYNQ1 (AR7_REGS_BASE + 0x1C00)
+#define AR7_REGS_MDIO (AR7_REGS_BASE + 0x1E00)
+#define AR7_REGS_IRQ (AR7_REGS_BASE + 0x2400)
+#define AR7_REGS_MAC1 (AR7_REGS_BASE + 0x2800)
+
+#define AR7_REGS_WDT (AR7_REGS_BASE + 0x1f00)
+#define UR8_REGS_WDT (AR7_REGS_BASE + 0x0b00)
+#define UR8_REGS_UART1 (AR7_REGS_BASE + 0x0f00)
+
+#define AR7_RESET_PEREPHERIAL 0x0
+#define AR7_RESET_SOFTWARE 0x4
+#define AR7_RESET_STATUS 0x8
+
+#define AR7_RESET_BIT_CPMAC_LO 17
+#define AR7_RESET_BIT_CPMAC_HI 21
+#define AR7_RESET_BIT_MDIO 22
+#define AR7_RESET_BIT_EPHY 26
+
+/* GPIO control registers */
+#define AR7_GPIO_INPUT 0x0
+#define AR7_GPIO_OUTPUT 0x4
+#define AR7_GPIO_DIR 0x8
+#define AR7_GPIO_ENABLE 0xC
+
+#define AR7_CHIP_7100 0x18
+#define AR7_CHIP_7200 0x2b
+#define AR7_CHIP_7300 0x05
+
+/* Interrupts */
+#define AR7_IRQ_UART0 15
+#define AR7_IRQ_UART1 16
+
+struct plat_cpmac_data {
+ int reset_bit;
+ int power_bit;
+ u32 phy_mask;
+ char dev_addr[6];
+};
+
+struct plat_dsl_data {
+ int reset_bit_dsl;
+ int reset_bit_sar;
+};
+
+extern int ar7_cpu_clock, ar7_bus_clock, ar7_dsp_clock;
+
+static inline u16 ar7_chip_id(void)
+{
+ return readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x14)) & 0xffff;
+}
+
+static inline u8 ar7_chip_rev(void)
+{
+ return (readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x14)) >> 16) & 0xff;
+}
+
+static inline int ar7_cpu_freq(void)
+{
+ return ar7_cpu_clock;
+}
+
+static inline int ar7_bus_freq(void)
+{
+ return ar7_bus_clock;
+}
+
+static inline int ar7_vbus_freq(void)
+{
+ return ar7_bus_clock / 2;
+}
+#define ar7_cpmac_freq ar7_vbus_freq
+
+static inline int ar7_dsp_freq(void)
+{
+ return ar7_dsp_clock;
+}
+
+static inline int ar7_has_high_cpmac(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7100:
+ case AR7_CHIP_7200:
+ return 0;
+ default:
+ return 1;
+ }
+}
+#define ar7_has_high_vlynq ar7_has_high_cpmac
+#define ar7_has_second_uart ar7_has_high_cpmac
+
+static inline void ar7_device_enable(u32 bit)
+{
+ void *reset_reg = (void *)KSEG1ADDR(AR7_REGS_RESET + AR7_RESET_PEREPHERIAL);
+ writel(readl(reset_reg) | (1 << bit), reset_reg);
+ mdelay(20);
+}
+
+static inline void ar7_device_disable(u32 bit)
+{
+ void *reset_reg = (void *)KSEG1ADDR(AR7_REGS_RESET + AR7_RESET_PEREPHERIAL);
+ writel(readl(reset_reg) & ~(1 << bit), reset_reg);
+ mdelay(20);
+}
+
+static inline void ar7_device_reset(u32 bit)
+{
+ ar7_device_disable(bit);
+ ar7_device_enable(bit);
+}
+
+static inline void ar7_device_on(u32 bit)
+{
+ void *power_reg = (void *)KSEG1ADDR(AR7_REGS_POWER);
+ writel(readl(power_reg) | (1 << bit), power_reg);
+ mdelay(20);
+}
+
+static inline void ar7_device_off(u32 bit)
+{
+ void *power_reg = (void *)KSEG1ADDR(AR7_REGS_POWER);
+ writel(readl(power_reg) & ~(1 << bit), power_reg);
+ mdelay(20);
+}
+
+#endif /* __AR7_H__ */
diff -urN linux-2.6.22.1/include/asm-mips/ar7/gpio.h
linux-ar7/include/asm-mips/ar7/gpio.h
--- linux-2.6.22.1/include/asm-mips/ar7/gpio.h 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/include/asm-mips/ar7/gpio.h 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,116 @@
+/*
+ * $Id: gpio.h 6693 2007-03-25 05:42:16Z ejka $
+ *
+ * Copyright (C) 2007 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
+ */
+
+#ifndef __AR7_GPIO_H__
+#define __AR7_GPIO_H__
+#include <asm/ar7/ar7.h>
+
+#define AR7_GPIO_MAX 32
+
+extern int gpio_request(unsigned gpio, char *label);
+extern void gpio_free(unsigned gpio);
+
+/* Common GPIO layer */
+static inline int gpio_direction_input(unsigned gpio)
+{
+ void __iomem *gpio_dir = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO +
AR7_GPIO_DIR);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_dir) | (1 << gpio), gpio_dir);
+
+ return 0;
+}
+
+static inline int gpio_direction_output(unsigned gpio)
+{
+ void __iomem *gpio_dir = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO +
AR7_GPIO_DIR);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_dir) & ~(1 << gpio), gpio_dir);
+
+ return 0;
+}
+
+static inline int gpio_get_value(unsigned gpio)
+{
+ void __iomem *gpio_in = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO +
AR7_GPIO_INPUT);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ return ((readl(gpio_in) & (1 << gpio)) != 0);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+ void __iomem *gpio_out = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO +
AR7_GPIO_OUTPUT);
+ volatile unsigned tmp;
+
+ if (gpio >= AR7_GPIO_MAX)
+ return;
+
+ tmp = readl(gpio_out) & ~(1 << gpio);
+ if (value)
+ tmp |= 1 << gpio;
+ writel(tmp, gpio_out);
+}
+
+static inline int gpio_to_irq(unsigned gpio)
+{
+ return -EINVAL;
+}
+
+static inline int irq_to_gpio(unsigned irq)
+{
+ return -EINVAL;
+}
+
+/* Board specific GPIO functions */
+static inline int ar7_gpio_enable(unsigned gpio)
+{
+ void __iomem *gpio_en = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO +
AR7_GPIO_ENABLE);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_en) | (1 << gpio), gpio_en);
+
+ return 0;
+}
+
+static inline int ar7_gpio_disable(unsigned gpio)
+{
+ void __iomem *gpio_en = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO +
AR7_GPIO_ENABLE);
+
+ if (gpio >= AR7_GPIO_MAX)
+ return -EINVAL;
+
+ writel(readl(gpio_en) & ~(1 << gpio), gpio_en);
+
+ return 0;
+}
+
+#include <asm-generic/gpio.h>
+
+#endif
diff -urN linux-2.6.22.1/include/asm-mips/ar7/mmzone.h
linux-ar7/include/asm-mips/ar7/mmzone.h
--- linux-2.6.22.1/include/asm-mips/ar7/mmzone.h 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/include/asm-mips/ar7/mmzone.h 2007-08-09 16:22:39.000000000
+0200
@@ -0,0 +1,29 @@
+/*
+ * $Id: mmzone.h 6600 2007-03-18 09:40:51Z ejka $
+ *
+ * Copyright (C) 2007 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
+ */
+
+#ifndef _ASM_MACH_MMZONE_H
+#define _ASM_MACH_MMZONE_H
+
+extern pg_data_t __node_data[];
+#define NODE_DATA(nid) (&__node_data[nid])
+#define NODE_MEM_MAP(nid) (NODE_DATA(nid)->node_mem_map)
+#define pa_to_nid(addr) (((addr) >= ARCH_PFN_OFFSET << PAGE_SHIFT) ? 0 : -1)
+
+#endif /* _ASM_MACH_MMZONE_H */
diff -urN linux-2.6.22.1/include/asm-mips/ar7/prom.h
linux-ar7/include/asm-mips/ar7/prom.h
--- linux-2.6.22.1/include/asm-mips/ar7/prom.h 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/include/asm-mips/ar7/prom.h 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,27 @@
+/*
+ * $Id: prom.h 8190 2007-07-27 03:48:54Z nico $
+ *
+ * Copyright (C) 2006, 2007 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
+ */
+
+#ifndef __PROM_H__
+#define __PROM_H__
+
+extern char *prom_getenv(char *name);
+extern void prom_printf(char *fmt, ...);
+
+#endif /* __PROM_H__ */
diff -urN linux-2.6.22.1/include/asm-mips/ar7/spaces.h
linux-ar7/include/asm-mips/ar7/spaces.h
--- linux-2.6.22.1/include/asm-mips/ar7/spaces.h 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/include/asm-mips/ar7/spaces.h 2007-08-09 16:22:39.000000000
+0200
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002 Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_AR7_SPACES_H
+#define _ASM_AR7_SPACES_H
+
+#define CAC_BASE 0x80000000
+#define IO_BASE 0xa0000000
+#define UNCAC_BASE 0xa0000000
+#define MAP_BASE 0xc0000000
+
+/*
+ * This handles the memory map.
+ * We handle pages at KSEG0 for kernels with 32 bit address space.
+ */
+#define PAGE_OFFSET 0x94000000UL
+#define PHYS_OFFSET 0x14000000UL
+
+/*
+ * Memory above this physical address will be considered highmem.
+ */
+#ifndef HIGHMEM_START
+#define HIGHMEM_START 0x40000000UL
+#endif
+
+#endif /* __ASM_AR7_SPACES_H */
diff -urN linux-2.6.22.1/include/asm-mips/ar7/vlynq.h
linux-ar7/include/asm-mips/ar7/vlynq.h
--- linux-2.6.22.1/include/asm-mips/ar7/vlynq.h 1970-01-01 01:00:00.000000000
+0100
+++ linux-ar7/include/asm-mips/ar7/vlynq.h 2007-08-09 16:22:39.000000000 +0200
@@ -0,0 +1,92 @@
+/*
+ * $Id: vlynq.h 8190 2007-07-27 03:48:54Z nico $
+ *
+ * Copyright (C) 2006, 2007 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
+ */
+
+
+#ifndef __VLYNQ_H__
+#define __VLYNQ_H__
+
+struct vlynq_mapping {
+ u32 size;
+ u32 offset;
+} __attribute__ ((packed));
+
+struct vlynq_device_id {
+ u32 id;
+};
+
+struct vlynq_regs;
+struct vlynq_device {
+ u32 id;
+ int irq;
+ int local_irq;
+ int remote_irq;
+ int clock_div;
+ u32 regs_start, regs_end;
+ u32 mem_start, mem_end;
+ u32 irq_start, irq_end;
+ void *priv;
+ struct vlynq_regs *local;
+ struct vlynq_regs *remote;
+ struct device dev;
+};
+
+struct vlynq_driver {
+ char *name;
+ int (*probe)(struct vlynq_device *dev);
+ int (*remove)(struct vlynq_device *dev);
+ struct device_driver driver;
+};
+
+#define to_vlynq_driver(drv) container_of(drv, struct vlynq_driver, driver)
+
+struct plat_vlynq_ops {
+ int (*on)(struct vlynq_device *dev);
+ void (*off)(struct vlynq_device *dev);
+};
+
+#define to_vlynq_device(device) container_of(device, struct vlynq_device,
dev)
+
+extern struct bus_type vlynq_bus_type;
+
+extern int __vlynq_register_driver(struct vlynq_driver *driver,
+ struct module *owner);
+
+static inline int vlynq_register_driver(struct vlynq_driver *driver)
+{
+ return __vlynq_register_driver(driver, THIS_MODULE);
+}
+
+extern void vlynq_unregister_driver(struct vlynq_driver *driver);
+extern int vlynq_device_enable(struct vlynq_device *dev);
+extern void vlynq_device_disable(struct vlynq_device *dev);
+extern u32 vlynq_local_id(struct vlynq_device *dev);
+extern u32 vlynq_remote_id(struct vlynq_device *dev);
+extern void vlynq_set_local_mapping(struct vlynq_device *dev,
+ u32 tx_offset,
+ struct vlynq_mapping *mapping);
+extern void vlynq_set_remote_mapping(struct vlynq_device *dev,
+ u32 tx_offset,
+ struct vlynq_mapping *mapping);
+extern int vlynq_virq_to_irq(struct vlynq_device *dev, int virq);
+extern int vlynq_irq_to_virq(struct vlynq_device *dev, int irq);
+extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq);
+extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq);
+
+#endif /* __VLYNQ_H__ */
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH][MIPS][5/7] AR7: watchdog timer
2007-08-20 15:04 [PATCH 1/1] AR7 port Matteo Croce
@ 2007-09-06 15:31 ` Matteo Croce
2007-09-09 8:47 ` Wim Van Sebroeck
0 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-06 15:31 UTC (permalink / raw)
To: linux-mips; +Cc: Nicolas Thill, Enrik Berkhan, Christer Weinigel, wim
Driver for the watchdog timer. It worked with 2.4, doesn't does with 2.6.
Apart that it doesn't reboots the device it works :)
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
Signed-off-by: Enrik Berkhan <Enrik.Berkhan@akk.org>
Signed-off-by: Christer Weinigel <wingel@nano-system.com>
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 37bddc1..78d4940 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -583,6 +583,12 @@ config SBC_EPX_C3_WATCHDOG
# MIPS Architecture
+config AR7_WDT
+ tristate "TI AR7 Watchdog Timer"
+ depends on WATCHDOG && AR7
+ help
+ Hardware driver for the TI AR7 Watchdog Timer.
+
config INDYDOG
tristate "Indy/I2 Hardware Watchdog"
depends on SGI_IP22
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 389f8b1..76424f2 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
# M68KNOMMU Architecture
# MIPS Architecture
+obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
obj-$(CONFIG_INDYDOG) += indydog.o
obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
diff --git a/drivers/char/watchdog/ar7_wdt.c b/drivers/char/watchdog/ar7_wdt.c
new file mode 100644
index 0000000..ca36981
--- /dev/null
+++ b/drivers/char/watchdog/ar7_wdt.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
+ * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
+ *
+ * Some code taken from:
+ * National Semiconductor SCx200 Watchdog support
+ * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+ *
+ * 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 <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+
+#include <asm/addrspace.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/ar7/ar7.h>
+
+#define DRVNAME "ar7_wdt"
+#define LONGNAME "TI AR7 Watchdog Timer"
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int margin = 60;
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+typedef struct {
+ u32 kick_lock;
+ u32 kick;
+ u32 change_lock;
+ u32 change ;
+ u32 disable_lock;
+ u32 disable;
+ u32 prescale_lock;
+ u32 prescale;
+} ar7_wdt_t;
+
+static struct semaphore open_semaphore;
+static unsigned expect_close;
+
+/* XXX currently fixed, allows max margin ~68.72 secs */
+#define prescale_value 0xFFFF
+
+/* Offset of the WDT registers */
+static unsigned long ar7_regs_wdt;
+/* Pointer to the remapped WDT IO space */
+static ar7_wdt_t *ar7_wdt;
+static void ar7_wdt_get_regs(void)
+{
+ u16 chip_id = ar7_chip_id();
+ switch (chip_id) {
+ case AR7_CHIP_7100:
+ case AR7_CHIP_7200:
+ ar7_regs_wdt = AR7_REGS_WDT;
+ break;
+ default:
+ ar7_regs_wdt = UR8_REGS_WDT;
+ break;
+ }
+}
+
+
+static void ar7_wdt_kick(u32 value)
+{
+ ar7_wdt->kick_lock = 0x5555;
+ if ((ar7_wdt->kick_lock & 3) == 1) {
+ ar7_wdt->kick_lock = 0xAAAA;
+ if ((ar7_wdt->kick_lock & 3) == 3) {
+ ar7_wdt->kick = value;
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
+}
+
+static void ar7_wdt_prescale(u32 value)
+{
+ ar7_wdt->prescale_lock = 0x5A5A;
+ if ((ar7_wdt->prescale_lock & 3) == 1) {
+ ar7_wdt->prescale_lock = 0xA5A5;
+ if ((ar7_wdt->prescale_lock & 3) == 3) {
+ ar7_wdt->prescale = value;
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
+}
+
+static void ar7_wdt_change(u32 value)
+{
+ ar7_wdt->change_lock = 0x6666;
+ if ((ar7_wdt->change_lock & 3) == 1) {
+ ar7_wdt->change_lock = 0xBBBB;
+ if ((ar7_wdt->change_lock & 3) == 3) {
+ ar7_wdt->change = value;
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
+}
+
+static void ar7_wdt_disable(u32 value)
+{
+ ar7_wdt->disable_lock = 0x7777;
+ if ((ar7_wdt->disable_lock & 3) == 1) {
+ ar7_wdt->disable_lock = 0xCCCC;
+ if ((ar7_wdt->disable_lock & 3) == 2) {
+ ar7_wdt->disable_lock = 0xDDDD;
+ if ((ar7_wdt->disable_lock & 3) == 3) {
+ ar7_wdt->disable = value;
+ return;
+ }
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
+}
+
+static void ar7_wdt_update_margin(int new_margin)
+{
+ u32 change;
+
+ change = new_margin * (ar7_vbus_freq() / prescale_value);
+ if (change < 1) change = 1;
+ if (change > 0xFFFF) change = 0xFFFF;
+ ar7_wdt_change(change);
+ margin = change * prescale_value / ar7_vbus_freq();
+ printk(KERN_INFO DRVNAME
+ ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
+ margin, prescale_value, change, ar7_vbus_freq());
+}
+
+static void ar7_wdt_enable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
+ ar7_wdt_disable(1);
+ ar7_wdt_kick(1);
+}
+
+static void ar7_wdt_disable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
+ ar7_wdt_disable(0);
+}
+
+static int ar7_wdt_open(struct inode *inode, struct file *file)
+{
+ /* only allow one at a time */
+ if (down_trylock(&open_semaphore))
+ return -EBUSY;
+ ar7_wdt_enable_wdt();
+ expect_close = 0;
+
+ return 0;
+}
+
+static int ar7_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!expect_close) {
+ printk(KERN_WARNING DRVNAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n");
+ } else if (!nowayout) {
+ ar7_wdt_disable_wdt();
+ }
+ up(&open_semaphore);
+
+ return 0;
+}
+
+static int ar7_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_HALT || code == SYS_POWER_OFF)
+ if (!nowayout)
+ ar7_wdt_disable_wdt();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ar7_wdt_notifier = {
+ .notifier_call = ar7_wdt_notify_sys
+};
+
+static ssize_t ar7_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /* check for a magic close character */
+ if (len) {
+ size_t i;
+
+ ar7_wdt_kick(1);
+
+ expect_close = 0;
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data+i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 1;
+ }
+
+ }
+ return len;
+}
+
+static int ar7_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info ident = {
+ .identity = LONGNAME,
+ .firmware_version = 1,
+ .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
+ };
+ int new_margin;
+
+ switch (cmd) {
+ default:
+ return -ENOTTY;
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof (ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int *)arg))
+ return -EFAULT;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ ar7_wdt_kick(1);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int *)arg))
+ return -EFAULT;
+ if (new_margin < 1)
+ return -EINVAL;
+
+ ar7_wdt_update_margin(new_margin);
+ ar7_wdt_kick(1);
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(margin, (int *)arg))
+ return -EFAULT;
+ return 0;
+ }
+}
+
+static struct file_operations ar7_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ar7_wdt_write,
+ .ioctl = ar7_wdt_ioctl,
+ .open = ar7_wdt_open,
+ .release = ar7_wdt_release,
+};
+
+static struct miscdevice ar7_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ar7_wdt_fops,
+};
+
+static int __init ar7_wdt_init(void)
+{
+ int rc;
+
+ ar7_wdt_get_regs();
+
+ if (!request_mem_region(ar7_regs_wdt, sizeof(ar7_wdt_t), LONGNAME)) {
+ printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
+ return -EBUSY;
+ }
+
+ ar7_wdt = (ar7_wdt_t *)ioremap(ar7_regs_wdt, sizeof(ar7_wdt_t));
+
+ ar7_wdt_disable_wdt();
+ ar7_wdt_prescale(prescale_value);
+ ar7_wdt_update_margin(margin);
+
+ sema_init(&open_semaphore, 1);
+
+ rc = misc_register(&ar7_wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR DRVNAME ": unable to register misc device\n");
+ goto out_alloc;
+ }
+
+ rc = register_reboot_notifier(&ar7_wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR DRVNAME ": unable to register reboot notifier\n");
+ goto out_register;
+ }
+ goto out;
+
+out_register:
+ misc_deregister(&ar7_wdt_miscdev);
+out_alloc:
+ release_mem_region(ar7_regs_wdt, sizeof(ar7_wdt_t));
+out:
+ return rc;
+}
+
+static void __exit ar7_wdt_cleanup(void)
+{
+ unregister_reboot_notifier(&ar7_wdt_notifier);
+ misc_deregister(&ar7_wdt_miscdev);
+ iounmap(ar7_wdt);
+ release_mem_region(ar7_regs_wdt, sizeof(ar7_wdt_t));
+}
+
+module_init(ar7_wdt_init);
+module_exit(ar7_wdt_cleanup);
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-06 15:31 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
@ 2007-09-09 8:47 ` Wim Van Sebroeck
2007-09-09 18:19 ` Matteo Croce
0 siblings, 1 reply; 30+ messages in thread
From: Wim Van Sebroeck @ 2007-09-09 8:47 UTC (permalink / raw)
To: Matteo Croce; +Cc: linux-mips, Nicolas Thill, Enrik Berkhan, Christer Weinigel
Hi Matteo,
> Driver for the watchdog timer. It worked with 2.4, doesn't does with 2.6.
> Apart that it doesn't reboots the device it works :)
Can you please explain this a bit more? Is this driver working under 2.4
(and also rebooting the device) but not under 2.6?
Thanks in advance,
Wim.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-09 8:47 ` Wim Van Sebroeck
@ 2007-09-09 18:19 ` Matteo Croce
2007-09-09 18:27 ` Florian Fainelli
2007-09-12 19:41 ` Wim Van Sebroeck
0 siblings, 2 replies; 30+ messages in thread
From: Matteo Croce @ 2007-09-09 18:19 UTC (permalink / raw)
To: Wim Van Sebroeck
Cc: linux-mips, Nicolas Thill, Enrik Berkhan, Christer Weinigel
Il Sunday 09 September 2007 10:47:52 hai scritto:
> Hi Matteo,
>
> > Driver for the watchdog timer. It worked with 2.4, doesn't does with 2.6.
> > Apart that it doesn't reboots the device it works :)
>
> Can you please explain this a bit more? Is this driver working under 2.4
> (and also rebooting the device) but not under 2.6?
Exactly.
A guy had it working with the attached patch but it doesn't works for me.
Cheers,
Matteo
Index: target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c
===================================================================
--- target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c (revision 8520)
+++ target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c (working copy)
@@ -76,7 +76,6 @@
// Offset of the WDT registers
static unsigned long ar7_regs_wdt;
// Pointer to the remapped WDT IO space
-static ar7_wdt_t *ar7_wdt;
static void ar7_wdt_get_regs(void)
{
u16 chip_id = ar7_chip_id();
@@ -94,6 +93,8 @@
static void ar7_wdt_kick(u32 value)
{
+ volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(ar7_regs_wdt, sizeof(ar7_wdt_t));
+
ar7_wdt->kick_lock = 0x5555;
if ((ar7_wdt->kick_lock & 3) == 1) {
ar7_wdt->kick_lock = 0xAAAA;
@@ -107,6 +108,8 @@
static void ar7_wdt_prescale(u32 value)
{
+ volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(ar7_regs_wdt, sizeof(ar7_wdt_t));
+
ar7_wdt->prescale_lock = 0x5A5A;
if ((ar7_wdt->prescale_lock & 3) == 1) {
ar7_wdt->prescale_lock = 0xA5A5;
@@ -120,6 +123,8 @@
static void ar7_wdt_change(u32 value)
{
+ volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(ar7_regs_wdt, sizeof(ar7_wdt_t));
+
ar7_wdt->change_lock = 0x6666;
if ((ar7_wdt->change_lock & 3) == 1) {
ar7_wdt->change_lock = 0xBBBB;
@@ -133,6 +138,8 @@
static void ar7_wdt_disable(u32 value)
{
+ volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(ar7_regs_wdt, sizeof(ar7_wdt_t));
+
ar7_wdt->disable_lock = 0x7777;
if ((ar7_wdt->disable_lock & 3) == 1) {
ar7_wdt->disable_lock = 0xCCCC;
@@ -215,9 +222,6 @@
static ssize_t ar7_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
/* check for a magic close character */
if (len) {
size_t i;
@@ -304,8 +308,6 @@
return -EBUSY;
}
- ar7_wdt = (ar7_wdt_t *)ioremap(ar7_regs_wdt, sizeof(ar7_wdt_t));
-
ar7_wdt_disable_wdt();
ar7_wdt_prescale(prescale_value);
ar7_wdt_update_margin(margin);
@@ -337,7 +339,6 @@
{
unregister_reboot_notifier(&ar7_wdt_notifier);
misc_deregister(&ar7_wdt_miscdev);
- iounmap(ar7_wdt);
release_mem_region(ar7_regs_wdt, sizeof(ar7_wdt_t));
}
Index: target/linux/ar7-2.6/config/default
===================================================================
--- target/linux/ar7-2.6/config/default (revision 8520)
+++ target/linux/ar7-2.6/config/default (working copy)
@@ -3,7 +3,7 @@
# CONFIG_64BIT_PHYS_ADDR is not set
CONFIG_AR7=y
CONFIG_AR7_GPIO=y
-# CONFIG_AR7_WDT is not set
+CONFIG_AR7_WDT=y
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
# CONFIG_ARCH_SUPPORTS_MSI is not set
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-09 18:19 ` Matteo Croce
@ 2007-09-09 18:27 ` Florian Fainelli
2007-09-10 8:06 ` Thomas Bogendoerfer
2007-09-12 19:41 ` Wim Van Sebroeck
1 sibling, 1 reply; 30+ messages in thread
From: Florian Fainelli @ 2007-09-09 18:27 UTC (permalink / raw)
To: Matteo Croce
Cc: Wim Van Sebroeck, linux-mips, Nicolas Thill, Enrik Berkhan,
Christer Weinigel
[-- Attachment #1: Type: text/plain, Size: 819 bytes --]
Hi all,
The thing is that the different TNETD versions : 7100, 7200 and 7300 should
not have the same watchdog handling.
Right now, we have no easy way to figure out what is different from one
version to another.
Le dimanche 9 septembre 2007, Matteo Croce a écrit :
> Il Sunday 09 September 2007 10:47:52 hai scritto:
> > Hi Matteo,
> >
> > > Driver for the watchdog timer. It worked with 2.4, doesn't does with
> > > 2.6. Apart that it doesn't reboots the device it works :)
> >
> > Can you please explain this a bit more? Is this driver working under 2.4
> > (and also rebooting the device) but not under 2.6?
>
> Exactly.
> A guy had it working with the attached patch but it doesn't works for me.
>
> Cheers,
> Matteo
--
Cordialement, Florian Fainelli
------------------------------
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-09 18:27 ` Florian Fainelli
@ 2007-09-10 8:06 ` Thomas Bogendoerfer
2007-09-10 9:00 ` Thomas Bogendoerfer
0 siblings, 1 reply; 30+ messages in thread
From: Thomas Bogendoerfer @ 2007-09-10 8:06 UTC (permalink / raw)
To: Florian Fainelli
Cc: Matteo Croce, Wim Van Sebroeck, linux-mips, Nicolas Thill,
Enrik Berkhan, Christer Weinigel
On Sun, Sep 09, 2007 at 08:27:29PM +0200, Florian Fainelli wrote:
> The thing is that the different TNETD versions : 7100, 7200 and 7300 should
> not have the same watchdog handling.
you just need to use the right address, 7100/7200 have a different
address where the watchdog is (0x18611F00) than the 7300 (0x18610B00).
Usage of the watchdog is the same.
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-10 8:06 ` Thomas Bogendoerfer
@ 2007-09-10 9:00 ` Thomas Bogendoerfer
2007-09-11 19:54 ` Matteo Croce
0 siblings, 1 reply; 30+ messages in thread
From: Thomas Bogendoerfer @ 2007-09-10 9:00 UTC (permalink / raw)
To: Florian Fainelli
Cc: Matteo Croce, Wim Van Sebroeck, linux-mips, Nicolas Thill,
Enrik Berkhan, Christer Weinigel
On Mon, Sep 10, 2007 at 10:06:34AM +0200, Thomas Bogendoerfer wrote:
> On Sun, Sep 09, 2007 at 08:27:29PM +0200, Florian Fainelli wrote:
> > The thing is that the different TNETD versions : 7100, 7200 and 7300 should
> > not have the same watchdog handling.
>
> you just need to use the right address, 7100/7200 have a different
> address where the watchdog is (0x18611F00) than the 7300 (0x18610B00).
> Usage of the watchdog is the same.
>
got the addresses wrong, they are 0x08611f00 and 0x08610b00 (both physical
addresses).
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-10 9:00 ` Thomas Bogendoerfer
@ 2007-09-11 19:54 ` Matteo Croce
2007-09-11 20:34 ` Thomas Bogendoerfer
0 siblings, 1 reply; 30+ messages in thread
From: Matteo Croce @ 2007-09-11 19:54 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: Florian Fainelli, Wim Van Sebroeck, linux-mips, Nicolas Thill,
Enrik Berkhan, Christer Weinigel
Il Monday 10 September 2007 11:00:23 Thomas Bogendoerfer ha scritto:
> On Mon, Sep 10, 2007 at 10:06:34AM +0200, Thomas Bogendoerfer wrote:
> > On Sun, Sep 09, 2007 at 08:27:29PM +0200, Florian Fainelli wrote:
> > > The thing is that the different TNETD versions : 7100, 7200 and 7300 should
> > > not have the same watchdog handling.
> >
> > you just need to use the right address, 7100/7200 have a different
> > address where the watchdog is (0x18611F00) than the 7300 (0x18610B00).
> > Usage of the watchdog is the same.
> >
>
> got the addresses wrong, they are 0x08611f00 and 0x08610b00 (both physical
> addresses).
>
> Thomas.
>
If you read the ar7.h include file you will find:
#define AR7_REGS_BASE 0x08610000
#define AR7_REGS_WDT (AR7_REGS_BASE + 0x1f00)
#define UR8_REGS_WDT (AR7_REGS_BASE + 0x0b00)
So they are correct
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-11 19:54 ` Matteo Croce
@ 2007-09-11 20:34 ` Thomas Bogendoerfer
0 siblings, 0 replies; 30+ messages in thread
From: Thomas Bogendoerfer @ 2007-09-11 20:34 UTC (permalink / raw)
To: Matteo Croce
Cc: Florian Fainelli, Wim Van Sebroeck, linux-mips, Nicolas Thill,
Enrik Berkhan, Christer Weinigel
On Tue, Sep 11, 2007 at 09:54:24PM +0200, Matteo Croce wrote:
> If you read the ar7.h include file you will find:
>
> #define AR7_REGS_BASE 0x08610000
>
> #define AR7_REGS_WDT (AR7_REGS_BASE + 0x1f00)
> #define UR8_REGS_WDT (AR7_REGS_BASE + 0x0b00)
>
> So they are correct
well 7300 is not UR8, but ar7_wdt_get_regs() looks correct.
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessary a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH][MIPS][5/7] AR7: watchdog timer
2007-09-09 18:19 ` Matteo Croce
2007-09-09 18:27 ` Florian Fainelli
@ 2007-09-12 19:41 ` Wim Van Sebroeck
1 sibling, 0 replies; 30+ messages in thread
From: Wim Van Sebroeck @ 2007-09-12 19:41 UTC (permalink / raw)
To: Matteo Croce; +Cc: linux-mips, Nicolas Thill, Enrik Berkhan, Christer Weinigel
Hi Matteo,
> > > Driver for the watchdog timer. It worked with 2.4, doesn't does with 2.6.
> > > Apart that it doesn't reboots the device it works :)
> >
> > Can you please explain this a bit more? Is this driver working under 2.4
> > (and also rebooting the device) but not under 2.6?
>
> Exactly.
> A guy had it working with the attached patch but it doesn't works for me.
I'll have a look at the driver this weekend.
Greetings,
Wim.
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2007-10-07 20:27 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <200709080143.12345.technoboy85@gmail.com>
2007-09-08 0:18 ` [PATCH][MIPS][1/7] AR7: core support Matteo Croce
2007-09-08 17:40 ` Atsushi Nemoto
2007-09-11 22:43 ` Matteo Croce
2007-09-12 11:10 ` Ralf Baechle
2007-09-12 11:22 ` Thomas Bogendoerfer
2007-09-12 13:55 ` Ralf Baechle
2007-09-08 0:19 ` [PATCH][MIPS][2/7] AR7: mtd partition map Matteo Croce
2007-09-08 0:19 ` Matteo Croce
2007-09-08 0:20 ` [PATCH][MIPS][3/7] AR7: gpio char device Matteo Croce
2007-09-08 0:20 ` [PATCH][MIPS][4/7] AR7: leds driver Matteo Croce
2007-09-11 21:43 ` Richard Purdie
2007-09-08 0:21 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
2007-09-08 0:22 ` [PATCH][MIPS][6/7] AR7: serial Matteo Croce
2007-09-08 0:23 ` [PATCH][MIPS][7/7] AR7: ethernet Matteo Croce
2007-09-12 16:50 ` Ralf Baechle
2007-09-13 1:42 ` Thiemo Seufer
2007-09-13 11:35 ` Ralf Baechle
2007-09-20 15:28 [PATCH][MIPS][0/7] AR7: 4th effort Matteo Croce
2007-09-20 16:06 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
2007-10-03 19:24 ` Wim Van Sebroeck
2007-10-03 20:17 ` Matteo Croce
2007-10-07 20:31 ` Wim Van Sebroeck
-- strict thread matches above, loose matches on Subject: below --
2007-08-20 15:04 [PATCH 1/1] AR7 port Matteo Croce
2007-09-06 15:31 ` [PATCH][MIPS][5/7] AR7: watchdog timer Matteo Croce
2007-09-09 8:47 ` Wim Van Sebroeck
2007-09-09 18:19 ` Matteo Croce
2007-09-09 18:27 ` Florian Fainelli
2007-09-10 8:06 ` Thomas Bogendoerfer
2007-09-10 9:00 ` Thomas Bogendoerfer
2007-09-11 19:54 ` Matteo Croce
2007-09-11 20:34 ` Thomas Bogendoerfer
2007-09-12 19:41 ` Wim Van Sebroeck
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.