diff options
author | wbx <wbx@hydrogenium.(none)> | 2009-05-17 14:41:34 +0200 |
---|---|---|
committer | wbx <wbx@hydrogenium.(none)> | 2009-05-17 14:41:34 +0200 |
commit | 219a6dab8995aad9ac4860cc1a84d6f3509a03a4 (patch) | |
tree | b9c0f3c43aebba2fcfef777592d0add39f2072f4 /target/rb433/patches |
Initial import
Diffstat (limited to 'target/rb433/patches')
-rw-r--r-- | target/rb433/patches/ar71xx.patch | 9913 | ||||
-rw-r--r-- | target/rb433/patches/ip175-switch.patch | 1364 |
2 files changed, 11277 insertions, 0 deletions
diff --git a/target/rb433/patches/ar71xx.patch b/target/rb433/patches/ar71xx.patch new file mode 100644 index 000000000..3646174f4 --- /dev/null +++ b/target/rb433/patches/ar71xx.patch @@ -0,0 +1,9913 @@ +diff -Nur linux-2.6.29.1.orig/arch/mips/Kconfig linux-2.6.29.1/arch/mips/Kconfig +--- linux-2.6.29.1.orig/arch/mips/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/Kconfig 2009-04-13 14:27:23.734393467 +0200 +@@ -22,6 +22,23 @@ + config MACH_ALCHEMY + bool "Alchemy processor based machines" + ++config ATHEROS_AR71XX ++ bool "Atheros AR71xx based boards" ++ select CEVT_R4K ++ select CSRC_R4K ++ select DMA_NONCOHERENT ++ select HW_HAS_PCI ++ select IRQ_CPU ++ select ARCH_REQUIRE_GPIOLIB ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_HAS_CPU_MIPS32_R2 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_BIG_ENDIAN ++ select SYS_HAS_EARLY_PRINTK ++ select MIPS_MACHINE ++ help ++ Support for Atheros AR71xx based boards. ++ + config BASLER_EXCITE + bool "Basler eXcite smart camera" + select CEVT_R4K +@@ -640,6 +657,7 @@ + endchoice + + source "arch/mips/alchemy/Kconfig" ++source "arch/mips/ar71xx/Kconfig" + source "arch/mips/basler/excite/Kconfig" + source "arch/mips/jazz/Kconfig" + source "arch/mips/lasat/Kconfig" +@@ -810,6 +828,9 @@ + config SYNC_R4K + bool + ++config MIPS_MACHINE ++ def_bool n ++ + config NO_IOPORT + def_bool n + +diff -Nur linux-2.6.29.1.orig/arch/mips/Makefile linux-2.6.29.1/arch/mips/Makefile +--- linux-2.6.29.1.orig/arch/mips/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/Makefile 2009-04-13 14:27:34.483063970 +0200 +@@ -602,6 +602,14 @@ + load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff81100000 + endif + ++# ++# Atheros AR71xx ++# ++core-$(CONFIG_ATHEROS_AR71XX) += arch/mips/ar71xx/ ++cflags-$(CONFIG_ATHEROS_AR71XX) += -I$(srctree)/arch/mips/include/asm/mach-ar71xx ++load-$(CONFIG_ATHEROS_AR71XX) += 0xffffffff80060000 ++ ++ + cflags-y += -I$(srctree)/arch/mips/include/asm/mach-generic + drivers-$(CONFIG_PCI) += arch/mips/pci/ + +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/Kconfig linux-2.6.29.1/arch/mips/ar71xx/Kconfig +--- linux-2.6.29.1.orig/arch/mips/ar71xx/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/Kconfig 2009-04-13 14:27:34.483063970 +0200 +@@ -0,0 +1,64 @@ ++if ATHEROS_AR71XX ++ ++config AR71XX_EARLY_SERIAL ++ bool "Use early serial console" ++ default n ++ ++menu "Atheros AR71xx machine selection" ++ ++config AR71XX_MACH_AP81 ++ bool "Atheros AP81 board support" ++ default y ++ ++config AR71XX_MACH_AP83 ++ bool "Atheros AP83 board support" ++ default y ++ ++config AR71XX_MACH_PB42 ++ bool "Atheros PB42 board support" ++ default y ++ ++config AR71XX_MACH_AW_NR580 ++ bool "AzureWave AW-NR580 board support" ++ default y ++ ++config AR71XX_MACH_GENERIC ++ bool "Generic AR71xx based machine support" ++ default y ++ ++config AR71XX_MACH_WP543 ++ bool "Compex WP543 board support" ++ select MYLOADER ++ default y ++ ++config AR71XX_MACH_RB_4XX ++ bool "MikroTik RouterBOARD 4xx series support" ++ default y ++ ++config AR71XX_MACH_WNR2000 ++ bool "NETGEAR WNR2000 board support" ++ default y ++ ++config AR71XX_MACH_MZK_W04NU ++ bool "Planex MZK-W04NU board support" ++ default y ++ ++config AR71XX_MACH_MZK_W300NH ++ bool "Planex MZK-W300NH board support" ++ default y ++ ++config AR71XX_MACH_TL_WR941ND ++ bool "TP-LINK TL-WR941ND support" ++ default y ++ ++config AR71XX_MACH_TEW_632BRP ++ bool "TRENDnet TEW-632BRP support" ++ default y ++ ++config AR71XX_MACH_UBNT ++ bool "Ubiquiti AR71xx based boards support" ++ default y ++ ++endmenu ++ ++endif +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/Makefile linux-2.6.29.1/arch/mips/ar71xx/Makefile +--- linux-2.6.29.1.orig/arch/mips/ar71xx/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/Makefile 2009-04-13 14:27:34.487064480 +0200 +@@ -0,0 +1,25 @@ ++# ++# Makefile for the Atheros AR71xx SoC specific parts of the kernel ++# ++# Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++# Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License version 2 as published ++# by the Free Software Foundation. ++ ++obj-y := prom.o irq.o setup.o devices.o gpio.o ar71xx.o ++ ++obj-$(CONFIG_AR71XX_MACH_AP81) += mach-ap81.o ++obj-$(CONFIG_AR71XX_MACH_AP83) += mach-ap83.o ++obj-$(CONFIG_AR71XX_MACH_AW_NR580) += mach-aw-nr580.o ++obj-$(CONFIG_AR71XX_MACH_GENERIC) += mach-generic.o ++obj-$(CONFIG_AR71XX_MACH_MZK_W04NU) += mach-mzk-w04nu.o ++obj-$(CONFIG_AR71XX_MACH_MZK_W300NH) += mach-mzk-w300nh.o ++obj-$(CONFIG_AR71XX_MACH_PB42) += mach-pb42.o ++obj-$(CONFIG_AR71XX_MACH_RB_4XX) += mach-rb-4xx.o ++obj-$(CONFIG_AR71XX_MACH_TEW_632BRP) += mach-tew-632brp.o ++obj-$(CONFIG_AR71XX_MACH_TL_WR941ND) += mach-tl-wr941nd.o ++obj-$(CONFIG_AR71XX_MACH_UBNT) += mach-ubnt.o ++obj-$(CONFIG_AR71XX_MACH_WNR2000) += mach-wnr2000.o ++obj-$(CONFIG_AR71XX_MACH_WP543) += mach-wp543.o +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/ar71xx.c linux-2.6.29.1/arch/mips/ar71xx/ar71xx.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/ar71xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/ar71xx.c 2009-04-13 14:27:34.487064480 +0200 +@@ -0,0 +1,100 @@ ++/* ++ * AR71xx SoC routines ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/types.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++void __iomem *ar71xx_ddr_base; ++EXPORT_SYMBOL_GPL(ar71xx_ddr_base); ++ ++void __iomem *ar71xx_pll_base; ++EXPORT_SYMBOL_GPL(ar71xx_pll_base); ++ ++void __iomem *ar71xx_reset_base; ++EXPORT_SYMBOL_GPL(ar71xx_reset_base); ++ ++void __iomem *ar71xx_gpio_base; ++EXPORT_SYMBOL_GPL(ar71xx_gpio_base); ++ ++void __iomem *ar71xx_usb_ctrl_base; ++EXPORT_SYMBOL_GPL(ar71xx_usb_ctrl_base); ++ ++void ar71xx_device_stop(u32 mask) ++{ ++ unsigned long flags; ++ u32 t; ++ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++ local_irq_save(flags); ++ t = ar71xx_reset_rr(AR71XX_RESET_REG_RESET_MODULE); ++ ar71xx_reset_wr(AR71XX_RESET_REG_RESET_MODULE, t | mask); ++ local_irq_restore(flags); ++ break; ++ ++ case AR71XX_SOC_AR9130: ++ case AR71XX_SOC_AR9132: ++ local_irq_save(flags); ++ t = ar71xx_reset_rr(AR91XX_RESET_REG_RESET_MODULE); ++ ar71xx_reset_wr(AR91XX_RESET_REG_RESET_MODULE, t | mask); ++ local_irq_restore(flags); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++EXPORT_SYMBOL_GPL(ar71xx_device_stop); ++ ++void ar71xx_device_start(u32 mask) ++{ ++ unsigned long flags; ++ u32 t; ++ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++ local_irq_save(flags); ++ t = ar71xx_reset_rr(AR71XX_RESET_REG_RESET_MODULE); ++ ar71xx_reset_wr(AR71XX_RESET_REG_RESET_MODULE, t & ~mask); ++ local_irq_restore(flags); ++ break; ++ ++ case AR71XX_SOC_AR9130: ++ case AR71XX_SOC_AR9132: ++ local_irq_save(flags); ++ t = ar71xx_reset_rr(AR91XX_RESET_REG_RESET_MODULE); ++ ar71xx_reset_wr(AR91XX_RESET_REG_RESET_MODULE, t & ~mask); ++ local_irq_restore(flags); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++EXPORT_SYMBOL_GPL(ar71xx_device_start); ++ ++void ar71xx_ddr_flush(u32 reg) ++{ ++ ar71xx_ddr_wr(reg, 1); ++ while ((ar71xx_ddr_rr(reg) & 0x1)); ++ ++ ar71xx_ddr_wr(reg, 1); ++ while ((ar71xx_ddr_rr(reg) & 0x1)); ++} ++EXPORT_SYMBOL_GPL(ar71xx_ddr_flush); ++ +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/devices.c linux-2.6.29.1/arch/mips/ar71xx/devices.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/devices.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/devices.c 2009-04-13 14:27:34.491064431 +0200 +@@ -0,0 +1,675 @@ ++/* ++ * Atheros AR71xx SoC platform devices ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/etherdevice.h> ++#include <linux/platform_device.h> ++#include <linux/serial_8250.h> ++#include <linux/ath9k_platform.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++static u8 ar71xx_mac_base[ETH_ALEN] __initdata; ++ ++/* ++ * OHCI (USB full speed host controller) ++ */ ++static struct resource ar71xx_ohci_resources[] = { ++ [0] = { ++ .start = AR71XX_OHCI_BASE, ++ .end = AR71XX_OHCI_BASE + AR71XX_OHCI_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = AR71XX_MISC_IRQ_OHCI, ++ .end = AR71XX_MISC_IRQ_OHCI, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 ar71xx_ohci_dmamask = DMA_BIT_MASK(32); ++static struct platform_device ar71xx_ohci_device = { ++ .name = "ar71xx-ohci", ++ .id = -1, ++ .resource = ar71xx_ohci_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_ohci_resources), ++ .dev = { ++ .dma_mask = &ar71xx_ohci_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++}; ++ ++/* ++ * EHCI (USB full speed host controller) ++ */ ++static struct resource ar71xx_ehci_resources[] = { ++ [0] = { ++ .start = AR71XX_EHCI_BASE, ++ .end = AR71XX_EHCI_BASE + AR71XX_EHCI_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = AR71XX_CPU_IRQ_USB, ++ .end = AR71XX_CPU_IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++ ++static u64 ar71xx_ehci_dmamask = DMA_BIT_MASK(32); ++static struct ar71xx_ehci_platform_data ar71xx_ehci_data; ++ ++static struct platform_device ar71xx_ehci_device = { ++ .name = "ar71xx-ehci", ++ .id = -1, ++ .resource = ar71xx_ehci_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_ehci_resources), ++ .dev = { ++ .dma_mask = &ar71xx_ehci_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ .platform_data = &ar71xx_ehci_data, ++ }, ++}; ++ ++#define AR71XX_USB_RESET_MASK \ ++ (RESET_MODULE_USB_HOST | RESET_MODULE_USB_PHY \ ++ | RESET_MODULE_USB_OHCI_DLL) ++ ++static void ar71xx_usb_setup(void) ++{ ++ ar71xx_device_stop(AR71XX_USB_RESET_MASK); ++ mdelay(1000); ++ ar71xx_device_start(AR71XX_USB_RESET_MASK); ++ ++ /* Turning on the Buff and Desc swap bits */ ++ ar71xx_usb_ctrl_wr(USB_CTRL_REG_CONFIG, 0xf0000); ++ ++ /* WAR for HW bug. Here it adjusts the duration between two SOFS */ ++ ar71xx_usb_ctrl_wr(USB_CTRL_REG_FLADJ, 0x20c00); ++ ++ mdelay(900); ++} ++ ++static void ar91xx_usb_setup(void) ++{ ++ ar71xx_device_stop(RESET_MODULE_USBSUS_OVERRIDE); ++ mdelay(10); ++ ++ ar71xx_device_start(RESET_MODULE_USB_HOST); ++ mdelay(10); ++ ++ ar71xx_device_start(RESET_MODULE_USB_PHY); ++ mdelay(10); ++} ++ ++void __init ar71xx_add_device_usb(void) ++{ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++ ar71xx_usb_setup(); ++ platform_device_register(&ar71xx_ohci_device); ++ platform_device_register(&ar71xx_ehci_device); ++ break; ++ ++ case AR71XX_SOC_AR9130: ++ case AR71XX_SOC_AR9132: ++ ar91xx_usb_setup(); ++ ar71xx_ehci_data.is_ar91xx = 1; ++ platform_device_register(&ar71xx_ehci_device); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++#ifndef CONFIG_AR71XX_EARLY_SERIAL ++static struct resource ar71xx_uart_resources[] = { ++ { ++ .start = AR71XX_UART_BASE, ++ .end = AR71XX_UART_BASE + AR71XX_UART_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++#define AR71XX_UART_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP) ++static struct plat_serial8250_port ar71xx_uart_data[] = { ++ { ++ .mapbase = AR71XX_UART_BASE, ++ .irq = AR71XX_MISC_IRQ_UART, ++ .flags = AR71XX_UART_FLAGS, ++ .iotype = UPIO_MEM32, ++ .regshift = 2, ++ }, { ++ /* terminating entry */ ++ } ++}; ++ ++static struct platform_device ar71xx_uart_device = { ++ .name = "serial8250", ++ .id = PLAT8250_DEV_PLATFORM, ++ .resource = ar71xx_uart_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_uart_resources), ++ .dev = { ++ .platform_data = ar71xx_uart_data ++ }, ++}; ++ ++void __init ar71xx_add_device_uart(void) ++{ ++ ar71xx_uart_data[0].uartclk = ar71xx_ahb_freq; ++ platform_device_register(&ar71xx_uart_device); ++} ++#endif /* CONFIG_AR71XX_EARLY_SERIAL */ ++ ++static struct resource ar71xx_mdio_resources[] = { ++ { ++ .name = "mdio_base", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_GE0_BASE + 0x20, ++ .end = AR71XX_GE0_BASE + 0x38 - 1, ++ } ++}; ++ ++static struct ag71xx_mdio_platform_data ar71xx_mdio_data = { ++ .phy_mask = 0xffffffff, ++}; ++ ++static struct platform_device ar71xx_mdio_device = { ++ .name = "ag71xx-mdio", ++ .id = -1, ++ .resource = ar71xx_mdio_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_mdio_resources), ++ .dev = { ++ .platform_data = &ar71xx_mdio_data, ++ }, ++}; ++ ++void __init ar71xx_add_device_mdio(u32 phy_mask) ++{ ++ ar71xx_mdio_data.phy_mask = phy_mask; ++ platform_device_register(&ar71xx_mdio_device); ++} ++ ++static void ar71xx_set_pll(u32 cfg_reg, u32 pll_reg, u32 pll_val, u32 shift) ++{ ++ void __iomem *base; ++ u32 t; ++ ++ base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); ++ ++ t = __raw_readl(base + cfg_reg); ++ t &= ~(3 << shift); ++ t |= (2 << shift); ++ __raw_writel(t, base + cfg_reg); ++ udelay(100); ++ ++ __raw_writel(pll_val, base + pll_reg); ++ ++ t |= (3 << shift); ++ __raw_writel(t, base + cfg_reg); ++ udelay(100); ++ ++ t &= ~(3 << shift); ++ __raw_writel(t, base + cfg_reg); ++ udelay(100); ++ ++ printk(KERN_DEBUG "ar71xx: pll_reg %#x: %#x\n", ++ (unsigned int)(base + pll_reg), __raw_readl(base + pll_reg)); ++ ++ iounmap(base); ++} ++ ++static void ar71xx_set_pll_ge0(u32 val) ++{ ++ ar71xx_set_pll(AR71XX_PLL_REG_SEC_CONFIG, AR71XX_PLL_REG_ETH0_INT_CLOCK, ++ val, AR71XX_ETH0_PLL_SHIFT); ++} ++ ++static void ar71xx_set_pll_ge1(u32 val) ++{ ++ ar71xx_set_pll(AR71XX_PLL_REG_SEC_CONFIG, AR71XX_PLL_REG_ETH1_INT_CLOCK, ++ val, AR71XX_ETH1_PLL_SHIFT); ++} ++ ++static void ar91xx_set_pll_ge0(u32 val) ++{ ++ ar71xx_set_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH0_INT_CLOCK, ++ val, AR91XX_ETH0_PLL_SHIFT); ++} ++ ++static void ar91xx_set_pll_ge1(u32 val) ++{ ++ ar71xx_set_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH1_INT_CLOCK, ++ val, AR91XX_ETH1_PLL_SHIFT); ++} ++ ++static void ar71xx_ddr_flush_ge0(void) ++{ ++ ar71xx_ddr_flush(AR71XX_DDR_REG_FLUSH_GE0); ++} ++ ++static void ar71xx_ddr_flush_ge1(void) ++{ ++ ar71xx_ddr_flush(AR71XX_DDR_REG_FLUSH_GE1); ++} ++ ++static void ar91xx_ddr_flush_ge0(void) ++{ ++ ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_GE0); ++} ++ ++static void ar91xx_ddr_flush_ge1(void) ++{ ++ ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_GE1); ++} ++ ++static struct resource ar71xx_eth0_resources[] = { ++ { ++ .name = "mac_base", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_GE0_BASE, ++ .end = AR71XX_GE0_BASE + 0x20 - 1, ++ }, { ++ .name = "mac_base2", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_GE0_BASE + 0x38, ++ .end = AR71XX_GE0_BASE + 0x200 - 1, ++ }, { ++ .name = "mii_ctrl", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_MII_BASE + MII_REG_MII0_CTRL, ++ .end = AR71XX_MII_BASE + MII_REG_MII0_CTRL + 3, ++ }, { ++ .name = "mac_irq", ++ .flags = IORESOURCE_IRQ, ++ .start = AR71XX_CPU_IRQ_GE0, ++ .end = AR71XX_CPU_IRQ_GE0, ++ }, ++}; ++ ++struct ag71xx_platform_data ar71xx_eth0_data = { ++ .reset_bit = RESET_MODULE_GE0_MAC, ++}; ++ ++static struct platform_device ar71xx_eth0_device = { ++ .name = "ag71xx", ++ .id = 0, ++ .resource = ar71xx_eth0_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_eth0_resources), ++ .dev = { ++ .platform_data = &ar71xx_eth0_data, ++ }, ++}; ++ ++static struct resource ar71xx_eth1_resources[] = { ++ { ++ .name = "mac_base", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_GE1_BASE, ++ .end = AR71XX_GE1_BASE + 0x20 - 1, ++ }, { ++ .name = "mac_base2", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_GE1_BASE + 0x38, ++ .end = AR71XX_GE1_BASE + 0x200 - 1, ++ }, { ++ .name = "mii_ctrl", ++ .flags = IORESOURCE_MEM, ++ .start = AR71XX_MII_BASE + MII_REG_MII1_CTRL, ++ .end = AR71XX_MII_BASE + MII_REG_MII1_CTRL + 3, ++ }, { ++ .name = "mac_irq", ++ .flags = IORESOURCE_IRQ, ++ .start = AR71XX_CPU_IRQ_GE1, ++ .end = AR71XX_CPU_IRQ_GE1, ++ }, ++}; ++ ++struct ag71xx_platform_data ar71xx_eth1_data = { ++ .reset_bit = RESET_MODULE_GE1_MAC, ++}; ++ ++static struct platform_device ar71xx_eth1_device = { ++ .name = "ag71xx", ++ .id = 1, ++ .resource = ar71xx_eth1_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_eth1_resources), ++ .dev = { ++ .platform_data = &ar71xx_eth1_data, ++ }, ++}; ++ ++static int ar71xx_eth_instance __initdata; ++void __init ar71xx_add_device_eth(unsigned int id) ++{ ++ struct platform_device *pdev; ++ struct ag71xx_platform_data *pdata; ++ ++ switch (id) { ++ case 0: ++ switch (ar71xx_eth0_data.phy_if_mode) { ++ case PHY_INTERFACE_MODE_MII: ++ ar71xx_eth0_data.mii_if = MII0_CTRL_IF_MII; ++ break; ++ case PHY_INTERFACE_MODE_GMII: ++ ar71xx_eth0_data.mii_if = MII0_CTRL_IF_GMII; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ ar71xx_eth0_data.mii_if = MII0_CTRL_IF_RGMII; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ ar71xx_eth0_data.mii_if = MII0_CTRL_IF_RMII; ++ break; ++ default: ++ printk(KERN_ERR "ar71xx: invalid PHY interface mode " ++ "for eth0\n"); ++ return; ++ } ++ pdev = &ar71xx_eth0_device; ++ break; ++ case 1: ++ switch (ar71xx_eth1_data.phy_if_mode) { ++ case PHY_INTERFACE_MODE_RMII: ++ ar71xx_eth1_data.mii_if = MII1_CTRL_IF_RMII; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ ar71xx_eth1_data.mii_if = MII1_CTRL_IF_RGMII; ++ break; ++ default: ++ printk(KERN_ERR "ar71xx: invalid PHY interface mode " ++ "for eth1\n"); ++ return; ++ } ++ pdev = &ar71xx_eth1_device; ++ break; ++ default: ++ printk(KERN_ERR "ar71xx: invalid ethernet id %d\n", id); ++ return; ++ } ++ ++ pdata = pdev->dev.platform_data; ++ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ pdata->ddr_flush = id ? ar71xx_ddr_flush_ge1 ++ : ar71xx_ddr_flush_ge0; ++ pdata->set_pll = id ? ar71xx_set_pll_ge1 ++ : ar71xx_set_pll_ge0; ++ break; ++ ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++ pdata->ddr_flush = id ? ar71xx_ddr_flush_ge1 ++ : ar71xx_ddr_flush_ge0; ++ pdata->set_pll = id ? ar71xx_set_pll_ge1 ++ : ar71xx_set_pll_ge0; ++ pdata->has_gbit = 1; ++ break; ++ ++ case AR71XX_SOC_AR9130: ++ pdata->ddr_flush = id ? ar91xx_ddr_flush_ge1 ++ : ar91xx_ddr_flush_ge0; ++ pdata->set_pll = id ? ar91xx_set_pll_ge1 ++ : ar91xx_set_pll_ge0; ++ pdata->is_ar91xx = 1; ++ break; ++ ++ case AR71XX_SOC_AR9132: ++ pdata->ddr_flush = id ? ar91xx_ddr_flush_ge1 ++ : ar91xx_ddr_flush_ge0; ++ pdata->set_pll = id ? ar91xx_set_pll_ge1 ++ : ar91xx_set_pll_ge0; ++ pdata->is_ar91xx = 1; ++ pdata->has_gbit = 1; ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ switch (pdata->phy_if_mode) { ++ case PHY_INTERFACE_MODE_GMII: ++ case PHY_INTERFACE_MODE_RGMII: ++ if (!pdata->has_gbit) { ++ printk(KERN_ERR "ar71xx: no gbit available on eth%d\n", ++ id); ++ return; ++ } ++ /* fallthrough */ ++ default: ++ break; ++ } ++ ++ if (is_valid_ether_addr(ar71xx_mac_base)) { ++ memcpy(pdata->mac_addr, ar71xx_mac_base, ETH_ALEN); ++ pdata->mac_addr[5] += ar71xx_eth_instance; ++ } else { ++ random_ether_addr(pdata->mac_addr); ++ printk(KERN_DEBUG ++ "ar71xx: using random MAC address for eth%d\n", ++ ar71xx_eth_instance); ++ } ++ ++ platform_device_register(pdev); ++ ar71xx_eth_instance++; ++} ++ ++static struct resource ar71xx_spi_resources[] = { ++ [0] = { ++ .start = AR71XX_SPI_BASE, ++ .end = AR71XX_SPI_BASE + AR71XX_SPI_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device ar71xx_spi_device = { ++ .name = "ar71xx-spi", ++ .id = -1, ++ .resource = ar71xx_spi_resources, ++ .num_resources = ARRAY_SIZE(ar71xx_spi_resources), ++}; ++ ++void __init ar71xx_add_device_spi(struct ar71xx_spi_platform_data *pdata, ++ struct spi_board_info const *info, ++ unsigned n) ++{ ++ spi_register_board_info(info, n); ++ ar71xx_spi_device.dev.platform_data = pdata; ++ platform_device_register(&ar71xx_spi_device); ++} ++ ++void __init ar71xx_add_device_leds_gpio(int id, unsigned num_leds, ++ struct gpio_led *leds) ++{ ++ struct platform_device *pdev; ++ struct gpio_led_platform_data pdata; ++ struct gpio_led *p; ++ int err; ++ ++ p = kmalloc(num_leds * sizeof(*p), GFP_KERNEL); ++ if (!p) ++ return; ++ ++ memcpy(p, leds, num_leds * sizeof(*p)); ++ ++ pdev = platform_device_alloc("leds-gpio", id); ++ if (!pdev) ++ goto err_free_leds; ++ ++ memset(&pdata, 0, sizeof(pdata)); ++ pdata.num_leds = num_leds; ++ pdata.leds = p; ++ ++ err = platform_device_add_data(pdev, &pdata, sizeof(pdata)); ++ if (err) ++ goto err_put_pdev; ++ ++ err = platform_device_add(pdev); ++ if (err) ++ goto err_put_pdev; ++ ++ return; ++ ++err_put_pdev: ++ platform_device_put(pdev); ++ ++err_free_leds: ++ kfree(p); ++} ++ ++void __init ar71xx_add_device_gpio_buttons(int id, ++ unsigned poll_interval, ++ unsigned nbuttons, ++ struct gpio_button *buttons) ++{ ++ struct platform_device *pdev; ++ struct gpio_buttons_platform_data pdata; ++ struct gpio_button *p; ++ int err; ++ ++ p = kmalloc(nbuttons * sizeof(*p), GFP_KERNEL); ++ if (!p) ++ return; ++ ++ memcpy(p, buttons, nbuttons * sizeof(*p)); ++ ++ pdev = platform_device_alloc("gpio-buttons", id); ++ if (!pdev) ++ goto err_free_buttons; ++ ++ memset(&pdata, 0, sizeof(pdata)); ++ pdata.poll_interval = poll_interval; ++ pdata.nbuttons = nbuttons; ++ pdata.buttons = p; ++ ++ err = platform_device_add_data(pdev, &pdata, sizeof(pdata)); ++ if (err) ++ goto err_put_pdev; ++ ++ ++ err = platform_device_add(pdev); ++ if (err) ++ goto err_put_pdev; ++ ++ return; ++ ++err_put_pdev: ++ platform_device_put(pdev); ++ ++err_free_buttons: ++ kfree(p); ++} ++ ++void __init ar71xx_add_device_wdt(void) ++{ ++ platform_device_register_simple("ar71xx-wdt", -1, NULL, 0); ++} ++ ++void __init ar71xx_set_mac_base(unsigned char *mac) ++{ ++ memcpy(ar71xx_mac_base, mac, ETH_ALEN); ++} ++ ++void __init ar71xx_parse_mac_addr(char *mac_str) ++{ ++ u8 tmp[ETH_ALEN]; ++ int t; ++ ++ t = sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", ++ &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]); ++ ++ if (t != ETH_ALEN) ++ t = sscanf(mac_str, "%02hhx.%02hhx.%02hhx.%02hhx.%02hhx.%02hhx", ++ &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]); ++ ++ if (t == ETH_ALEN) ++ ar71xx_set_mac_base(tmp); ++ else ++ printk(KERN_DEBUG "ar71xx: failed to parse mac address " ++ "\"%s\"\n", mac_str); ++} ++ ++static struct resource ar91xx_wmac_resources[] = { ++ { ++ .start = AR91XX_WMAC_BASE, ++ .end = AR91XX_WMAC_BASE + AR91XX_WMAC_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = AR71XX_CPU_IRQ_WMAC, ++ .end = AR71XX_CPU_IRQ_WMAC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct ath9k_platform_data ar91xx_wmac_data; ++ ++static struct platform_device ar91xx_wmac_device = { ++ .name = "ath9k", ++ .id = -1, ++ .resource = ar91xx_wmac_resources, ++ .num_resources = ARRAY_SIZE(ar91xx_wmac_resources), ++ .dev = { ++ .platform_data = &ar91xx_wmac_data, ++ }, ++}; ++ ++void __init ar91xx_add_device_wmac(void) ++{ ++ u8 *ee = (u8 *) KSEG1ADDR(0x1fff1000); ++ ++ memcpy(ar91xx_wmac_data.eeprom_data, ee, ++ sizeof(ar91xx_wmac_data.eeprom_data)); ++ ++ ar71xx_device_stop(RESET_MODULE_AMBA2WMAC); ++ mdelay(10); ++ ++ ar71xx_device_start(RESET_MODULE_AMBA2WMAC); ++ mdelay(10); ++ ++ platform_device_register(&ar91xx_wmac_device); ++} ++ ++static struct platform_device ar71xx_dsa_switch_device = { ++ .name = "dsa", ++ .id = 0, ++}; ++ ++void __init ar71xx_add_device_dsa(unsigned int id, ++ struct dsa_platform_data *d) ++{ ++ switch (id) { ++ case 0: ++ d->netdev = &ar71xx_eth0_device.dev; ++ break; ++ case 1: ++ d->netdev = &ar71xx_eth1_device.dev; ++ break; ++ default: ++ printk(KERN_ERR ++ "ar71xx: invalid ethernet id %d for DSA switch\n", ++ id); ++ return; ++ } ++ d->mii_bus = &ar71xx_mdio_device.dev; ++ ar71xx_dsa_switch_device.dev.platform_data = d; ++ ++ platform_device_register(&ar71xx_dsa_switch_device); ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/devices.h linux-2.6.29.1/arch/mips/ar71xx/devices.h +--- linux-2.6.29.1.orig/arch/mips/ar71xx/devices.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/devices.h 2009-04-13 14:27:34.491064431 +0200 +@@ -0,0 +1,58 @@ ++/* ++ * Atheros AR71xx SoC device definitions ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef __AR71XX_DEVICES_H ++#define __AR71XX_DEVICES_H ++ ++#include <asm/mach-ar71xx/platform.h> ++ ++#include <linux/leds.h> ++#include <linux/gpio_buttons.h> ++#include <net/dsa.h> ++ ++void ar71xx_add_device_spi(struct ar71xx_spi_platform_data *pdata, ++ struct spi_board_info const *info, ++ unsigned n) __init; ++ ++void ar71xx_set_mac_base(unsigned char *mac) __init; ++void ar71xx_parse_mac_addr(char *mac_str) __init; ++ ++extern struct ag71xx_platform_data ar71xx_eth0_data; ++extern struct ag71xx_platform_data ar71xx_eth1_data; ++void ar71xx_add_device_eth(unsigned int id) __init; ++ ++void ar71xx_add_device_mdio(u32 phy_mask) __init; ++ ++void ar71xx_add_device_leds_gpio(int id, ++ unsigned num_leds, ++ struct gpio_led *leds) __init; ++ ++void ar71xx_add_device_gpio_buttons(int id, ++ unsigned poll_interval, ++ unsigned nbuttons, ++ struct gpio_button *buttons) __init; ++ ++void ar71xx_add_device_usb(void) __init; ++ ++#ifdef CONFIG_AR71XX_EARLY_SERIAL ++static inline void ar71xx_add_device_uart(void) {} ++#else ++void ar71xx_add_device_uart(void) __init; ++#endif ++ ++void ar71xx_add_device_wdt(void) __init; ++ ++void ar91xx_add_device_wmac(void) __init; ++ ++void ar71xx_add_device_dsa(unsigned int id, ++ struct dsa_platform_data *d) __init; ++ ++#endif /* __AR71XX_DEVICES_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/gpio.c linux-2.6.29.1/arch/mips/ar71xx/gpio.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/gpio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/gpio.c 2009-04-13 14:27:34.495064103 +0200 +@@ -0,0 +1,154 @@ ++/* ++ * Atheros AR71xx SoC GPIO API support ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/gpio.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++static DEFINE_SPINLOCK(ar71xx_gpio_lock); ++ ++unsigned long ar71xx_gpio_count; ++EXPORT_SYMBOL(ar71xx_gpio_count); ++ ++void __ar71xx_gpio_set_value(unsigned gpio, int value) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ar71xx_gpio_lock, flags); ++ ++ if (value) ++ ar71xx_gpio_wr(GPIO_REG_SET, (1 << gpio)); ++ else ++ ar71xx_gpio_wr(GPIO_REG_CLEAR, (1 << gpio)); ++ ++ spin_unlock_irqrestore(&ar71xx_gpio_lock, flags); ++} ++EXPORT_SYMBOL(__ar71xx_gpio_set_value); ++ ++int __ar71xx_gpio_get_value(unsigned gpio) ++{ ++ return (ar71xx_gpio_rr(GPIO_REG_IN) & (1 << gpio)) ? 1 : 0; ++} ++EXPORT_SYMBOL(__ar71xx_gpio_get_value); ++ ++static int ar71xx_gpio_get_value(struct gpio_chip *chip, unsigned offset) ++{ ++ return __ar71xx_gpio_get_value(offset); ++} ++ ++static void ar71xx_gpio_set_value(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ __ar71xx_gpio_set_value(offset, value); ++} ++ ++static int ar71xx_gpio_direction_input(struct gpio_chip *chip, ++ unsigned offset) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ar71xx_gpio_lock, flags); ++ ++ ar71xx_gpio_wr(GPIO_REG_OE, ++ ar71xx_gpio_rr(GPIO_REG_OE) & ~(1 << offset)); ++ ++ spin_unlock_irqrestore(&ar71xx_gpio_lock, flags); ++ ++ return 0; ++} ++ ++static int ar71xx_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ar71xx_gpio_lock, flags); ++ ++ if (value) ++ ar71xx_gpio_wr(GPIO_REG_SET, (1 << offset)); ++ else ++ ar71xx_gpio_wr(GPIO_REG_CLEAR, (1 << offset)); ++ ++ ar71xx_gpio_wr(GPIO_REG_OE, ++ ar71xx_gpio_rr(GPIO_REG_OE) | (1 << offset)); ++ ++ spin_unlock_irqrestore(&ar71xx_gpio_lock, flags); ++ ++ return 0; ++} ++ ++static struct gpio_chip ar71xx_gpio_chip = { ++ .label = "ar71xx", ++ .get = ar71xx_gpio_get_value, ++ .set = ar71xx_gpio_set_value, ++ .direction_input = ar71xx_gpio_direction_input, ++ .direction_output = ar71xx_gpio_direction_output, ++ .base = 0, ++ .ngpio = AR71XX_GPIO_COUNT, ++}; ++ ++void ar71xx_gpio_function_enable(u32 mask) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ar71xx_gpio_lock, flags); ++ ++ ar71xx_gpio_wr(GPIO_REG_FUNC, ar71xx_gpio_rr(GPIO_REG_FUNC) | mask); ++ ++ spin_unlock_irqrestore(&ar71xx_gpio_lock, flags); ++} ++ ++void ar71xx_gpio_function_disable(u32 mask) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ar71xx_gpio_lock, flags); ++ ++ ar71xx_gpio_wr(GPIO_REG_FUNC, ar71xx_gpio_rr(GPIO_REG_FUNC) & ~mask); ++ ++ spin_unlock_irqrestore(&ar71xx_gpio_lock, flags); ++} ++ ++void __init ar71xx_gpio_init(void) ++{ ++ int err; ++ ++ if (!request_mem_region(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE, ++ "AR71xx GPIO controller")) ++ panic("cannot allocate AR71xx GPIO registers page"); ++ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++ ar71xx_gpio_chip.ngpio = AR71XX_GPIO_COUNT; ++ break; ++ ++ case AR71XX_SOC_AR9130: ++ case AR71XX_SOC_AR9132: ++ ar71xx_gpio_chip.ngpio = AR91XX_GPIO_COUNT; ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ err = gpiochip_add(&ar71xx_gpio_chip); ++ if (err) ++ panic("cannot add AR71xx GPIO chip, error=%d", err); ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/irq.c linux-2.6.29.1/arch/mips/ar71xx/irq.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/irq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/irq.c 2009-04-13 14:27:34.543070220 +0200 +@@ -0,0 +1,302 @@ ++/* ++ * Atheros AR71xx SoC specific interrupt handling ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++ ++#include <asm/irq_cpu.h> ++#include <asm/mipsregs.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#ifdef CONFIG_PCI ++static void ar71xx_pci_irq_dispatch(void) ++{ ++ u32 pending; ++ ++ pending = ar71xx_reset_rr(AR71XX_RESET_REG_PCI_INT_STATUS) & ++ ar71xx_reset_rr(AR71XX_RESET_REG_PCI_INT_ENABLE); ++ ++ if (pending & PCI_INT_DEV0) ++ do_IRQ(AR71XX_PCI_IRQ_DEV0); ++ ++ else if (pending & PCI_INT_DEV1) ++ do_IRQ(AR71XX_PCI_IRQ_DEV1); ++ ++ else if (pending & PCI_INT_DEV2) ++ do_IRQ(AR71XX_PCI_IRQ_DEV2); ++ ++ else ++ spurious_interrupt(); ++} ++ ++static void ar71xx_pci_irq_unmask(unsigned int irq) ++{ ++ irq -= AR71XX_PCI_IRQ_BASE; ++ ar71xx_reset_wr(AR71XX_RESET_REG_PCI_INT_ENABLE, ++ ar71xx_reset_rr(AR71XX_RESET_REG_PCI_INT_ENABLE) | (1 << irq)); ++} ++ ++static void ar71xx_pci_irq_mask(unsigned int irq) ++{ ++ irq -= AR71XX_PCI_IRQ_BASE; ++ ar71xx_reset_wr(AR71XX_RESET_REG_PCI_INT_ENABLE, ++ ar71xx_reset_rr(AR71XX_RESET_REG_PCI_INT_ENABLE) & ~(1 << irq)); ++} ++ ++static struct irq_chip ar71xx_pci_irq_chip = { ++ .name = "AR71XX PCI ", ++ .mask = ar71xx_pci_irq_mask, ++ .unmask = ar71xx_pci_irq_unmask, ++ .mask_ack = ar71xx_pci_irq_mask, ++}; ++ ++static struct irqaction ar71xx_pci_irqaction = { ++ .handler = no_action, ++ .name = "cascade [AR71XX PCI]", ++}; ++ ++static void __init ar71xx_pci_irq_init(void) ++{ ++ int i; ++ ++ ar71xx_reset_wr(AR71XX_RESET_REG_PCI_INT_ENABLE, 0); ++ ar71xx_reset_wr(AR71XX_RESET_REG_PCI_INT_STATUS, 0); ++ ++ for (i = AR71XX_PCI_IRQ_BASE; ++ i < AR71XX_PCI_IRQ_BASE + AR71XX_PCI_IRQ_COUNT; i++) { ++ irq_desc[i].status = IRQ_DISABLED; ++ set_irq_chip_and_handler(i, &ar71xx_pci_irq_chip, ++ handle_level_irq); ++ } ++ ++ setup_irq(AR71XX_CPU_IRQ_PCI, &ar71xx_pci_irqaction); ++} ++#endif /* CONFIG_PCI */ ++ ++static void ar71xx_gpio_irq_dispatch(void) ++{ ++ u32 pending; ++ ++ pending = ar71xx_gpio_rr(GPIO_REG_INT_PENDING) ++ & ar71xx_gpio_rr(GPIO_REG_INT_ENABLE); ++ ++ if (pending) ++ do_IRQ(AR71XX_GPIO_IRQ_BASE + fls(pending) - 1); ++ else ++ spurious_interrupt(); ++} ++ ++static void ar71xx_gpio_irq_unmask(unsigned int irq) ++{ ++ irq -= AR71XX_GPIO_IRQ_BASE; ++ ar71xx_gpio_wr(GPIO_REG_INT_ENABLE, ++ ar71xx_gpio_rr(GPIO_REG_INT_ENABLE) | (1 << irq)); ++} ++ ++static void ar71xx_gpio_irq_mask(unsigned int irq) ++{ ++ irq -= AR71XX_GPIO_IRQ_BASE; ++ ar71xx_gpio_wr(GPIO_REG_INT_ENABLE, ++ ar71xx_gpio_rr(GPIO_REG_INT_ENABLE) & ~(1 << irq)); ++} ++ ++#if 0 ++static int ar71xx_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) ++{ ++ /* TODO: implement */ ++ return 0; ++} ++#else ++#define ar71xx_gpio_irq_set_type NULL ++#endif ++ ++struct irq_chip ar71xx_gpio_irq_chip = { ++ .name = "AR71XX GPIO", ++ .unmask = ar71xx_gpio_irq_unmask, ++ .mask = ar71xx_gpio_irq_mask, ++ .mask_ack = ar71xx_gpio_irq_mask, ++ .set_type = ar71xx_gpio_irq_set_type, ++}; ++ ++static struct irqaction ar71xx_gpio_irqaction = { ++ .handler = no_action, ++ .name = "cascade [AR71XX GPIO]", ++}; ++ ++#define GPIO_IRQ_INIT_STATUS (IRQ_LEVEL | IRQ_TYPE_LEVEL_HIGH | IRQ_DISABLED) ++#define GPIO_INT_ALL 0xffff ++ ++static void __init ar71xx_gpio_irq_init(void) ++{ ++ int i; ++ ++ ar71xx_gpio_wr(GPIO_REG_INT_ENABLE, 0); ++ ar71xx_gpio_wr(GPIO_REG_INT_PENDING, 0); ++ ++ /* setup type of all GPIO interrupts to level sensitive */ ++ ar71xx_gpio_wr(GPIO_REG_INT_TYPE, GPIO_INT_ALL); ++ ++ /* setup polarity of all GPIO interrupts to active high */ ++ ar71xx_gpio_wr(GPIO_REG_INT_POLARITY, GPIO_INT_ALL); ++ ++ for (i = AR71XX_GPIO_IRQ_BASE; ++ i < AR71XX_GPIO_IRQ_BASE + AR71XX_GPIO_IRQ_COUNT; i++) { ++ irq_desc[i].status = GPIO_IRQ_INIT_STATUS; ++ set_irq_chip_and_handler(i, &ar71xx_gpio_irq_chip, ++ handle_level_irq); ++ } ++ ++ setup_irq(AR71XX_MISC_IRQ_GPIO, &ar71xx_gpio_irqaction); ++} ++ ++static void ar71xx_misc_irq_dispatch(void) ++{ ++ u32 pending; ++ ++ pending = ar71xx_reset_rr(AR71XX_RESET_REG_MISC_INT_STATUS) ++ & ar71xx_reset_rr(AR71XX_RESET_REG_MISC_INT_ENABLE); ++ ++ if (pending & MISC_INT_UART) ++ do_IRQ(AR71XX_MISC_IRQ_UART); ++ ++ else if (pending & MISC_INT_DMA) ++ do_IRQ(AR71XX_MISC_IRQ_DMA); ++ ++ else if (pending & MISC_INT_PERFC) ++ do_IRQ(AR71XX_MISC_IRQ_PERFC); ++ ++ else if (pending & MISC_INT_TIMER) ++ do_IRQ(AR71XX_MISC_IRQ_TIMER); ++ ++ else if (pending & MISC_INT_OHCI) ++ do_IRQ(AR71XX_MISC_IRQ_OHCI); ++ ++ else if (pending & MISC_INT_ERROR) ++ do_IRQ(AR71XX_MISC_IRQ_ERROR); ++ ++ else if (pending & MISC_INT_GPIO) ++ ar71xx_gpio_irq_dispatch(); ++ ++ else if (pending & MISC_INT_WDOG) ++ do_IRQ(AR71XX_MISC_IRQ_WDOG); ++ ++ else ++ spurious_interrupt(); ++} ++ ++static void ar71xx_misc_irq_unmask(unsigned int irq) ++{ ++ irq -= AR71XX_MISC_IRQ_BASE; ++ ar71xx_reset_wr(AR71XX_RESET_REG_MISC_INT_ENABLE, ++ ar71xx_reset_rr(AR71XX_RESET_REG_MISC_INT_ENABLE) | (1 << irq)); ++} ++ ++static void ar71xx_misc_irq_mask(unsigned int irq) ++{ ++ irq -= AR71XX_MISC_IRQ_BASE; ++ ar71xx_reset_wr(AR71XX_RESET_REG_MISC_INT_ENABLE, ++ ar71xx_reset_rr(AR71XX_RESET_REG_MISC_INT_ENABLE) & ~(1 << irq)); ++} ++ ++struct irq_chip ar71xx_misc_irq_chip = { ++ .name = "AR71XX MISC", ++ .unmask = ar71xx_misc_irq_unmask, ++ .mask = ar71xx_misc_irq_mask, ++ .mask_ack = ar71xx_misc_irq_mask, ++}; ++ ++static struct irqaction ar71xx_misc_irqaction = { ++ .handler = no_action, ++ .name = "cascade [AR71XX MISC]", ++}; ++ ++static void __init ar71xx_misc_irq_init(void) ++{ ++ int i; ++ ++ ar71xx_reset_wr(AR71XX_RESET_REG_MISC_INT_ENABLE, 0); ++ ar71xx_reset_wr(AR71XX_RESET_REG_MISC_INT_STATUS, 0); ++ ++ for (i = AR71XX_MISC_IRQ_BASE; ++ i < AR71XX_MISC_IRQ_BASE + AR71XX_MISC_IRQ_COUNT; i++) { ++ irq_desc[i].status = IRQ_DISABLED; ++ set_irq_chip_and_handler(i, &ar71xx_misc_irq_chip, ++ handle_level_irq); ++ } ++ ++ setup_irq(AR71XX_CPU_IRQ_MISC, &ar71xx_misc_irqaction); ++} ++ ++static void ar913x_wmac_irq_dispatch(void) ++{ ++ do_IRQ(AR71XX_CPU_IRQ_WMAC); ++} ++ ++static void (* ar71xx_ip2_irq_handler)(void) = spurious_interrupt; ++ ++asmlinkage void plat_irq_dispatch(void) ++{ ++ unsigned long pending; ++ ++ pending = read_c0_status() & read_c0_cause() & ST0_IM; ++ ++ if (pending & STATUSF_IP7) ++ do_IRQ(AR71XX_CPU_IRQ_TIMER); ++ ++ else if (pending & STATUSF_IP2) ++ ar71xx_ip2_irq_handler(); ++ ++ else if (pending & STATUSF_IP4) ++ do_IRQ(AR71XX_CPU_IRQ_GE0); ++ ++ else if (pending & STATUSF_IP5) ++ do_IRQ(AR71XX_CPU_IRQ_GE1); ++ ++ else if (pending & STATUSF_IP3) ++ do_IRQ(AR71XX_CPU_IRQ_USB); ++ ++ else if (pending & STATUSF_IP6) ++ ar71xx_misc_irq_dispatch(); ++ ++ else ++ spurious_interrupt(); ++} ++ ++void __init arch_init_irq(void) ++{ ++ mips_cpu_irq_init(); ++ ++ ar71xx_misc_irq_init(); ++ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++#ifdef CONFIG_PCI ++ ar71xx_pci_irq_init(); ++ ar71xx_ip2_irq_handler = ar71xx_pci_irq_dispatch; ++#endif ++ break; ++ case AR71XX_SOC_AR9130: ++ case AR71XX_SOC_AR9132: ++ ar71xx_ip2_irq_handler = ar913x_wmac_irq_dispatch; ++ break; ++ default: ++ BUG(); ++ } ++ ++ ar71xx_gpio_irq_init(); ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-ap81.c linux-2.6.29.1/arch/mips/ar71xx/mach-ap81.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-ap81.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-ap81.c 2009-04-13 14:27:34.543070220 +0200 +@@ -0,0 +1,148 @@ ++/* ++ * Atheros AP81 board support ++ * ++ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2009 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#define AP81_GPIO_LED_STATUS 1 ++#define AP81_GPIO_LED_AOSS 3 ++#define AP81_GPIO_LED_WLAN 6 ++#define AP81_GPIO_LED_POWER 14 ++ ++#define AP81_GPIO_BTN_SW4 12 ++#define AP81_GPIO_BTN_SW1 21 ++ ++#define AP81_BUTTONS_POLL_INTERVAL 20 ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition ap81_partitions[] = { ++ { ++ .name = "u-boot", ++ .offset = 0, ++ .size = 0x040000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "u-boot-env", ++ .offset = 0x040000, ++ .size = 0x010000, ++ } , { ++ .name = "rootfs", ++ .offset = 0x050000, ++ .size = 0x500000, ++ } , { ++ .name = "uImage", ++ .offset = 0x550000, ++ .size = 0x100000, ++ } , { ++ .name = "ART", ++ .offset = 0x650000, ++ .size = 0x1b0000, ++ .mask_flags = MTD_WRITEABLE, ++ } ++}; ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static struct flash_platform_data ap81_flash_data = { ++#ifdef CONFIG_MTD_PARTITIONS ++ .parts = ap81_partitions, ++ .nr_parts = ARRAY_SIZE(ap81_partitions), ++#endif ++}; ++ ++static struct spi_board_info ap81_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &ap81_flash_data, ++ } ++}; ++ ++static struct gpio_led ap81_leds_gpio[] __initdata = { ++ { ++ .name = "ap81:green:status", ++ .gpio = AP81_GPIO_LED_STATUS, ++ .active_low = 1, ++ }, { ++ .name = "ap81:amber:aoss", ++ .gpio = AP81_GPIO_LED_AOSS, ++ .active_low = 1, ++ }, { ++ .name = "ap81:green:wlan", ++ .gpio = AP81_GPIO_LED_WLAN, ++ .active_low = 1, ++ }, { ++ .name = "ap81:green:power", ++ .gpio = AP81_GPIO_LED_POWER, ++ .active_low = 1, ++ } ++}; ++ ++static struct gpio_button ap81_gpio_buttons[] __initdata = { ++ { ++ .desc = "sw1", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = AP81_GPIO_BTN_SW1, ++ .active_low = 1, ++ } , { ++ .desc = "sw4", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = AP81_GPIO_BTN_SW4, ++ .active_low = 1, ++ } ++}; ++ ++static void __init ap81_setup(void) ++{ ++ ar71xx_add_device_mdio(0x0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth0_data.phy_mask = 0xf; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ar71xx_eth0_data.has_ar8216 = 1; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x10; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_usb(); ++ ++ ar71xx_add_device_spi(NULL, ap81_spi_info, ++ ARRAY_SIZE(ap81_spi_info)); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(ap81_leds_gpio), ++ ap81_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, AP81_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(ap81_gpio_buttons), ++ ap81_gpio_buttons); ++ ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_AP81, "Atheros AP81", ap81_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-ap83.c linux-2.6.29.1/arch/mips/ar71xx/mach-ap83.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-ap83.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-ap83.c 2009-04-13 14:27:34.547067937 +0200 +@@ -0,0 +1,87 @@ ++/* ++ * Atheros AP83 board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#define AP83_GPIO_LED_WLAN 6 ++#define AP83_GPIO_LED_POWER 14 ++#define AP83_GPIO_LED_JUMPSTART 15 ++#define AP83_GPIO_BTN_JUMPSTART 12 ++#define AP83_GPIO_BTN_RESET 21 ++ ++static struct gpio_led ap83_leds_gpio[] __initdata = { ++ { ++ .name = "ap83:green:jumpstart", ++ .gpio = AP83_GPIO_LED_JUMPSTART, ++ .active_low = 0, ++ }, { ++ .name = "ap83:green:power", ++ .gpio = AP83_GPIO_LED_POWER, ++ .active_low = 0, ++ }, { ++ .name = "ap83:green:wlan", ++ .gpio = AP83_GPIO_LED_WLAN, ++ .active_low = 0, ++ }, ++}; ++ ++static struct gpio_button ap83_gpio_buttons[] __initdata = { ++ { ++ .desc = "soft_reset", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = AP83_GPIO_BTN_RESET, ++ .active_low = 1, ++ } , { ++ .desc = "jumpstart", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = AP83_GPIO_BTN_JUMPSTART, ++ .active_low = 1, ++ } ++}; ++ ++static void __init ap83_setup(void) ++{ ++ ar71xx_add_device_mdio(0xfffffffe); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; ++ ar71xx_eth0_data.phy_mask = 0x1; ++ ++ ar71xx_add_device_eth(0); ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; ++ ar71xx_eth1_data.phy_mask = 0x0; ++ ar71xx_eth1_data.speed = SPEED_1000; ++ ar71xx_eth1_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(ap83_leds_gpio), ++ ap83_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, 20, ARRAY_SIZE(ap83_gpio_buttons), ++ ap83_gpio_buttons); ++ ++ ar71xx_add_device_usb(); ++ ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_AP83, "Atheros AP83", ap83_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-aw-nr580.c linux-2.6.29.1/arch/mips/ar71xx/mach-aw-nr580.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-aw-nr580.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-aw-nr580.c 2009-04-13 14:27:34.547067937 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * AzureWave AW-NR580 board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#include "devices.h" ++ ++#define AW_NR580_GPIO_LED_READY_RED 0 ++#define AW_NR580_GPIO_LED_WLAN 1 ++#define AW_NR580_GPIO_LED_READY_GREEN 2 ++#define AW_NR580_GPIO_LED_WPS_GREEN 4 ++#define AW_NR580_GPIO_LED_WPS_AMBER 5 ++ ++#define AW_NR580_GPIO_BTN_WPS 3 ++#define AW_NR580_GPIO_BTN_RESET 11 ++ ++#define AW_NR580_BUTTONS_POLL_INTERVAL 20 ++ ++static struct spi_board_info aw_nr580_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ } ++}; ++ ++static struct gpio_led aw_nr580_leds_gpio[] __initdata = { ++ { ++ .name = "aw-nr580:red:ready", ++ .gpio = AW_NR580_GPIO_LED_READY_RED, ++ .active_low = 0, ++ }, { ++ .name = "aw-nr580:green:ready", ++ .gpio = AW_NR580_GPIO_LED_READY_GREEN, ++ .active_low = 0, ++ }, { ++ .name = "aw-nr580:green:wps", ++ .gpio = AW_NR580_GPIO_LED_WPS_GREEN, ++ .active_low = 0, ++ }, { ++ .name = "aw-nr580:amber:wps", ++ .gpio = AW_NR580_GPIO_LED_WPS_AMBER, ++ .active_low = 0, ++ }, { ++ .name = "aw-nr580:green:wlan", ++ .gpio = AW_NR580_GPIO_LED_WLAN, ++ .active_low = 0, ++ } ++}; ++ ++static struct gpio_button aw_nr580_gpio_buttons[] __initdata = { ++ { ++ .desc = "reset", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = AW_NR580_GPIO_BTN_RESET, ++ .active_low = 1, ++ }, { ++ .desc = "wps", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = AW_NR580_GPIO_BTN_WPS, ++ .active_low = 1, ++ } ++}; ++ ++static struct ar71xx_pci_irq aw_nr580_pci_irqs[] __initdata = { ++ { ++ .slot = 1, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV1, ++ } ++}; ++ ++static void __init aw_nr580_setup(void) ++{ ++ ar71xx_add_device_mdio(0x0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = 0xf; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_add_device_eth(0); ++ ++ ar71xx_pci_init(ARRAY_SIZE(aw_nr580_pci_irqs), aw_nr580_pci_irqs); ++ ++ ar71xx_add_device_spi(NULL, aw_nr580_spi_info, ++ ARRAY_SIZE(aw_nr580_spi_info)); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(aw_nr580_leds_gpio), ++ aw_nr580_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, AW_NR580_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(aw_nr580_gpio_buttons), ++ aw_nr580_gpio_buttons); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_AW_NR580, "AzureWave AW-NR580", aw_nr580_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-generic.c linux-2.6.29.1/arch/mips/ar71xx/mach-generic.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-generic.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-generic.c 2009-04-13 14:27:34.547067937 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * Generic AR71xx machine support ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++static void __init ar71xx_generic_init(void) ++{ ++ /* Nothing to do */ ++} ++ ++MIPS_MACHINE(AR71XX_MACH_GENERIC, "Generic AR71xx board", ar71xx_generic_init); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-mzk-w04nu.c linux-2.6.29.1/arch/mips/ar71xx/mach-mzk-w04nu.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-mzk-w04nu.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-mzk-w04nu.c 2009-04-13 14:27:34.551067888 +0200 +@@ -0,0 +1,173 @@ ++/* ++ * Planex MZK-W04NU board support ++ * ++ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#define MZK_W04NU_GPIO_LED_USB 0 ++#define MZK_W04NU_GPIO_LED_STATUS 1 ++#define MZK_W04NU_GPIO_LED_WPS 3 ++#define MZK_W04NU_GPIO_LED_WLAN 6 ++#define MZK_W04NU_GPIO_LED_AP 15 ++#define MZK_W04NU_GPIO_LED_ROUTER 16 ++ ++#define MZK_W04NU_GPIO_BTN_APROUTER 5 ++#define MZK_W04NU_GPIO_BTN_WPS 12 ++#define MZK_W04NU_GPIO_BTN_RESET 21 ++ ++#define MZK_W04NU_BUTTONS_POLL_INTERVAL 20 ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition mzk_w04nu_partitions[] = { ++ { ++ .name = "u-boot", ++ .offset = 0, ++ .size = 0x040000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "u-boot-env", ++ .offset = 0x040000, ++ .size = 0x010000, ++ } , { ++ .name = "kernel", ++ .offset = 0x050000, ++ .size = 0x160000, ++ } , { ++ .name = "rootfs", ++ .offset = 0x1b0000, ++ .size = 0x630000, ++ } , { ++ .name = "art", ++ .offset = 0x7e0000, ++ .size = 0x020000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "firmware", ++ .offset = 0x050000, ++ .size = 0x770000, ++ } ++}; ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static struct flash_platform_data mzk_w04nu_flash_data = { ++#ifdef CONFIG_MTD_PARTITIONS ++ .parts = mzk_w04nu_partitions, ++ .nr_parts = ARRAY_SIZE(mzk_w04nu_partitions), ++#endif ++}; ++ ++static struct spi_board_info mzk_w04nu_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &mzk_w04nu_flash_data, ++ } ++}; ++ ++static struct gpio_led mzk_w04nu_leds_gpio[] __initdata = { ++ { ++ .name = "mzk-w04nu:green:status", ++ .gpio = MZK_W04NU_GPIO_LED_STATUS, ++ .active_low = 1, ++ }, { ++ .name = "mzk-w04nu:blue:wps", ++ .gpio = MZK_W04NU_GPIO_LED_WPS, ++ .active_low = 1, ++ }, { ++ .name = "mzk-w04nu:green:wlan", ++ .gpio = MZK_W04NU_GPIO_LED_WLAN, ++ .active_low = 1, ++ }, { ++ .name = "mzk-w04nu:green:usb", ++ .gpio = MZK_W04NU_GPIO_LED_USB, ++ .active_low = 1, ++ }, { ++ .name = "mzk-w04nu:green:ap", ++ .gpio = MZK_W04NU_GPIO_LED_AP, ++ .active_low = 1, ++ }, { ++ .name = "mzk-w04nu:green:router", ++ .gpio = MZK_W04NU_GPIO_LED_ROUTER, ++ .active_low = 1, ++ } ++}; ++ ++static struct gpio_button mzk_w04nu_gpio_buttons[] __initdata = { ++ { ++ .desc = "reset", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = MZK_W04NU_GPIO_BTN_RESET, ++ .active_low = 1, ++ }, { ++ .desc = "wps", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = MZK_W04NU_GPIO_BTN_WPS, ++ .active_low = 1, ++ }, { ++ .desc = "aprouter", ++ .type = EV_KEY, ++ .code = BTN_2, ++ .threshold = 5, ++ .gpio = MZK_W04NU_GPIO_BTN_APROUTER, ++ .active_low = 0, ++ } ++}; ++ ++static void __init mzk_w04nu_setup(void) ++{ ++ u8 *mac = (u8 *) KSEG1ADDR(0x1fff1000); ++ ++ ar71xx_set_mac_base(mac); ++ ++ ar71xx_add_device_mdio(0x0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth0_data.phy_mask = 0xf; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ar71xx_eth0_data.has_ar8216 = 1; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x10; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_spi(NULL, mzk_w04nu_spi_info, ++ ARRAY_SIZE(mzk_w04nu_spi_info)); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(mzk_w04nu_leds_gpio), ++ mzk_w04nu_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, MZK_W04NU_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(mzk_w04nu_gpio_buttons), ++ mzk_w04nu_gpio_buttons); ++ ar71xx_add_device_usb(); ++ ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_MZK_W04NU, "Planex MZK-W04NU", mzk_w04nu_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-mzk-w300nh.c linux-2.6.29.1/arch/mips/ar71xx/mach-mzk-w300nh.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-mzk-w300nh.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-mzk-w300nh.c 2009-04-13 14:27:34.551067888 +0200 +@@ -0,0 +1,81 @@ ++/* ++ * Planex MZK-W300NH board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++ ++#include <asm/mips_machine.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition mzk_w300nh_partitions[] = { ++ { ++ .name = "u-boot", ++ .offset = 0, ++ .size = 0x040000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "u-boot-env", ++ .offset = 0x040000, ++ .size = 0x010000, ++ } , { ++ .name = "uImage", ++ .offset = 0x050000, ++ .size = 0x160000, ++ } , { ++ .name = "rootfs", ++ .offset = 0x1b0000, ++ .size = 0x610000, ++ } , { ++ .name = "config", ++ .offset = 0x7c0000, ++ .size = 0x020000, ++ } , { ++ .name = "art", ++ .offset = 0x7e0000, ++ .size = 0x020000, ++ .mask_flags = MTD_WRITEABLE, ++ } ++}; ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static struct flash_platform_data mzk_w300nh_flash_data = { ++#ifdef CONFIG_MTD_PARTITIONS ++ .parts = mzk_w300nh_partitions, ++ .nr_parts = ARRAY_SIZE(mzk_w300nh_partitions), ++#endif ++}; ++ ++static struct spi_board_info mzk_w300nh_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &mzk_w300nh_flash_data, ++ } ++}; ++ ++static void __init mzk_w300nh_setup(void) ++{ ++ ar71xx_add_device_spi(NULL, mzk_w300nh_spi_info, ++ ARRAY_SIZE(mzk_w300nh_spi_info)); ++ ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_MZK_W300NH, "Planex MZK-W300NH", mzk_w300nh_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-pb42.c linux-2.6.29.1/arch/mips/ar71xx/mach-pb42.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-pb42.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-pb42.c 2009-04-13 14:27:34.555068677 +0200 +@@ -0,0 +1,102 @@ ++/* ++ * Atheros PB42 board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/bitops.h> ++#include <linux/input.h> ++#include <linux/platform_device.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#include "devices.h" ++ ++#define PB42_BUTTONS_POLL_INTERVAL 20 ++ ++#define PB42_GPIO_BTN_SW4 8 ++#define PB42_GPIO_BTN_SW5 3 ++ ++static struct spi_board_info pb42_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ } ++}; ++ ++static struct ar71xx_pci_irq pb42_pci_irqs[] __initdata = { ++ { ++ .slot = 0, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV0, ++ }, { ++ .slot = 1, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV1, ++ }, { ++ .slot = 2, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV2, ++ } ++}; ++ ++static struct gpio_button pb42_gpio_buttons[] __initdata = { ++ { ++ .desc = "sw4", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = PB42_GPIO_BTN_SW4, ++ .active_low = 1, ++ } , { ++ .desc = "sw5", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = PB42_GPIO_BTN_SW5, ++ .active_low = 1, ++ } ++}; ++ ++#define PB42_WAN_PHYMASK BIT(20) ++#define PB42_LAN_PHYMASK (BIT(16) | BIT(17) | BIT(18) | BIT(19)) ++#define PB42_MDIO_PHYMASK (PB42_LAN_PHYMASK | PB42_WAN_PHYMASK) ++ ++static void __init pb42_init(void) ++{ ++ ar71xx_add_device_spi(NULL, pb42_spi_info, ++ ARRAY_SIZE(pb42_spi_info)); ++ ++ ar71xx_add_device_mdio(~PB42_MDIO_PHYMASK); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = PB42_WAN_PHYMASK; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = PB42_LAN_PHYMASK; ++ ar71xx_eth1_data.speed = SPEED_100; ++ ar71xx_eth1_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_gpio_buttons(-1, PB42_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(pb42_gpio_buttons), ++ pb42_gpio_buttons); ++ ++ ar71xx_pci_init(ARRAY_SIZE(pb42_pci_irqs), pb42_pci_irqs); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_PB42, "Atheros PB42", pb42_init); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-rb-4xx.c linux-2.6.29.1/arch/mips/ar71xx/mach-rb-4xx.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-rb-4xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-rb-4xx.c 2009-04-13 14:27:34.555068677 +0200 +@@ -0,0 +1,251 @@ ++/* ++ * MikroTik RouterBOARD 4xx series support ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/irq.h> ++#include <linux/mmc/host.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/spi/mmc_spi.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#include "devices.h" ++ ++#define RB4XX_GPIO_USER_LED 4 ++#define RB4XX_GPIO_RESET_SWITCH 7 ++ ++#define RB4XX_BUTTONS_POLL_INTERVAL 20 ++ ++static struct gpio_led rb4xx_leds_gpio[] __initdata = { ++ { ++ .name = "rb4xx:yellow:user", ++ .gpio = RB4XX_GPIO_USER_LED, ++ .active_low = 0, ++ }, ++}; ++ ++static struct gpio_button rb4xx_gpio_buttons[] __initdata = { ++ { ++ .desc = "reset_switch", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = RB4XX_GPIO_RESET_SWITCH, ++ .active_low = 1, ++ } ++}; ++ ++static struct platform_device rb4xx_nand_device = { ++ .name = "rb4xx-nand", ++ .id = -1, ++}; ++ ++static struct ar71xx_pci_irq rb4xx_pci_irqs[] __initdata = { ++ { ++ .slot = 1, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV0, ++ }, { ++ .slot = 1, ++ .pin = 2, ++ .irq = AR71XX_PCI_IRQ_DEV1, ++ }, { ++ .slot = 2, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV1, ++ }, { ++ .slot = 3, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV2, ++ } ++}; ++ ++#if 0 ++/* ++ * SPI device support is experimental ++ */ ++static struct flash_platform_data rb4xx_flash_data = { ++ .type = "pm25lv512", ++}; ++ ++static struct spi_board_info rb4xx_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &rb4xx_flash_data, ++ } ++}; ++ ++static struct mmc_spi_platform_data rb433_mmc_data = { ++ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, ++}; ++ ++static struct spi_board_info rb433_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &rb433_flash_data, ++ }, { ++ .bus_num = 0, ++ .chip_select = 2, ++ .max_speed_hz = 25000000, ++ .modalias = "mmc_spi", ++ .platform_data = &rb433_mmc_data, ++ } ++}; ++ ++static u32 rb433_spi_get_ioc_base(u8 chip_select, int cs_high, int is_on) ++{ ++ u32 ret; ++ ++ if (is_on == AR71XX_SPI_CS_INACTIVE) { ++ ret = SPI_IOC_CS0 | SPI_IOC_CS1; ++ } else { ++ if (cs_high) { ++ ret = SPI_IOC_CS0 | SPI_IOC_CS1; ++ } else { ++ if ((chip_select ^ 2) == 0) ++ ret = SPI_IOC_CS1 ^ (SPI_IOC_CS0 | SPI_IOC_CS1); ++ else ++ ret = SPI_IOC_CS0 ^ (SPI_IOC_CS0 | SPI_IOC_CS1); ++ } ++ } ++ ++ return ret; ++} ++ ++struct ar71xx_spi_platform_data rb433_spi_data = { ++ .bus_num = 0, ++ .num_chipselect = 3, ++ .get_ioc_base = rb433_spi_get_ioc_base, ++}; ++ ++static void rb4xx_add_device_spi(void) ++{ ++ ar71xx_add_device_spi(NULL, rb4xx_spi_info, ARRAY_SIZE(rb4xx_spi_info)); ++} ++ ++static void rb433_add_device_spi(void) ++{ ++ ar71xx_add_device_spi(&rb433_spi_data, rb433_spi_info, ++ ARRAY_SIZE(rb433_spi_info)); ++} ++#else ++static inline void rb4xx_add_device_spi(void) {} ++static inline void rb433_add_device_spi(void) {} ++#endif ++ ++static void __init rb4xx_generic_setup(void) ++{ ++ ar71xx_gpio_function_enable(GPIO_FUNC_SPI_CS1_EN | ++ GPIO_FUNC_SPI_CS2_EN); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(rb4xx_leds_gpio), ++ rb4xx_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, RB4XX_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(rb4xx_gpio_buttons), ++ rb4xx_gpio_buttons); ++ ++ platform_device_register(&rb4xx_nand_device); ++} ++ ++static void __init rb411_setup(void) ++{ ++ rb4xx_generic_setup(); ++ rb4xx_add_device_spi(); ++ ++ ar71xx_add_device_mdio(0xfffffffe); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = 0x00000001; ++ ++ ar71xx_add_device_eth(0); ++ ++ ar71xx_pci_init(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_RB_411, "MikroTik RouterBOARD 411/A/AH", rb411_setup); ++ ++static void __init rb433_setup(void) ++{ ++ rb4xx_generic_setup(); ++ rb433_add_device_spi(); ++ ++ ar71xx_add_device_mdio(0xffffffe9); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = 0x00000006; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x00000010; ++ ++ ar71xx_add_device_eth(1); ++ ar71xx_add_device_eth(0); ++ ++ ar71xx_pci_init(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_RB_433, "MikroTik RouterBOARD 433/AH", rb433_setup); ++ ++static void __init rb450_setup(void) ++{ ++ rb4xx_generic_setup(); ++ rb4xx_add_device_spi(); ++ ++ ar71xx_add_device_mdio(0xffffffe0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = 0x0000000f; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x00000010; ++ ++ ar71xx_add_device_eth(1); ++ ar71xx_add_device_eth(0); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_RB_450, "MikroTik RouterBOARD 450", rb450_setup); ++ ++static void __init rb493_setup(void) ++{ ++ rb4xx_generic_setup(); ++ rb4xx_add_device_spi(); ++ ++ ar71xx_add_device_mdio(0x3fffff00); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = 0; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x00000001; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_pci_init(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_RB_493, "MikroTik RouterBOARD 493/AH", rb493_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-tew-632brp.c linux-2.6.29.1/arch/mips/ar71xx/mach-tew-632brp.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-tew-632brp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-tew-632brp.c 2009-04-13 14:27:34.559070024 +0200 +@@ -0,0 +1,142 @@ ++/* ++ * TrendNET TEW-632BRP board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#define TEW_632BRP_GPIO_LED_STATUS 1 ++#define TEW_632BRP_GPIO_LED_WPS 3 ++#define TEW_632BRP_GPIO_LED_WLAN 6 ++#define TEW_632BRP_GPIO_BTN_WPS 12 ++#define TEW_632BRP_GPIO_BTN_RESET 21 ++ ++#define TEW_632BRP_BUTTONS_POLL_INTERVAL 20 ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition tew_632brp_partitions[] = { ++ { ++ .name = "u-boot", ++ .offset = 0, ++ .size = 0x020000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "config", ++ .offset = 0x020000, ++ .size = 0x010000, ++ } , { ++ .name = "kernel", ++ .offset = 0x030000, ++ .size = 0x0c0000, ++ } , { ++ .name = "rootfs", ++ .offset = 0x0f0000, ++ .size = 0x300000, ++ } , { ++ .name = "art", ++ .offset = 0x3f0000, ++ .size = 0x010000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "firmware", ++ .offset = 0x030000, ++ .size = 0x3c0000, ++ } ++}; ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static struct flash_platform_data tew_632brp_flash_data = { ++#ifdef CONFIG_MTD_PARTITIONS ++ .parts = tew_632brp_partitions, ++ .nr_parts = ARRAY_SIZE(tew_632brp_partitions), ++#endif ++}; ++ ++static struct spi_board_info tew_632brp_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &tew_632brp_flash_data, ++ } ++}; ++ ++static struct gpio_led tew_632brp_leds_gpio[] __initdata = { ++ { ++ .name = "tew-632brp:green:status", ++ .gpio = TEW_632BRP_GPIO_LED_STATUS, ++ .active_low = 1, ++ }, { ++ .name = "tew-632brp:blue:wps", ++ .gpio = TEW_632BRP_GPIO_LED_WPS, ++ .active_low = 1, ++ }, { ++ .name = "tew-632brp:green:wlan", ++ .gpio = TEW_632BRP_GPIO_LED_WLAN, ++ .active_low = 1, ++ } ++}; ++ ++static struct gpio_button tew_632brp_gpio_buttons[] __initdata = { ++ { ++ .desc = "reset", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = TEW_632BRP_GPIO_BTN_RESET, ++ }, { ++ .desc = "wps", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = TEW_632BRP_GPIO_BTN_WPS, ++ } ++}; ++ ++static void __init tew_632brp_setup(void) ++{ ++ ar71xx_add_device_mdio(0x0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth0_data.phy_mask = 0xf; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x10; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_spi(NULL, tew_632brp_spi_info, ++ ARRAY_SIZE(tew_632brp_spi_info)); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(tew_632brp_leds_gpio), ++ tew_632brp_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, TEW_632BRP_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(tew_632brp_gpio_buttons), ++ tew_632brp_gpio_buttons); ++ ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_TEW_632BRP, "TRENDnet TEW-632BRP", tew_632brp_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-tl-wr941nd.c linux-2.6.29.1/arch/mips/ar71xx/mach-tl-wr941nd.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-tl-wr941nd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-tl-wr941nd.c 2009-04-13 14:27:34.591071588 +0200 +@@ -0,0 +1,144 @@ ++/* ++ * TP-LINK TL-WR941ND board support ++ * ++ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#define TL_WR941ND_GPIO_LED_SYSTEM 2 ++#define TL_WR941ND_GPIO_LED_QSS 5 ++ ++#define TL_WR941ND_GPIO_BTN_RESET 3 ++#define TL_WR941ND_GPIO_BTN_QSS 7 ++ ++#define TL_WR941ND_BUTTONS_POLL_INTERVAL 20 ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition tl_wr941nd_partitions[] = { ++ { ++ .name = "u-boot", ++ .offset = 0, ++ .size = 0x020000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "kernel", ++ .offset = 0x020000, ++ .size = 0x120000, ++ } , { ++ .name = "rootfs", ++ .offset = 0x140000, ++ .size = 0x2b0000, ++ } , { ++ .name = "art", ++ .offset = 0x3f0000, ++ .size = 0x010000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "firmware", ++ .offset = 0x020000, ++ .size = 0x3d0000, ++ } ++}; ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static struct flash_platform_data tl_wr941nd_flash_data = { ++#ifdef CONFIG_MTD_PARTITIONS ++ .parts = tl_wr941nd_partitions, ++ .nr_parts = ARRAY_SIZE(tl_wr941nd_partitions), ++#endif ++}; ++ ++static struct spi_board_info tl_wr941nd_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &tl_wr941nd_flash_data, ++ } ++}; ++ ++static struct gpio_led tl_wr941nd_leds_gpio[] __initdata = { ++ { ++ .name = "tl-wr941nd:green:system", ++ .gpio = TL_WR941ND_GPIO_LED_SYSTEM, ++ .active_low = 1, ++ }, { ++ .name = "tl-wr941nd:red:qss", ++ .gpio = TL_WR941ND_GPIO_LED_QSS, ++ .active_low = 1, ++ } ++}; ++ ++static struct gpio_button tl_wr941nd_gpio_buttons[] __initdata = { ++ { ++ .desc = "reset", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = TL_WR941ND_GPIO_BTN_RESET, ++ .active_low = 1, ++ }, { ++ .desc = "qss", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = TL_WR941ND_GPIO_BTN_QSS, ++ .active_low = 1, ++ } ++}; ++ ++static struct dsa_platform_data tl_wr941nd_dsa_data = { ++ .port_names[0] = "wan", ++ .port_names[1] = "lan1", ++ .port_names[2] = "lan2", ++ .port_names[3] = "lan3", ++ .port_names[4] = "lan4", ++ .port_names[5] = "cpu", ++}; ++ ++static void __init tl_wr941nd_setup(void) ++{ ++ u8 *mac = (u8 *) KSEG1ADDR(0x1f01fc00); ++ ++ ar71xx_set_mac_base(mac); ++ ++ ar71xx_add_device_mdio(0x0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth0_data.phy_mask = 0x0; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_dsa(0, &tl_wr941nd_dsa_data); ++ ++ ar71xx_add_device_spi(NULL, tl_wr941nd_spi_info, ++ ARRAY_SIZE(tl_wr941nd_spi_info)); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(tl_wr941nd_leds_gpio), ++ tl_wr941nd_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, TL_WR941ND_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(tl_wr941nd_gpio_buttons), ++ tl_wr941nd_gpio_buttons); ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_TL_WR941ND, "TP-LINK TL-WR941ND", tl_wr941nd_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-ubnt.c linux-2.6.29.1/arch/mips/ar71xx/mach-ubnt.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-ubnt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-ubnt.c 2009-04-13 14:27:34.591071588 +0200 +@@ -0,0 +1,188 @@ ++/* ++ * Ubiquiti RouterStation support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * Copyright (C) 2008 Ubiquiti <support@ubnt.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#include "devices.h" ++ ++#define UBNT_RS_GPIO_LED_RF 2 ++#define UBNT_RS_GPIO_SW4 8 ++ ++#define UBNT_LS_SR71_GPIO_LED_D25 0 ++#define UBNT_LS_SR71_GPIO_LED_D26 1 ++#define UBNT_LS_SR71_GPIO_LED_D24 2 ++#define UBNT_LS_SR71_GPIO_LED_D23 4 ++#define UBNT_LS_SR71_GPIO_LED_D22 5 ++#define UBNT_LS_SR71_GPIO_LED_D27 6 ++#define UBNT_LS_SR71_GPIO_LED_D28 7 ++ ++#define UBNT_BUTTONS_POLL_INTERVAL 20 ++ ++static struct spi_board_info ubnt_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ } ++}; ++ ++static struct ar71xx_pci_irq ubnt_pci_irqs[] __initdata = { ++ { ++ .slot = 0, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV0, ++ }, { ++ .slot = 1, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV1, ++ }, { ++ .slot = 2, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV2, ++ } ++}; ++ ++static struct gpio_led ubnt_rs_leds_gpio[] __initdata = { ++ { ++ .name = "ubnt:green:rf", ++ .gpio = UBNT_RS_GPIO_LED_RF, ++ .active_low = 0, ++ } ++}; ++ ++static struct gpio_led ubnt_ls_sr71_leds_gpio[] __initdata = { ++ { ++ .name = "ubnt:green:d22", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D22, ++ .active_low = 0, ++ }, { ++ .name = "ubnt:green:d23", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D23, ++ .active_low = 0, ++ }, { ++ .name = "ubnt:green:d24", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D24, ++ .active_low = 0, ++ }, { ++ .name = "ubnt:red:d25", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D25, ++ .active_low = 0, ++ }, { ++ .name = "ubnt:red:d26", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D26, ++ .active_low = 0, ++ }, { ++ .name = "ubnt:green:d27", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D27, ++ .active_low = 0, ++ }, { ++ .name = "ubnt:green:d28", ++ .gpio = UBNT_LS_SR71_GPIO_LED_D28, ++ .active_low = 0, ++ } ++}; ++ ++static struct gpio_button ubnt_gpio_buttons[] __initdata = { ++ { ++ .desc = "sw4", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = UBNT_RS_GPIO_SW4, ++ .active_low = 1, ++ } ++}; ++ ++static void __init ubnt_generic_setup(void) ++{ ++ ar71xx_add_device_spi(NULL, ubnt_spi_info, ++ ARRAY_SIZE(ubnt_spi_info)); ++ ++ ar71xx_add_device_gpio_buttons(-1, UBNT_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(ubnt_gpio_buttons), ++ ubnt_gpio_buttons); ++ ++ ar71xx_pci_init(ARRAY_SIZE(ubnt_pci_irqs), ubnt_pci_irqs); ++} ++ ++#define UBNT_RS_WAN_PHYMASK (1 << 20) ++#define UBNT_RS_LAN_PHYMASK ((1 << 16) | (1 << 17) | (1 << 18) | (1 << 19)) ++ ++static void __init ubnt_rs_setup(void) ++{ ++ ubnt_generic_setup(); ++ ++ ar71xx_add_device_mdio(~(UBNT_RS_WAN_PHYMASK | UBNT_RS_LAN_PHYMASK)); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = UBNT_RS_WAN_PHYMASK; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = UBNT_RS_LAN_PHYMASK; ++ ++ ar71xx_eth1_data.speed = SPEED_100; ++ ar71xx_eth1_data.duplex = DUPLEX_FULL; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_usb(); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(ubnt_rs_leds_gpio), ++ ubnt_rs_leds_gpio); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_UBNT_RS, "Ubiquiti RouterStation", ubnt_rs_setup); ++ ++static void __init ubnt_rspro_setup(void) ++{ ++ ubnt_generic_setup(); ++ ++ ar71xx_add_device_usb(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_UBNT_RSPRO, "Ubiquiti RouterStation Pro", ++ ubnt_rspro_setup); ++ ++static void __init ubnt_lsx_setup(void) ++{ ++ ubnt_generic_setup(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_UBNT_LSX, "Ubiquiti LSX", ubnt_lsx_setup); ++ ++#define UBNT_LSSR71_PHY_MASK (1 << 1) ++ ++static void __init ubnt_lssr71_setup(void) ++{ ++ ubnt_generic_setup(); ++ ++ ar71xx_add_device_mdio(~UBNT_LSSR71_PHY_MASK); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = UBNT_LSSR71_PHY_MASK; ++ ++ ar71xx_add_device_eth(0); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(ubnt_ls_sr71_leds_gpio), ++ ubnt_ls_sr71_leds_gpio); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_UBNT_LSSR71, "Ubiquiti LS-SR71", ubnt_lssr71_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-wnr2000.c linux-2.6.29.1/arch/mips/ar71xx/mach-wnr2000.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-wnr2000.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-wnr2000.c 2009-04-13 14:27:34.595070701 +0200 +@@ -0,0 +1,158 @@ ++/* ++ * NETGEAR WNR2000 board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * Copyright (C) 2008-2009 Andy Boyett <agb@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++#define WNR2000_GPIO_LED_PWR_GREEN 14 ++#define WNR2000_GPIO_LED_PWR_AMBER 7 ++#define WNR2000_GPIO_LED_WPS 4 ++#define WNR2000_GPIO_LED_WLAN 6 ++#define WNR2000_GPIO_BTN_RESET 21 ++#define WNR2000_GPIO_BTN_WPS 8 ++ ++#define WNR2000_BUTTONS_POLL_INTERVAL 20 ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition wnr2000_partitions[] = { ++ { ++ .name = "u-boot", ++ .offset = 0, ++ .size = 0x040000, ++ .mask_flags = MTD_WRITEABLE, ++ } , { ++ .name = "u-boot-env", ++ .offset = 0x040000, ++ .size = 0x010000, ++ } , { ++ .name = "rootfs", ++ .offset = 0x050000, ++ .size = 0x240000, ++ } , { ++ .name = "user-config", ++ .offset = 0x290000, ++ .size = 0x010000, ++ } , { ++ .name = "uImage", ++ .offset = 0x2a0000, ++ .size = 0x120000, ++ } , { ++ .name = "language_table", ++ .offset = 0x3c0000, ++ .size = 0x020000, ++ } , { ++ .name = "rootfs_checksum", ++ .offset = 0x3e0000, ++ .size = 0x010000, ++ } , { ++ .name = "art", ++ .offset = 0x3f0000, ++ .size = 0x010000, ++ .mask_flags = MTD_WRITEABLE, ++ } ++}; ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++static struct flash_platform_data wnr2000_flash_data = { ++#ifdef CONFIG_MTD_PARTITIONS ++ .parts = wnr2000_partitions, ++ .nr_parts = ARRAY_SIZE(wnr2000_partitions), ++#endif ++}; ++ ++static struct spi_board_info wnr2000_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ .platform_data = &wnr2000_flash_data, ++ } ++}; ++ ++static struct gpio_led wnr2000_leds_gpio[] __initdata = { ++ { ++ .name = "wnr2000:green:power", ++ .gpio = WNR2000_GPIO_LED_PWR_GREEN, ++ .active_low = 1, ++ }, { ++ .name = "wnr2000:amber:power", ++ .gpio = WNR2000_GPIO_LED_PWR_AMBER, ++ .active_low = 1, ++ }, { ++ .name = "wnr2000:green:wps", ++ .gpio = WNR2000_GPIO_LED_WPS, ++ .active_low = 1, ++ }, { ++ .name = "wnr2000:blue:wlan", ++ .gpio = WNR2000_GPIO_LED_WLAN, ++ .active_low = 1, ++ } ++}; ++ ++static struct gpio_button wnr2000_gpio_buttons[] __initdata = { ++ { ++ .desc = "reset", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = WNR2000_GPIO_BTN_RESET, ++ }, { ++ .desc = "wps", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = WNR2000_GPIO_BTN_WPS, ++ } ++}; ++ ++static void __init wnr2000_setup(void) ++{ ++ ar71xx_add_device_mdio(0x0); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth0_data.phy_mask = 0xf; ++ ar71xx_eth0_data.speed = SPEED_100; ++ ar71xx_eth0_data.duplex = DUPLEX_FULL; ++ ar71xx_eth0_data.has_ar8216 = 1; ++ ++ ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ++ ar71xx_eth1_data.phy_mask = 0x10; ++ ++ ar71xx_add_device_eth(0); ++ ar71xx_add_device_eth(1); ++ ++ ar71xx_add_device_spi(NULL, wnr2000_spi_info, ++ ARRAY_SIZE(wnr2000_spi_info)); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(wnr2000_leds_gpio), ++ wnr2000_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, WNR2000_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(wnr2000_gpio_buttons), ++ wnr2000_gpio_buttons); ++ ++ ++ ar91xx_add_device_wmac(); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_WNR2000, "NETGEAR WNR2000", wnr2000_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/mach-wp543.c linux-2.6.29.1/arch/mips/ar71xx/mach-wp543.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/mach-wp543.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/mach-wp543.c 2009-04-13 14:27:34.595070701 +0200 +@@ -0,0 +1,118 @@ ++/* ++ * Compex WP543 board support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++#include <linux/input.h> ++ ++#include <asm/mips_machine.h> ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#include "devices.h" ++ ++#define WP543_GPIO_SW6 2 ++#define WP543_GPIO_LED_1 3 ++#define WP543_GPIO_LED_2 4 ++#define WP543_GPIO_LED_WLAN 5 ++#define WP543_GPIO_LED_CONN 6 ++#define WP543_GPIO_LED_DIAG 7 ++#define WP543_GPIO_SW4 8 ++ ++#define WP543_BUTTONS_POLL_INTERVAL 20 ++ ++static struct spi_board_info wp543_spi_info[] = { ++ { ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 25000000, ++ .modalias = "m25p80", ++ } ++}; ++ ++static struct ar71xx_pci_irq wp543_pci_irqs[] __initdata = { ++ { ++ .slot = 1, ++ .pin = 1, ++ .irq = AR71XX_PCI_IRQ_DEV0, ++ }, { ++ .slot = 1, ++ .pin = 2, ++ .irq = AR71XX_PCI_IRQ_DEV1, ++ } ++}; ++ ++static struct gpio_led wp543_leds_gpio[] __initdata = { ++ { ++ .name = "wp543:green:led1", ++ .gpio = WP543_GPIO_LED_1, ++ .active_low = 1, ++ }, { ++ .name = "wp543:green:led2", ++ .gpio = WP543_GPIO_LED_2, ++ .active_low = 1, ++ }, { ++ .name = "wp543:green:wlan", ++ .gpio = WP543_GPIO_LED_WLAN, ++ .active_low = 1, ++ }, { ++ .name = "wp543:green:conn", ++ .gpio = WP543_GPIO_LED_CONN, ++ .active_low = 1, ++ }, { ++ .name = "wp543:green:diag", ++ .gpio = WP543_GPIO_LED_DIAG, ++ .active_low = 1, ++ } ++}; ++ ++static struct gpio_button wp543_gpio_buttons[] __initdata = { ++ { ++ .desc = "sw6", ++ .type = EV_KEY, ++ .code = BTN_0, ++ .threshold = 5, ++ .gpio = WP543_GPIO_SW6, ++ }, { ++ .desc = "sw4", ++ .type = EV_KEY, ++ .code = BTN_1, ++ .threshold = 5, ++ .gpio = WP543_GPIO_SW4, ++ } ++}; ++ ++static void __init wp543_setup(void) ++{ ++ ar71xx_add_device_spi(NULL, wp543_spi_info, ARRAY_SIZE(wp543_spi_info)); ++ ++ ar71xx_add_device_mdio(0xfffffff7); ++ ++ ar71xx_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; ++ ar71xx_eth0_data.phy_mask = 0x00000008; ++ ar71xx_add_device_eth(0); ++ ++ ar71xx_add_device_usb(); ++ ++ ar71xx_pci_init(ARRAY_SIZE(wp543_pci_irqs), wp543_pci_irqs); ++ ++ ar71xx_add_device_leds_gpio(-1, ARRAY_SIZE(wp543_leds_gpio), ++ wp543_leds_gpio); ++ ++ ar71xx_add_device_gpio_buttons(-1, WP543_BUTTONS_POLL_INTERVAL, ++ ARRAY_SIZE(wp543_gpio_buttons), ++ wp543_gpio_buttons); ++} ++ ++MIPS_MACHINE(AR71XX_MACH_WP543, "Compex WP543", wp543_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/prom.c linux-2.6.29.1/arch/mips/ar71xx/prom.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/prom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/prom.c 2009-04-13 14:27:34.595070701 +0200 +@@ -0,0 +1,207 @@ ++/* ++ * Atheros AR71xx SoC specific prom routines ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/serial_reg.h> ++ ++#include <asm/bootinfo.h> ++#include <asm/addrspace.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#include "devices.h" ++ ++struct board_rec { ++ char *name; ++ unsigned long mach_type; ++}; ++ ++static int ar71xx_prom_argc __initdata; ++static char **ar71xx_prom_argv __initdata; ++static char **ar71xx_prom_envp __initdata; ++ ++static struct board_rec boards[] __initdata = { ++ { ++ .name = "411", ++ .mach_type = AR71XX_MACH_RB_411, ++ }, { ++ .name = "433", ++ .mach_type = AR71XX_MACH_RB_433, ++ }, { ++ .name = "450", ++ .mach_type = AR71XX_MACH_RB_450, ++ }, { ++ .name = "493", ++ .mach_type = AR71XX_MACH_RB_493, ++ }, { ++ .name = "AP81", ++ .mach_type = AR71XX_MACH_AP81, ++ }, { ++ .name = "AP83", ++ .mach_type = AR71XX_MACH_AP83, ++ }, { ++ .name = "AW-NR580", ++ .mach_type = AR71XX_MACH_AW_NR580, ++ }, { ++ .name = "TEW-632BRP", ++ .mach_type = AR71XX_MACH_TEW_632BRP, ++ }, { ++ .name = "TL-WR941ND", ++ .mach_type = AR71XX_MACH_TL_WR941ND, ++ }, { ++ .name = "UBNT-RS", ++ .mach_type = AR71XX_MACH_UBNT_RS, ++ }, { ++ .name = "UBNT-RSPRO", ++ .mach_type = AR71XX_MACH_UBNT_RSPRO, ++ }, { ++ .name = "Ubiquiti AR71xx-based board", ++ .mach_type = AR71XX_MACH_UBNT_RS, ++ }, { ++ .name = "UBNT-LS-SR71", ++ .mach_type = AR71XX_MACH_UBNT_LSSR71, ++ }, { ++ .name = "UBNT-LSX", ++ .mach_type = AR71XX_MACH_UBNT_LSX, ++ }, { ++ .name = "WNR2000", ++ .mach_type = AR71XX_MACH_WNR2000, ++ }, { ++ .name = "PB42", ++ .mach_type = AR71XX_MACH_PB42, ++ }, { ++ .name = "MZK-W300NH", ++ .mach_type = AR71XX_MACH_MZK_W300NH, ++ }, { ++ .name = "MZK-W04NU", ++ .mach_type = AR71XX_MACH_MZK_W04NU, ++ } ++}; ++ ++static inline int is_valid_ram_addr(void *addr) ++{ ++ if (((u32) addr > KSEG0) && ++ ((u32) addr < (KSEG0 + AR71XX_MEM_SIZE_MAX))) ++ return 1; ++ ++ if (((u32) addr > KSEG1) && ++ ((u32) addr < (KSEG1 + AR71XX_MEM_SIZE_MAX))) ++ return 1; ++ ++ return 0; ++} ++ ++static __init char *ar71xx_prom_getargv(const char *name) ++{ ++ int len = strlen(name); ++ int i; ++ ++ if (!is_valid_ram_addr(ar71xx_prom_argv)) ++ return NULL; ++ ++ for (i = 0; i < ar71xx_prom_argc; i++) { ++ char *argv = ar71xx_prom_argv[i]; ++ ++ if (!is_valid_ram_addr(argv)) ++ continue; ++ ++ if (strncmp(name, argv, len) == 0 && (argv)[len] == '=') ++ return argv + len + 1; ++ } ++ ++ return NULL; ++} ++ ++static __init char *ar71xx_prom_getenv(const char *envname) ++{ ++ int len = strlen(envname); ++ char **env; ++ ++ if (!is_valid_ram_addr(ar71xx_prom_envp)) ++ return NULL; ++ ++ for (env = ar71xx_prom_envp; is_valid_ram_addr(*env); env++) { ++ if (strncmp(envname, *env, len) == 0 && (*env)[len] == '=') ++ return *env + len + 1; ++ ++ /* RedBoot env comes in pointer pairs - key, value */ ++ if (strncmp(envname, *env, len) == 0 && (*env)[len] == 0) ++ if (is_valid_ram_addr(*(++env))) ++ return *env; ++ } ++ ++ return NULL; ++} ++ ++static __init unsigned long find_board_byname(char *name) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(boards); i++) ++ if (strcmp(name, boards[i].name) == 0) ++ return boards[i].mach_type; ++ ++ return AR71XX_MACH_GENERIC; ++} ++ ++ ++static void ar71xx_prom_init_generic(void) ++{ ++ char *p; ++ ++ ar71xx_prom_argc = fw_arg0; ++ ar71xx_prom_argv = (char **)fw_arg1; ++ ar71xx_prom_envp = (char **)fw_arg2; ++ ++ p = ar71xx_prom_getargv("board"); ++ if (!p) ++ p = ar71xx_prom_getenv("board"); ++ if (p) ++ ar71xx_mach_type = find_board_byname(p); ++ ++ p = ar71xx_prom_getenv("ethaddr"); ++ if (!p) ++ p = ar71xx_prom_getargv("kmac"); ++ if (p) ++ ar71xx_parse_mac_addr(p); ++} ++ ++void __init prom_init(void) ++{ ++ printk(KERN_DEBUG "prom: fw_arg0=%08x, fw_arg1=%08x, " ++ "fw_arg2=%08x, fw_arg3=%08x\n", ++ (unsigned int)fw_arg0, (unsigned int)fw_arg1, ++ (unsigned int)fw_arg2, (unsigned int)fw_arg3); ++ ++ ar71xx_mach_type = AR71XX_MACH_GENERIC; ++ ++ ar71xx_prom_init_generic(); ++} ++ ++void __init prom_free_prom_memory(void) ++{ ++ /* We do not have to prom memory to free */ ++} ++ ++#define UART_READ(r) \ ++ __raw_readl((void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE) + 4 * (r))) ++ ++#define UART_WRITE(r, v) \ ++ __raw_writel((v), (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE) + 4*(r))) ++ ++void prom_putchar(unsigned char ch) ++{ ++ while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0); ++ UART_WRITE(UART_TX, ch); ++ while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0); ++} +diff -Nur linux-2.6.29.1.orig/arch/mips/ar71xx/setup.c linux-2.6.29.1/arch/mips/ar71xx/setup.c +--- linux-2.6.29.1.orig/arch/mips/ar71xx/setup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/ar71xx/setup.c 2009-04-13 14:27:34.599071210 +0200 +@@ -0,0 +1,291 @@ ++/* ++ * Atheros AR71xx SoC specific setup ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/pci.h> ++#include <linux/serial_8250.h> ++#include <linux/bootmem.h> ++ ++#include <asm/bootinfo.h> ++#include <asm/traps.h> ++#include <asm/time.h> /* for mips_hpt_frequency */ ++#include <asm/reboot.h> /* for _machine_{restart,halt} */ ++#include <asm/mips_machine.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#include "devices.h" ++ ++#define AR71XX_SYS_TYPE_LEN 64 ++#define AR71XX_BASE_FREQ 40000000 ++#define AR91XX_BASE_FREQ 5000000 ++ ++unsigned long ar71xx_mach_type; ++ ++u32 ar71xx_cpu_freq; ++EXPORT_SYMBOL_GPL(ar71xx_cpu_freq); ++ ++u32 ar71xx_ahb_freq; ++EXPORT_SYMBOL_GPL(ar71xx_ahb_freq); ++ ++u32 ar71xx_ddr_freq; ++EXPORT_SYMBOL_GPL(ar71xx_ddr_freq); ++ ++enum ar71xx_soc_type ar71xx_soc; ++EXPORT_SYMBOL_GPL(ar71xx_soc); ++ ++int (*ar71xx_pci_bios_init)(unsigned nr_irqs, ++ struct ar71xx_pci_irq *map) __initdata; ++ ++int (*ar71xx_pci_be_handler)(int is_fixup); ++ ++static char ar71xx_sys_type[AR71XX_SYS_TYPE_LEN]; ++ ++static void ar71xx_restart(char *command) ++{ ++ ar71xx_device_stop(RESET_MODULE_FULL_CHIP); ++ for (;;) ++ if (cpu_wait) ++ cpu_wait(); ++} ++ ++static void ar71xx_halt(void) ++{ ++ while (1) ++ cpu_wait(); ++} ++ ++static int ar71xx_be_handler(struct pt_regs *regs, int is_fixup) ++{ ++ int err = 0; ++ ++ if (ar71xx_pci_be_handler) ++ err = ar71xx_pci_be_handler(is_fixup); ++ ++ return (is_fixup && !err) ? MIPS_BE_FIXUP : MIPS_BE_FATAL; ++} ++ ++int __init ar71xx_pci_init(unsigned nr_irqs, struct ar71xx_pci_irq *map) ++{ ++ if (!ar71xx_pci_bios_init) ++ return 0; ++ ++ return ar71xx_pci_bios_init(nr_irqs, map); ++} ++ ++static void __init ar71xx_detect_mem_size(void) ++{ ++ unsigned long size; ++ ++ for (size = AR71XX_MEM_SIZE_MIN; size < AR71XX_MEM_SIZE_MAX; ++ size <<= 1 ) { ++ if (!memcmp(ar71xx_detect_mem_size, ++ ar71xx_detect_mem_size + size, 1024)) ++ break; ++ } ++ ++ add_memory_region(0, size, BOOT_MEM_RAM); ++} ++ ++static void __init ar71xx_detect_sys_type(void) ++{ ++ char *chip; ++ u32 id; ++ u32 rev; ++ ++ id = ar71xx_reset_rr(AR71XX_RESET_REG_REV_ID) & REV_ID_MASK; ++ rev = (id >> REV_ID_REVISION_SHIFT) & REV_ID_REVISION_MASK; ++ ++ switch (id & REV_ID_CHIP_MASK) { ++ case REV_ID_CHIP_AR7130: ++ ar71xx_soc = AR71XX_SOC_AR7130; ++ chip = "7130"; ++ break; ++ ++ case REV_ID_CHIP_AR7141: ++ ar71xx_soc = AR71XX_SOC_AR7141; ++ chip = "7141"; ++ break; ++ ++ case REV_ID_CHIP_AR7161: ++ ar71xx_soc = AR71XX_SOC_AR7161; ++ chip = "7161"; ++ break; ++ ++ case REV_ID_CHIP_AR9130: ++ ar71xx_soc = AR71XX_SOC_AR9130; ++ chip = "9130"; ++ break; ++ ++ case REV_ID_CHIP_AR9132: ++ ar71xx_soc = AR71XX_SOC_AR9132; ++ chip = "9132"; ++ break; ++ ++ default: ++ panic("ar71xx: unknown chip id:0x%02x\n", id); ++ } ++ ++ sprintf(ar71xx_sys_type, "Atheros AR%s rev %u (id:0x%02x)", ++ chip, rev, id); ++} ++ ++static void __init ar91xx_detect_sys_frequency(void) ++{ ++ u32 pll; ++ u32 freq; ++ u32 div; ++ ++ pll = ar71xx_pll_rr(AR91XX_PLL_REG_CPU_CONFIG); ++ ++ div = ((pll >> AR91XX_PLL_DIV_SHIFT) & AR91XX_PLL_DIV_MASK); ++ freq = div * AR91XX_BASE_FREQ; ++ ++ ar71xx_cpu_freq = freq; ++ ++ div = ((pll >> AR91XX_DDR_DIV_SHIFT) & AR91XX_DDR_DIV_MASK) + 1; ++ ar71xx_ddr_freq = freq / div; ++ ++ div = (((pll >> AR91XX_AHB_DIV_SHIFT) & AR91XX_AHB_DIV_MASK) + 1) * 2; ++ ar71xx_ahb_freq = ar71xx_cpu_freq / div; ++} ++ ++static void __init ar71xx_detect_sys_frequency(void) ++{ ++ u32 pll; ++ u32 freq; ++ u32 div; ++ ++ pll = ar71xx_pll_rr(AR71XX_PLL_REG_CPU_CONFIG); ++ ++ div = ((pll >> AR71XX_PLL_DIV_SHIFT) & AR71XX_PLL_DIV_MASK) + 1; ++ freq = div * AR71XX_BASE_FREQ; ++ ++ div = ((pll >> AR71XX_CPU_DIV_SHIFT) & AR71XX_CPU_DIV_MASK) + 1; ++ ar71xx_cpu_freq = freq / div; ++ ++ div = ((pll >> AR71XX_DDR_DIV_SHIFT) & AR71XX_DDR_DIV_MASK) + 1; ++ ar71xx_ddr_freq = freq / div; ++ ++ div = (((pll >> AR71XX_AHB_DIV_SHIFT) & AR71XX_AHB_DIV_MASK) + 1) * 2; ++ ar71xx_ahb_freq = ar71xx_cpu_freq / div; ++} ++ ++static void __init detect_sys_frequency(void) ++{ ++ switch (ar71xx_soc) { ++ case AR71XX_SOC_AR7130: ++ case AR71XX_SOC_AR7141: ++ case AR71XX_SOC_AR7161: ++ ar71xx_detect_sys_frequency(); ++ break; ++ ++ case AR71XX_SOC_AR9130: ++ case AR71XX_SOC_AR9132: ++ ar91xx_detect_sys_frequency(); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++#ifdef CONFIG_AR71XX_EARLY_SERIAL ++static void __init ar71xx_early_serial_setup(void) ++{ ++ struct uart_port p; ++ ++ memset(&p, 0, sizeof(p)); ++ ++ p.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP; ++ p.iotype = UPIO_MEM32; ++ p.uartclk = ar71xx_ahb_freq; ++ p.irq = AR71XX_MISC_IRQ_UART; ++ p.regshift = 2; ++ p.mapbase = AR71XX_UART_BASE; ++ ++ early_serial_setup(&p); ++} ++#else ++static inline void ar71xx_early_serial_setup(void) {}; ++#endif /* CONFIG_AR71XX_EARLY_SERIAL */ ++ ++const char *get_system_type(void) ++{ ++ return ar71xx_sys_type; ++} ++ ++unsigned int __cpuinit get_c0_compare_irq(void) ++{ ++ return CP0_LEGACY_COMPARE_IRQ; ++} ++ ++void __init plat_mem_setup(void) ++{ ++ set_io_port_base(KSEG1); ++ ++ ar71xx_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE, ++ AR71XX_DDR_CTRL_SIZE); ++ ++ ar71xx_pll_base = ioremap_nocache(AR71XX_PLL_BASE, ++ AR71XX_PLL_SIZE); ++ ++ ar71xx_reset_base = ioremap_nocache(AR71XX_RESET_BASE, ++ AR71XX_RESET_SIZE); ++ ++ ar71xx_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE); ++ ++ ar71xx_usb_ctrl_base = ioremap_nocache(AR71XX_USB_CTRL_BASE, ++ AR71XX_USB_CTRL_SIZE); ++ ++ ar71xx_detect_mem_size(); ++ ar71xx_detect_sys_type(); ++ detect_sys_frequency(); ++ ++ printk(KERN_INFO ++ "%s, CPU:%u.%03u MHz, AHB:%u.%03u MHz, DDR:%u.%03u MHz\n", ++ ar71xx_sys_type, ++ ar71xx_cpu_freq / 1000000, (ar71xx_cpu_freq / 1000) % 1000, ++ ar71xx_ahb_freq / 1000000, (ar71xx_ahb_freq / 1000) % 1000, ++ ar71xx_ddr_freq / 1000000, (ar71xx_ddr_freq / 1000) % 1000); ++ ++ _machine_restart = ar71xx_restart; ++ _machine_halt = ar71xx_halt; ++ pm_power_off = ar71xx_halt; ++ ++ board_be_handler = ar71xx_be_handler; ++ ++ ar71xx_early_serial_setup(); ++} ++ ++void __init plat_time_init(void) ++{ ++ mips_hpt_frequency = ar71xx_cpu_freq / 2; ++} ++ ++static int __init ar71xx_machine_setup(void) ++{ ++ ar71xx_gpio_init(); ++ ++ ar71xx_add_device_uart(); ++ ar71xx_add_device_wdt(); ++ ++ mips_machine_setup(ar71xx_mach_type); ++ return 0; ++} ++ ++arch_initcall(ar71xx_machine_setup); +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/fw/myloader/myloader.h linux-2.6.29.1/arch/mips/include/asm/fw/myloader/myloader.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/fw/myloader/myloader.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/fw/myloader/myloader.h 2009-04-13 14:27:34.599071210 +0200 +@@ -0,0 +1,34 @@ ++/* ++ * Compex's MyLoader specific definitions ++ * ++ * Copyright (C) 2006-2008 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef _ASM_MIPS_FW_MYLOADER_H ++#define _ASM_MIPS_FW_MYLOADER_H ++ ++#include <linux/myloader.h> ++ ++struct myloader_info { ++ uint32_t vid; ++ uint32_t did; ++ uint32_t svid; ++ uint32_t sdid; ++ uint8_t macs[MYLO_ETHADDR_COUNT][6]; ++}; ++ ++#ifdef CONFIG_MYLOADER ++extern struct myloader_info *myloader_get_info(void) __init; ++#else ++static inline struct myloader_info *myloader_get_info(void) ++{ ++ return NULL; ++} ++#endif /* CONFIG_MYLOADER */ ++ ++#endif /* _ASM_MIPS_FW_MYLOADER_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/ar71xx.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/ar71xx.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/ar71xx.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/ar71xx.h 2009-04-13 14:27:34.623072313 +0200 +@@ -0,0 +1,438 @@ ++/* ++ * Atheros AR71xx SoC specific definitions ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef __ASM_MACH_AR71XX_H ++#define __ASM_MACH_AR71XX_H ++ ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/bitops.h> ++ ++#ifndef __ASSEMBLER__ ++ ++#define AR71XX_PCI_MEM_BASE 0x10000000 ++#define AR71XX_PCI_MEM_SIZE 0x08000000 ++#define AR71XX_APB_BASE 0x18000000 ++#define AR71XX_GE0_BASE 0x19000000 ++#define AR71XX_GE0_SIZE 0x01000000 ++#define AR71XX_GE1_BASE 0x1a000000 ++#define AR71XX_GE1_SIZE 0x01000000 ++#define AR71XX_EHCI_BASE 0x1b000000 ++#define AR71XX_EHCI_SIZE 0x01000000 ++#define AR71XX_OHCI_BASE 0x1c000000 ++#define AR71XX_OHCI_SIZE 0x01000000 ++#define AR71XX_SPI_BASE 0x1f000000 ++#define AR71XX_SPI_SIZE 0x01000000 ++ ++#define AR71XX_DDR_CTRL_BASE (AR71XX_APB_BASE + 0x00000000) ++#define AR71XX_DDR_CTRL_SIZE 0x10000 ++#define AR71XX_CPU_BASE (AR71XX_APB_BASE + 0x00010000) ++#define AR71XX_UART_BASE (AR71XX_APB_BASE + 0x00020000) ++#define AR71XX_UART_SIZE 0x10000 ++#define AR71XX_USB_CTRL_BASE (AR71XX_APB_BASE + 0x00030000) ++#define AR71XX_USB_CTRL_SIZE 0x10000 ++#define AR71XX_GPIO_BASE (AR71XX_APB_BASE + 0x00040000) ++#define AR71XX_GPIO_SIZE 0x10000 ++#define AR71XX_PLL_BASE (AR71XX_APB_BASE + 0x00050000) ++#define AR71XX_PLL_SIZE 0x10000 ++#define AR71XX_RESET_BASE (AR71XX_APB_BASE + 0x00060000) ++#define AR71XX_RESET_SIZE 0x10000 ++#define AR71XX_MII_BASE (AR71XX_APB_BASE + 0x00070000) ++#define AR71XX_MII_SIZE 0x10000 ++#define AR71XX_SLIC_BASE (AR71XX_APB_BASE + 0x00090000) ++#define AR71XX_SLIC_SIZE 0x10000 ++#define AR71XX_DMA_BASE (AR71XX_APB_BASE + 0x000A0000) ++#define AR71XX_DMA_SIZE 0x10000 ++#define AR71XX_STEREO_BASE (AR71XX_APB_BASE + 0x000B0000) ++#define AR71XX_STEREO_SIZE 0x10000 ++#define AR91XX_WMAC_BASE (AR71XX_APB_BASE + 0x000C0000) ++#define AR91XX_WMAC_SIZE 0x30000 ++ ++#define AR71XX_MEM_SIZE_MIN 0x0200000 ++#define AR71XX_MEM_SIZE_MAX 0x8000000 ++ ++#define AR71XX_CPU_IRQ_BASE 0 ++#define AR71XX_MISC_IRQ_BASE 8 ++#define AR71XX_MISC_IRQ_COUNT 8 ++#define AR71XX_GPIO_IRQ_BASE 16 ++#define AR71XX_GPIO_IRQ_COUNT 16 ++#define AR71XX_PCI_IRQ_BASE 32 ++#define AR71XX_PCI_IRQ_COUNT 4 ++ ++#define AR71XX_CPU_IRQ_PCI (AR71XX_CPU_IRQ_BASE + 2) ++#define AR71XX_CPU_IRQ_WMAC (AR71XX_CPU_IRQ_BASE + 2) ++#define AR71XX_CPU_IRQ_USB (AR71XX_CPU_IRQ_BASE + 3) ++#define AR71XX_CPU_IRQ_GE0 (AR71XX_CPU_IRQ_BASE + 4) ++#define AR71XX_CPU_IRQ_GE1 (AR71XX_CPU_IRQ_BASE + 5) ++#define AR71XX_CPU_IRQ_MISC (AR71XX_CPU_IRQ_BASE + 6) ++#define AR71XX_CPU_IRQ_TIMER (AR71XX_CPU_IRQ_BASE + 7) ++ ++#define AR71XX_MISC_IRQ_TIMER (AR71XX_MISC_IRQ_BASE + 0) ++#define AR71XX_MISC_IRQ_ERROR (AR71XX_MISC_IRQ_BASE + 1) ++#define AR71XX_MISC_IRQ_GPIO (AR71XX_MISC_IRQ_BASE + 2) ++#define AR71XX_MISC_IRQ_UART (AR71XX_MISC_IRQ_BASE + 3) ++#define AR71XX_MISC_IRQ_WDOG (AR71XX_MISC_IRQ_BASE + 4) ++#define AR71XX_MISC_IRQ_PERFC (AR71XX_MISC_IRQ_BASE + 5) ++#define AR71XX_MISC_IRQ_OHCI (AR71XX_MISC_IRQ_BASE + 6) ++#define AR71XX_MISC_IRQ_DMA (AR71XX_MISC_IRQ_BASE + 7) ++ ++#define AR71XX_GPIO_IRQ(_x) (AR71XX_GPIO_IRQ_BASE + (_x)) ++ ++#define AR71XX_PCI_IRQ_DEV0 (AR71XX_PCI_IRQ_BASE + 0) ++#define AR71XX_PCI_IRQ_DEV1 (AR71XX_PCI_IRQ_BASE + 1) ++#define AR71XX_PCI_IRQ_DEV2 (AR71XX_PCI_IRQ_BASE + 2) ++#define AR71XX_PCI_IRQ_CORE (AR71XX_PCI_IRQ_BASE + 3) ++ ++extern u32 ar71xx_ahb_freq; ++extern u32 ar71xx_cpu_freq; ++extern u32 ar71xx_ddr_freq; ++ ++enum ar71xx_soc_type { ++ AR71XX_SOC_UNKNOWN, ++ AR71XX_SOC_AR7130, ++ AR71XX_SOC_AR7141, ++ AR71XX_SOC_AR7161, ++ AR71XX_SOC_AR9130, ++ AR71XX_SOC_AR9132 ++}; ++ ++extern enum ar71xx_soc_type ar71xx_soc; ++ ++extern unsigned long ar71xx_mach_type; ++ ++#define AR71XX_MACH_GENERIC 0 ++#define AR71XX_MACH_WP543 1 /* Compex WP543 */ ++#define AR71XX_MACH_RB_411 2 /* MikroTik RouterBOARD 411/411A/411AH */ ++#define AR71XX_MACH_RB_433 3 /* MikroTik RouterBOARD 433/433AH */ ++#define AR71XX_MACH_RB_450 4 /* MikroTik RouterBOARD 450 */ ++#define AR71XX_MACH_RB_493 5 /* Mikrotik RouterBOARD 493/493AH */ ++#define AR71XX_MACH_AW_NR580 6 /* AzureWave AW-NR580 */ ++#define AR71XX_MACH_AP83 7 /* Atheros AP83 */ ++#define AR71XX_MACH_TEW_632BRP 8 /* TRENDnet TEW-632BRP */ ++#define AR71XX_MACH_UBNT_RS 9 /* Ubiquiti RouterStation */ ++#define AR71XX_MACH_UBNT_LSX 10 /* Ubiquiti LSX */ ++#define AR71XX_MACH_WNR2000 11 /* NETGEAR WNR2000 */ ++#define AR71XX_MACH_PB42 12 /* Atheros PB42 */ ++#define AR71XX_MACH_MZK_W300NH 13 /* Planex MZK-W300NH */ ++#define AR71XX_MACH_MZK_W04NU 14 /* Planex MZK-W04NU */ ++#define AR71XX_MACH_UBNT_LSSR71 15 /* Ubiquiti LS-SR71 */ ++#define AR71XX_MACH_TL_WR941ND 16 /* TP-LINK TL-WR941ND */ ++#define AR71XX_MACH_UBNT_RSPRO 17 /* Ubiquiti RouterStation Pro */ ++#define AR71XX_MACH_AP81 18 /* Atheros AP81 */ ++ ++/* ++ * PLL block ++ */ ++#define AR71XX_PLL_REG_CPU_CONFIG 0x00 ++#define AR71XX_PLL_REG_SEC_CONFIG 0x04 ++#define AR71XX_PLL_REG_ETH0_INT_CLOCK 0x10 ++#define AR71XX_PLL_REG_ETH1_INT_CLOCK 0x14 ++ ++#define AR71XX_PLL_DIV_SHIFT 3 ++#define AR71XX_PLL_DIV_MASK 0x1f ++#define AR71XX_CPU_DIV_SHIFT 16 ++#define AR71XX_CPU_DIV_MASK 0x3 ++#define AR71XX_DDR_DIV_SHIFT 18 ++#define AR71XX_DDR_DIV_MASK 0x3 ++#define AR71XX_AHB_DIV_SHIFT 20 ++#define AR71XX_AHB_DIV_MASK 0x7 ++ ++#define AR71XX_ETH0_PLL_SHIFT 17 ++#define AR71XX_ETH1_PLL_SHIFT 19 ++ ++#define AR91XX_PLL_REG_CPU_CONFIG 0x00 ++#define AR91XX_PLL_REG_ETH_CONFIG 0x04 ++#define AR91XX_PLL_REG_ETH0_INT_CLOCK 0x14 ++#define AR91XX_PLL_REG_ETH1_INT_CLOCK 0x18 ++ ++#define AR91XX_PLL_DIV_SHIFT 0 ++#define AR91XX_PLL_DIV_MASK 0x3ff ++#define AR91XX_DDR_DIV_SHIFT 22 ++#define AR91XX_DDR_DIV_MASK 0x3 ++#define AR91XX_AHB_DIV_SHIFT 19 ++#define AR91XX_AHB_DIV_MASK 0x1 ++ ++#define AR91XX_ETH0_PLL_SHIFT 20 ++#define AR91XX_ETH1_PLL_SHIFT 22 ++ ++extern void __iomem *ar71xx_pll_base; ++ ++static inline void ar71xx_pll_wr(unsigned reg, u32 val) ++{ ++ __raw_writel(val, ar71xx_pll_base + reg); ++} ++ ++static inline u32 ar71xx_pll_rr(unsigned reg) ++{ ++ return __raw_readl(ar71xx_pll_base + reg); ++} ++ ++/* ++ * USB_CONFIG block ++ */ ++#define USB_CTRL_REG_FLADJ 0x00 ++#define USB_CTRL_REG_CONFIG 0x04 ++ ++extern void __iomem *ar71xx_usb_ctrl_base; ++ ++static inline void ar71xx_usb_ctrl_wr(unsigned reg, u32 val) ++{ ++ __raw_writel(val, ar71xx_usb_ctrl_base + reg); ++} ++ ++static inline u32 ar71xx_usb_ctrl_rr(unsigned reg) ++{ ++ return __raw_readl(ar71xx_usb_ctrl_base + reg); ++} ++ ++/* ++ * GPIO block ++ */ ++#define GPIO_REG_OE 0x00 ++#define GPIO_REG_IN 0x04 ++#define GPIO_REG_OUT 0x08 ++#define GPIO_REG_SET 0x0c ++#define GPIO_REG_CLEAR 0x10 ++#define GPIO_REG_INT_MODE 0x14 ++#define GPIO_REG_INT_TYPE 0x18 ++#define GPIO_REG_INT_POLARITY 0x1c ++#define GPIO_REG_INT_PENDING 0x20 ++#define GPIO_REG_INT_ENABLE 0x24 ++#define GPIO_REG_FUNC 0x28 ++ ++#define GPIO_FUNC_STEREO_EN BIT(17) ++#define GPIO_FUNC_SLIC_EN BIT(16) ++#define GPIO_FUNC_SPI_CS2_EN BIT(13) ++#define GPIO_FUNC_SPI_CS1_EN BIT(12) ++#define GPIO_FUNC_UART_EN BIT(8) ++#define GPIO_FUNC_USB_OC_EN BIT(4) ++#define GPIO_FUNC_USB_CLK_EN BIT(0) ++ ++#define AR71XX_GPIO_COUNT 16 ++#define AR91XX_GPIO_COUNT 22 ++ ++extern void __iomem *ar71xx_gpio_base; ++ ++static inline void ar71xx_gpio_wr(unsigned reg, u32 value) ++{ ++ __raw_writel(value, ar71xx_gpio_base + reg); ++} ++ ++static inline u32 ar71xx_gpio_rr(unsigned reg) ++{ ++ return __raw_readl(ar71xx_gpio_base + reg); ++} ++ ++extern void ar71xx_gpio_init(void) __init; ++extern void ar71xx_gpio_function_enable(u32 mask); ++extern void ar71xx_gpio_function_disable(u32 mask); ++ ++/* ++ * DDR_CTRL block ++ */ ++#define AR71XX_DDR_REG_PCI_WIN0 0x7c ++#define AR71XX_DDR_REG_PCI_WIN1 0x80 ++#define AR71XX_DDR_REG_PCI_WIN2 0x84 ++#define AR71XX_DDR_REG_PCI_WIN3 0x88 ++#define AR71XX_DDR_REG_PCI_WIN4 0x8c ++#define AR71XX_DDR_REG_PCI_WIN5 0x90 ++#define AR71XX_DDR_REG_PCI_WIN6 0x94 ++#define AR71XX_DDR_REG_PCI_WIN7 0x98 ++#define AR71XX_DDR_REG_FLUSH_GE0 0x9c ++#define AR71XX_DDR_REG_FLUSH_GE1 0xa0 ++#define AR71XX_DDR_REG_FLUSH_USB 0xa4 ++#define AR71XX_DDR_REG_FLUSH_PCI 0xa8 ++ ++#define AR91XX_DDR_REG_FLUSH_GE0 0x7c ++#define AR91XX_DDR_REG_FLUSH_GE1 0x80 ++#define AR91XX_DDR_REG_FLUSH_USB 0x84 ++#define AR91XX_DDR_REG_FLUSH_WMAC 0x88 ++ ++#define PCI_WIN0_OFFS 0x10000000 ++#define PCI_WIN1_OFFS 0x11000000 ++#define PCI_WIN2_OFFS 0x12000000 ++#define PCI_WIN3_OFFS 0x13000000 ++#define PCI_WIN4_OFFS 0x14000000 ++#define PCI_WIN5_OFFS 0x15000000 ++#define PCI_WIN6_OFFS 0x16000000 ++#define PCI_WIN7_OFFS 0x07000000 ++ ++extern void __iomem *ar71xx_ddr_base; ++ ++static inline void ar71xx_ddr_wr(unsigned reg, u32 val) ++{ ++ __raw_writel(val, ar71xx_ddr_base + reg); ++} ++ ++static inline u32 ar71xx_ddr_rr(unsigned reg) ++{ ++ return __raw_readl(ar71xx_ddr_base + reg); ++} ++ ++extern void ar71xx_ddr_flush(u32 reg); ++ ++/* ++ * PCI block ++ */ ++#define AR71XX_PCI_CFG_BASE (AR71XX_PCI_MEM_BASE + PCI_WIN7_OFFS + 0x10000) ++#define AR71XX_PCI_CFG_SIZE 0x100 ++ ++#define PCI_REG_CRP_AD_CBE 0x00 ++#define PCI_REG_CRP_WRDATA 0x04 ++#define PCI_REG_CRP_RDDATA 0x08 ++#define PCI_REG_CFG_AD 0x0c ++#define PCI_REG_CFG_CBE 0x10 ++#define PCI_REG_CFG_WRDATA 0x14 ++#define PCI_REG_CFG_RDDATA 0x18 ++#define PCI_REG_PCI_ERR 0x1c ++#define PCI_REG_PCI_ERR_ADDR 0x20 ++#define PCI_REG_AHB_ERR 0x24 ++#define PCI_REG_AHB_ERR_ADDR 0x28 ++ ++#define PCI_CRP_CMD_WRITE 0x00010000 ++#define PCI_CRP_CMD_READ 0x00000000 ++#define PCI_CFG_CMD_READ 0x0000000a ++#define PCI_CFG_CMD_WRITE 0x0000000b ++ ++#define PCI_IDSEL_ADL_START 17 ++ ++/* ++ * RESET block ++ */ ++#define AR71XX_RESET_REG_TIMER 0x00 ++#define AR71XX_RESET_REG_TIMER_RELOAD 0x04 ++#define AR71XX_RESET_REG_WDOG_CTRL 0x08 ++#define AR71XX_RESET_REG_WDOG 0x0c ++#define AR71XX_RESET_REG_MISC_INT_STATUS 0x10 ++#define AR71XX_RESET_REG_MISC_INT_ENABLE 0x14 ++#define AR71XX_RESET_REG_PCI_INT_STATUS 0x18 ++#define AR71XX_RESET_REG_PCI_INT_ENABLE 0x1c ++#define AR71XX_RESET_REG_GLOBAL_INT_STATUS 0x20 ++#define AR71XX_RESET_REG_RESET_MODULE 0x24 ++#define AR71XX_RESET_REG_PERFC_CTRL 0x2c ++#define AR71XX_RESET_REG_PERFC0 0x30 ++#define AR71XX_RESET_REG_PERFC1 0x34 ++#define AR71XX_RESET_REG_REV_ID 0x90 ++ ++#define AR91XX_RESET_REG_GLOBAL_INT_STATUS 0x18 ++#define AR91XX_RESET_REG_RESET_MODULE 0x1c ++#define AR91XX_RESET_REG_PERF_CTRL 0x20 ++#define AR91XX_RESET_REG_PERFC0 0x24 ++#define AR91XX_RESET_REG_PERFC1 0x28 ++ ++#define WDOG_CTRL_LAST_RESET BIT(31) ++#define WDOG_CTRL_ACTION_MASK 3 ++#define WDOG_CTRL_ACTION_NONE 0 /* no action */ ++#define WDOG_CTRL_ACTION_GPI 1 /* general purpose interrupt */ ++#define WDOG_CTRL_ACTION_NMI 2 /* NMI */ ++#define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */ ++ ++#define MISC_INT_DMA BIT(7) ++#define MISC_INT_OHCI BIT(6) ++#define MISC_INT_PERFC BIT(5) ++#define MISC_INT_WDOG BIT(4) ++#define MISC_INT_UART BIT(3) ++#define MISC_INT_GPIO BIT(2) ++#define MISC_INT_ERROR BIT(1) ++#define MISC_INT_TIMER BIT(0) ++ ++#define PCI_INT_CORE BIT(4) ++#define PCI_INT_DEV2 BIT(2) ++#define PCI_INT_DEV1 BIT(1) ++#define PCI_INT_DEV0 BIT(0) ++ ++#define RESET_MODULE_EXTERNAL BIT(28) ++#define RESET_MODULE_FULL_CHIP BIT(24) ++#define RESET_MODULE_AMBA2WMAC BIT(22) ++#define RESET_MODULE_CPU_NMI BIT(21) ++#define RESET_MODULE_CPU_COLD BIT(20) ++#define RESET_MODULE_DMA BIT(19) ++#define RESET_MODULE_SLIC BIT(18) ++#define RESET_MODULE_STEREO BIT(17) ++#define RESET_MODULE_DDR BIT(16) ++#define RESET_MODULE_GE1_MAC BIT(13) ++#define RESET_MODULE_GE1_PHY BIT(12) ++#define RESET_MODULE_USBSUS_OVERRIDE BIT(10) ++#define RESET_MODULE_GE0_MAC BIT(9) ++#define RESET_MODULE_GE0_PHY BIT(8) ++#define RESET_MODULE_USB_OHCI_DLL BIT(6) ++#define RESET_MODULE_USB_HOST BIT(5) ++#define RESET_MODULE_USB_PHY BIT(4) ++#define RESET_MODULE_PCI_BUS BIT(1) ++#define RESET_MODULE_PCI_CORE BIT(0) ++ ++#define REV_ID_MASK 0xff ++#define REV_ID_CHIP_MASK 0xf3 ++#define REV_ID_CHIP_AR7130 0xa0 ++#define REV_ID_CHIP_AR7141 0xa1 ++#define REV_ID_CHIP_AR7161 0xa2 ++#define REV_ID_CHIP_AR9130 0xb0 ++#define REV_ID_CHIP_AR9132 0xb1 ++ ++#define REV_ID_REVISION_MASK 0x3 ++#define REV_ID_REVISION_SHIFT 2 ++ ++extern void __iomem *ar71xx_reset_base; ++ ++static inline void ar71xx_reset_wr(unsigned reg, u32 val) ++{ ++ __raw_writel(val, ar71xx_reset_base + reg); ++} ++ ++static inline u32 ar71xx_reset_rr(unsigned reg) ++{ ++ return __raw_readl(ar71xx_reset_base + reg); ++} ++ ++extern void ar71xx_device_stop(u32 mask); ++extern void ar71xx_device_start(u32 mask); ++ ++/* ++ * SPI block ++ */ ++#define SPI_REG_FS 0x00 /* Function Select */ ++#define SPI_REG_CTRL 0x04 /* SPI Control */ ++#define SPI_REG_IOC 0x08 /* SPI I/O Control */ ++#define SPI_REG_RDS 0x0c /* Read Data Shift */ ++ ++#define SPI_FS_GPIO BIT(0) /* Enable GPIO mode */ ++ ++#define SPI_CTRL_RD BIT(6) /* Remap Disable */ ++#define SPI_CTRL_DIV_MASK 0x3f ++ ++#define SPI_IOC_DO BIT(0) /* Data Out pin */ ++#define SPI_IOC_CLK BIT(8) /* CLK pin */ ++#define SPI_IOC_CS(n) BIT(16 + (n)) ++#define SPI_IOC_CS0 SPI_IOC_CS(0) ++#define SPI_IOC_CS1 SPI_IOC_CS(1) ++#define SPI_IOC_CS2 SPI_IOC_CS(2) ++#define SPI_IOC_CS_ALL (SPI_IOC_CS0 | SPI_IOC_CS1 | SPI_IOC_CS2) ++ ++/* ++ * MII_CTRL block ++ */ ++#define MII_REG_MII0_CTRL 0x00 ++#define MII_REG_MII1_CTRL 0x04 ++ ++#define MII0_CTRL_IF_GMII 0 ++#define MII0_CTRL_IF_MII 1 ++#define MII0_CTRL_IF_RGMII 2 ++#define MII0_CTRL_IF_RMII 3 ++ ++#define MII1_CTRL_IF_RGMII 0 ++#define MII1_CTRL_IF_RMII 1 ++ ++#endif /* __ASSEMBLER__ */ ++ ++#endif /* __ASM_MACH_AR71XX_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/cpu-feature-overrides.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/cpu-feature-overrides.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/cpu-feature-overrides.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/cpu-feature-overrides.h 2009-04-13 14:27:34.627073939 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * Atheros AR71xx specific CPU feature overrides ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++#ifndef __ASM_MACH_AR71XX_CPU_FEATURE_OVERRIDES_H ++#define __ASM_MACH_AR71XX_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 0 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 32 ++#define cpu_icache_line_size() 32 ++ ++#endif /* __ASM_MACH_AR71XX_CPU_FEATURE_OVERRIDES_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/gpio.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/gpio.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/gpio.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/gpio.h 2009-04-13 14:27:34.627073939 +0200 +@@ -0,0 +1,53 @@ ++/* ++ * Atheros AR71xx GPIO API definitions ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __ASM_MACH_AR71XX_GPIO_H ++#define __ASM_MACH_AR71XX_GPIO_H ++ ++#define ARCH_NR_GPIOS 64 ++#include <asm-generic/gpio.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++extern unsigned long ar71xx_gpio_count; ++extern void __ar71xx_gpio_set_value(unsigned gpio, int value); ++extern int __ar71xx_gpio_get_value(unsigned gpio); ++ ++static inline int gpio_to_irq(unsigned gpio) ++{ ++ return AR71XX_GPIO_IRQ(gpio); ++} ++ ++static inline int irq_to_gpio(unsigned irq) ++{ ++ return irq - AR71XX_GPIO_IRQ_BASE; ++} ++ ++static inline int gpio_get_value(unsigned gpio) ++{ ++ if (gpio < ar71xx_gpio_count) ++ return __ar71xx_gpio_get_value(gpio); ++ ++ return __gpio_get_value(gpio); ++} ++ ++static inline void gpio_set_value(unsigned gpio, int value) ++{ ++ if (gpio < ar71xx_gpio_count) ++ __ar71xx_gpio_set_value(gpio, value); ++ else ++ __gpio_set_value(gpio, value); ++} ++ ++#define gpio_cansleep __gpio_cansleep ++ ++#endif /* __ASM_MACH_AR71XX_GPIO_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/irq.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/irq.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/irq.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/irq.h 2009-04-13 14:27:34.643075140 +0200 +@@ -0,0 +1,17 @@ ++/* ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++#ifndef __ASM_MACH_AR71XX_IRQ_H ++#define __ASM_MACH_AR71XX_IRQ_H ++ ++#define MIPS_CPU_IRQ_BASE 0 ++#define NR_IRQS 36 ++ ++#include_next <irq.h> ++ ++#endif /* __ASM_MACH_AR71XX_IRQ_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/kernel-entry-init.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/kernel-entry-init.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/kernel-entry-init.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/kernel-entry-init.h 2009-04-13 14:27:34.643075140 +0200 +@@ -0,0 +1,32 @@ ++/* ++ * Atheros AR71xx specific kernel entry setup ++ * ++ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++#ifndef __ASM_MACH_AR71XX_KERNEL_ENTRY_H ++#define __ASM_MACH_AR71XX_KERNEL_ENTRY_H ++ ++ /* ++ * Some bootloaders set the 'Kseg0 coherency algorithm' to ++ * 'Cacheable, noncoherent, write-through, no write allocate' ++ * and this cause performance issues. Let's go and change it to ++ * 'Cacheable, noncoherent, write-back, write allocate' ++ */ ++ .macro kernel_entry_setup ++ mfc0 t0, CP0_CONFIG ++ li t1, ~CONF_CM_CMASK ++ and t0, t1 ++ ori t0, CONF_CM_CACHABLE_NONCOHERENT ++ mtc0 t0, CP0_CONFIG ++ nop ++ .endm ++ ++ .macro smp_slave_setup ++ .endm ++ ++#endif /* __ASM_MACH_AR71XX_KERNEL_ENTRY_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/mangle-port.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/mangle-port.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/mangle-port.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/mangle-port.h 2009-04-13 14:27:34.643075140 +0200 +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This file was derived from: inlude/asm-mips/mach-generic/mangle-port.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef __ASM_MACH_AR71XX_MANGLE_PORT_H ++#define __ASM_MACH_AR71XX_MANGLE_PORT_H ++ ++#define __swizzle_addr_b(port) ((port) ^ 3) ++#define __swizzle_addr_w(port) ((port) ^ 2) ++#define __swizzle_addr_l(port) (port) ++#define __swizzle_addr_q(port) (port) ++ ++#if defined(CONFIG_SWAP_IO_SPACE) ++ ++# define ioswabb(a, x) (x) ++# define __mem_ioswabb(a, x) (x) ++# define ioswabw(a, x) le16_to_cpu(x) ++# define __mem_ioswabw(a, x) (x) ++# define ioswabl(a, x) le32_to_cpu(x) ++# define __mem_ioswabl(a, x) (x) ++# define ioswabq(a, x) le64_to_cpu(x) ++# define __mem_ioswabq(a, x) (x) ++ ++#else ++ ++# define ioswabb(a, x) (x) ++# define __mem_ioswabb(a, x) (x) ++# define ioswabw(a, x) (x) ++# define __mem_ioswabw(a, x) cpu_to_le16(x) ++# define ioswabl(a, x) (x) ++# define __mem_ioswabl(a, x) cpu_to_le32(x) ++# define ioswabq(a, x) (x) ++# define __mem_ioswabq(a, x) cpu_to_le64(x) ++ ++#endif ++ ++#endif /* __ASM_MACH_AR71XX_MANGLE_PORT_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/pci.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/pci.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/pci.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/pci.h 2009-04-13 14:27:34.647074532 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * Atheros AR71xx SoC specific PCI definitions ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef __ASM_MACH_AR71XX_PCI_H ++#define __ASM_MACH_AR71XX_PCI_H ++ ++struct ar71xx_pci_irq { ++ int irq; ++ u8 slot; ++ u8 pin; ++}; ++ ++extern int (*ar71xx_pci_be_handler)(int is_fixup); ++extern int (*ar71xx_pci_bios_init)(unsigned nr_irqs, ++ struct ar71xx_pci_irq *map) __initdata; ++ ++extern int ar71xx_pci_init(unsigned nr_irqs, ++ struct ar71xx_pci_irq *map) __init; ++ ++#endif /* __ASM_MACH_AR71XX_PCI_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/platform.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/platform.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/platform.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/platform.h 2009-04-13 14:27:34.647074532 +0200 +@@ -0,0 +1,54 @@ ++/* ++ * Atheros AR71xx SoC specific platform data definitions ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef __ASM_MACH_AR71XX_PLATFORM_H ++#define __ASM_MACH_AR71XX_PLATFORM_H ++ ++#include <linux/if_ether.h> ++#include <linux/skbuff.h> ++#include <linux/phy.h> ++#include <linux/spi/spi.h> ++ ++struct ag71xx_platform_data { ++ phy_interface_t phy_if_mode; ++ u32 phy_mask; ++ int speed; ++ int duplex; ++ u32 reset_bit; ++ u32 mii_if; ++ u8 mac_addr[ETH_ALEN]; ++ ++ u8 has_gbit:1; ++ u8 is_ar91xx:1; ++ u8 has_ar8216:1; ++ ++ void (* ddr_flush)(void); ++ void (* set_pll)(u32 pll); ++}; ++ ++struct ag71xx_mdio_platform_data { ++ u32 phy_mask; ++}; ++ ++struct ar71xx_ehci_platform_data { ++ u8 is_ar91xx; ++}; ++ ++struct ar71xx_spi_platform_data { ++ unsigned bus_num; ++ unsigned num_chipselect; ++ u32 (*get_ioc_base)(u8 chip_select, int cs_high, int is_on); ++}; ++ ++#define AR71XX_SPI_CS_INACTIVE 0 ++#define AR71XX_SPI_CS_ACTIVE 1 ++ ++#endif /* __ASM_MACH_AR71XX_PLATFORM_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/war.h linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/war.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/mach-ar71xx/war.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/include/asm/mach-ar71xx/war.h 2009-04-13 14:27:34.647074532 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * 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) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org> ++ */ ++#ifndef __ASM_MACH_AR71XX_WAR_H ++#define __ASM_MACH_AR71XX_WAR_H ++ ++#define R4600_V1_INDEX_ICACHEOP_WAR 0 ++#define R4600_V1_HIT_CACHEOP_WAR 0 ++#define R4600_V2_HIT_CACHEOP_WAR 0 ++#define R5432_CP0_INTERRUPT_WAR 0 ++#define BCM1250_M3_WAR 0 ++#define SIBYTE_1956_WAR 0 ++#define MIPS4K_ICACHE_REFILL_WAR 0 ++#define MIPS_CACHE_SYNC_WAR 0 ++#define TX49XX_ICACHE_INDEX_INV_WAR 0 ++#define RM9000_CDEX_SMP_WAR 0 ++#define ICACHE_REFILLS_WORKAROUND_WAR 0 ++#define R10000_LLSC_WAR 0 ++#define MIPS34K_MISSED_ITLB_WAR 0 ++ ++#endif /* __ASM_MACH_AR71XX_WAR_H */ +diff -Nur linux-2.6.29.1.orig/arch/mips/include/asm/time.h linux-2.6.29.1/arch/mips/include/asm/time.h +--- linux-2.6.29.1.orig/arch/mips/include/asm/time.h 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/include/asm/time.h 2009-04-13 14:30:23.269613602 +0200 +@@ -52,6 +52,7 @@ + */ + #ifdef CONFIG_CEVT_R4K_LIB + extern unsigned int __weak get_c0_compare_int(void); ++extern unsigned int __weak get_c0_compare_irq(void); + extern int r4k_clockevent_init(void); + #endif + +diff -Nur linux-2.6.29.1.orig/arch/mips/kernel/Makefile linux-2.6.29.1/arch/mips/kernel/Makefile +--- linux-2.6.29.1.orig/arch/mips/kernel/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/kernel/Makefile 2009-04-13 14:27:34.655074992 +0200 +@@ -85,6 +85,7 @@ + + obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o ++obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o + + CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) + +diff -Nur linux-2.6.29.1.orig/arch/mips/kernel/cevt-r4k.c linux-2.6.29.1/arch/mips/kernel/cevt-r4k.c +--- linux-2.6.29.1.orig/arch/mips/kernel/cevt-r4k.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/kernel/cevt-r4k.c 2009-04-13 14:27:34.655074992 +0200 +@@ -15,6 +15,22 @@ + #include <asm/cevt-r4k.h> + + /* ++ * Compare interrupt can be routed and latched outside the core, ++ * so a single execution hazard barrier may not be enough to give ++ * it time to clear as seen in the Cause register. 4 time the ++ * pipeline depth seems reasonably conservative, and empirically ++ * works better in configurations with high CPU/bus clock ratios. ++ */ ++ ++#define compare_change_hazard() \ ++ do { \ ++ irq_disable_hazard(); \ ++ irq_disable_hazard(); \ ++ irq_disable_hazard(); \ ++ irq_disable_hazard(); \ ++ } while (0) ++ ++/* + * The SMTC Kernel for the 34K, 1004K, et. al. replaces several + * of these routines with SMTC-specific variants. + */ +@@ -30,6 +46,7 @@ + cnt = read_c0_count(); + cnt += delta; + write_c0_compare(cnt); ++ compare_change_hazard(); + res = ((int)(read_c0_count() - cnt) > 0) ? -ETIME : 0; + return res; + } +@@ -99,22 +116,6 @@ + return (read_c0_cause() >> cp0_compare_irq) & 0x100; + } + +-/* +- * Compare interrupt can be routed and latched outside the core, +- * so a single execution hazard barrier may not be enough to give +- * it time to clear as seen in the Cause register. 4 time the +- * pipeline depth seems reasonably conservative, and empirically +- * works better in configurations with high CPU/bus clock ratios. +- */ +- +-#define compare_change_hazard() \ +- do { \ +- irq_disable_hazard(); \ +- irq_disable_hazard(); \ +- irq_disable_hazard(); \ +- irq_disable_hazard(); \ +- } while (0) +- + int c0_compare_int_usable(void) + { + unsigned int delta; +diff -Nur linux-2.6.29.1.orig/arch/mips/kernel/mips_machine.c linux-2.6.29.1/arch/mips/kernel/mips_machine.c +--- linux-2.6.29.1.orig/arch/mips/kernel/mips_machine.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/kernel/mips_machine.c 2009-04-13 14:27:34.683076044 +0200 +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#include <asm/mips_machine.h> ++#include <asm/bootinfo.h> ++ ++static struct list_head mips_machines __initdata = ++ LIST_HEAD_INIT(mips_machines); ++ ++unsigned char mips_machine_name[MIPS_MACHINE_NAME_LEN] = "Unknown"; ++ ++static struct mips_machine * __init mips_machine_find(unsigned long machtype) ++{ ++ struct list_head *this; ++ ++ list_for_each(this, &mips_machines) { ++ struct mips_machine *mach; ++ ++ mach = list_entry(this, struct mips_machine, list); ++ if (mach->mach_type == machtype) ++ return mach; ++ } ++ ++ return NULL; ++} ++ ++void __init mips_machine_register(struct mips_machine *mach) ++{ ++ list_add_tail(&mach->list, &mips_machines); ++} ++ ++void __init mips_machine_setup(unsigned long machtype) ++{ ++ struct mips_machine *mach; ++ ++ mach = mips_machine_find(machtype); ++ if (!mach) { ++ printk(KERN_ALERT "MIPS: no machine registered for " ++ "machtype %lu\n", machtype); ++ return; ++ } ++ ++ if (mach->mach_name[0]) ++ strncpy(mips_machine_name, mach->mach_name, ++ MIPS_MACHINE_NAME_LEN); ++ ++ printk(KERN_INFO "MIPS: machine is %s\n", mips_machine_name); ++ ++ if (mach->mach_setup) ++ mach->mach_setup(); ++} ++ +diff -Nur linux-2.6.29.1.orig/arch/mips/kernel/proc.c linux-2.6.29.1/arch/mips/kernel/proc.c +--- linux-2.6.29.1.orig/arch/mips/kernel/proc.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/kernel/proc.c 2009-04-13 14:27:34.683076044 +0200 +@@ -14,6 +14,7 @@ + #include <asm/cpu-features.h> + #include <asm/mipsregs.h> + #include <asm/processor.h> ++#include <asm/mips_machine.h> + + unsigned int vced_count, vcei_count; + +@@ -33,8 +34,12 @@ + /* + * For the first processor also print the system type + */ +- if (n == 0) ++ if (n == 0) { + seq_printf(m, "system type\t\t: %s\n", get_system_type()); ++#ifdef CONFIG_MIPS_MACHINE ++ seq_printf(m, "machine\t\t\t: %s\n", mips_machine_name); ++#endif ++ } + + seq_printf(m, "processor\t\t: %ld\n", n); + sprintf(fmt, "cpu model\t\t: %%s V%%d.%%d%s\n", +diff -Nur linux-2.6.29.1.orig/arch/mips/kernel/traps.c linux-2.6.29.1/arch/mips/kernel/traps.c +--- linux-2.6.29.1.orig/arch/mips/kernel/traps.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/kernel/traps.c 2009-04-13 14:29:45.479250639 +0200 +@@ -47,6 +47,7 @@ + #include <asm/mmu_context.h> + #include <asm/types.h> + #include <asm/stacktrace.h> ++#include <asm/time.h> + #include <asm/irq.h> + + extern void check_wait(void); +@@ -1541,6 +1542,8 @@ + */ + if (cpu_has_mips_r2) { + cp0_compare_irq = (read_c0_intctl() >> 29) & 7; ++ if (get_c0_compare_irq) ++ cp0_compare_irq = get_c0_compare_irq(); + cp0_perfcount_irq = (read_c0_intctl() >> 26) & 7; + if (cp0_perfcount_irq == cp0_compare_irq) + cp0_perfcount_irq = -1; +diff -Nur linux-2.6.29.1.orig/arch/mips/pci/Makefile linux-2.6.29.1/arch/mips/pci/Makefile +--- linux-2.6.29.1.orig/arch/mips/pci/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/mips/pci/Makefile 2009-04-13 14:27:34.691076784 +0200 +@@ -16,6 +16,7 @@ + obj-$(CONFIG_NEC_MARKEINS) += ops-emma2rh.o pci-emma2rh.o fixup-emma2rh.o + obj-$(CONFIG_PCI_TX4927) += ops-tx4927.o + obj-$(CONFIG_BCM47XX) += pci-bcm47xx.o ++obj-$(CONFIG_ATHEROS_AR71XX) += pci-ar71xx.o + + # + # These are still pretty much in the old state, watch, go blind. +diff -Nur linux-2.6.29.1.orig/arch/mips/pci/pci-ar71xx.c linux-2.6.29.1/arch/mips/pci/pci-ar71xx.c +--- linux-2.6.29.1.orig/arch/mips/pci/pci-ar71xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/arch/mips/pci/pci-ar71xx.c 2009-04-13 14:27:34.695077573 +0200 +@@ -0,0 +1,347 @@ ++/* ++ * Atheros AR71xx PCI host controller driver ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/resource.h> ++#include <linux/types.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/pci.h> ++#include <linux/pci_regs.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/pci.h> ++ ++#undef DEBUG ++#ifdef DEBUG ++#define DBG(fmt, args...) printk(KERN_DEBUG fmt, ## args) ++#else ++#define DBG(fmt, args...) ++#endif ++ ++#define AR71XX_PCI_DELAY 100 /* msecs */ ++ ++#if 0 ++#define PCI_IDSEL_BASE PCI_IDSEL_ADL_START ++#else ++#define PCI_IDSEL_BASE 0 ++#endif ++ ++static unsigned ar71xx_pci_nr_irqs; ++static struct ar71xx_pci_irq *ar71xx_pci_irq_map __initdata; ++static void __iomem *ar71xx_pcicfg_base; ++ ++static DEFINE_SPINLOCK(ar71xx_pci_lock); ++ ++static inline void ar71xx_pci_delay(void) ++{ ++ mdelay(AR71XX_PCI_DELAY); ++} ++ ++static inline u32 ar71xx_pcicfg_rr(unsigned int reg) ++{ ++ return __raw_readl(ar71xx_pcicfg_base + reg); ++} ++ ++static inline void ar71xx_pcicfg_wr(unsigned int reg, u32 val) ++{ ++ __raw_writel(val, ar71xx_pcicfg_base + reg); ++} ++ ++/* Byte lane enable bits */ ++static u8 ble_table[4][4] = { ++ {0x0, 0xf, 0xf, 0xf}, ++ {0xe, 0xd, 0xb, 0x7}, ++ {0xc, 0xf, 0x3, 0xf}, ++ {0xf, 0xf, 0xf, 0xf}, ++}; ++ ++static inline u32 ar71xx_pci_get_ble(int where, int size, int local) ++{ ++ u32 t; ++ ++ t = ble_table[size & 3][where & 3]; ++ BUG_ON(t == 0xf); ++ t <<= (local) ? 20 : 4; ++ return t; ++} ++ ++static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn, ++ int where) ++{ ++ u32 ret; ++ ++ if (!bus->number) { ++ /* type 0 */ ++ ret = (1 << (PCI_IDSEL_BASE + PCI_SLOT(devfn))) ++ | (PCI_FUNC(devfn) << 8) | (where & ~3); ++ } else { ++ /* type 1 */ ++ ret = (bus->number << 16) | (PCI_SLOT(devfn) << 11) ++ | (PCI_FUNC(devfn) << 8) | (where & ~3) | 1; ++ } ++ ++ return ret; ++} ++ ++static int __ar71xx_pci_be_handler(int is_fixup) ++{ ++ u32 pci_err; ++ u32 ahb_err; ++ ++ pci_err = ar71xx_pcicfg_rr(PCI_REG_PCI_ERR) & 3; ++ if (pci_err) { ++ if (!is_fixup) ++ printk(KERN_ALERT "PCI error %d at PCI addr 0x%x\n", ++ pci_err, ++ ar71xx_pcicfg_rr(PCI_REG_PCI_ERR_ADDR)); ++ ++ ar71xx_pcicfg_wr(PCI_REG_PCI_ERR, pci_err); ++ } ++ ++ ahb_err = ar71xx_pcicfg_rr(PCI_REG_AHB_ERR) & 1; ++ if (ahb_err) { ++ if (!is_fixup) ++ printk(KERN_ALERT "AHB error at AHB address 0x%x\n", ++ ar71xx_pcicfg_rr(PCI_REG_AHB_ERR_ADDR)); ++ ++ ar71xx_pcicfg_wr(PCI_REG_AHB_ERR, ahb_err); ++ } ++ ++ return ((ahb_err | pci_err) ? 1 : 0); ++} ++ ++static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus, ++ unsigned int devfn, int where, int size, u32 cmd) ++{ ++ u32 addr; ++ ++ addr = ar71xx_pci_bus_addr(bus, devfn, where); ++ ++ DBG("PCI: set cfgaddr: %02x:%02x.%01x/%02x:%01d, addr=%08x\n", ++ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), ++ where, size, addr); ++ ++ ar71xx_pcicfg_wr(PCI_REG_CFG_AD, addr); ++ ar71xx_pcicfg_wr(PCI_REG_CFG_CBE, ++ cmd | ar71xx_pci_get_ble(where, size, 0)); ++ ++ return __ar71xx_pci_be_handler(1); ++} ++ ++static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ static u32 mask[8] = {0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0}; ++ unsigned long flags; ++ u32 data; ++ int ret; ++ ++ ret = PCIBIOS_SUCCESSFUL; ++ ++ DBG("PCI: read config: %02x:%02x.%01x/%02x:%01d\n", bus->number, ++ PCI_SLOT(devfn), PCI_FUNC(devfn), where, size); ++ ++ spin_lock_irqsave(&ar71xx_pci_lock, flags); ++ ++ if (bus->number == 0 && devfn == 0) { ++ u32 t; ++ ++ t = PCI_CRP_CMD_READ | (where & ~3); ++ ++ ar71xx_pcicfg_wr(PCI_REG_CRP_AD_CBE, t); ++ data = ar71xx_pcicfg_rr(PCI_REG_CRP_RDDATA); ++ ++ DBG("PCI: rd local cfg, ad_cbe:%08x, data:%08x\n", t, data); ++ ++ } else { ++ int err; ++ ++ err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size, ++ PCI_CFG_CMD_READ); ++ ++ if (err == 0) { ++ data = ar71xx_pcicfg_rr(PCI_REG_CFG_RDDATA); ++ } else { ++ ret = PCIBIOS_DEVICE_NOT_FOUND; ++ data = ~0; ++ } ++ } ++ ++ spin_unlock_irqrestore(&ar71xx_pci_lock, flags); ++ ++ DBG("PCI: read config: data=%08x raw=%08x\n", ++ (data >> (8 * (where & 3))) & mask[size & 7], data); ++ ++ *value = (data >> (8 * (where & 3))) & mask[size & 7]; ++ ++ return ret; ++} ++ ++static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ unsigned long flags; ++ int ret; ++ ++ DBG("PCI: write config: %02x:%02x.%01x/%02x:%01d value=%08x\n", ++ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), ++ where, size, value); ++ ++ value = value << (8 * (where & 3)); ++ ret = PCIBIOS_SUCCESSFUL; ++ ++ spin_lock_irqsave(&ar71xx_pci_lock, flags); ++ if (bus->number == 0 && devfn == 0) { ++ u32 t; ++ ++ t = PCI_CRP_CMD_WRITE | (where & ~3); ++ t |= ar71xx_pci_get_ble(where, size, 1); ++ ++ DBG("PCI: wr local cfg, ad_cbe:%08x, value:%08x\n", t, value); ++ ++ ar71xx_pcicfg_wr(PCI_REG_CRP_AD_CBE, t); ++ ar71xx_pcicfg_wr(PCI_REG_CRP_WRDATA, value); ++ } else { ++ int err; ++ ++ err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size, ++ PCI_CFG_CMD_WRITE); ++ ++ if (err == 0) ++ ar71xx_pcicfg_wr(PCI_REG_CFG_WRDATA, value); ++ else ++ ret = PCIBIOS_DEVICE_NOT_FOUND; ++ } ++ spin_unlock_irqrestore(&ar71xx_pci_lock, flags); ++ ++ return ret; ++} ++ ++static void ar71xx_pci_fixup(struct pci_dev *dev) ++{ ++ u32 t; ++ ++ if (dev->bus->number != 0 || dev->devfn != 0) ++ return; ++ ++ DBG("PCI: fixup host controller %s (%04x:%04x)\n", pci_name(dev), ++ dev->vendor, dev->device); ++ ++ /* setup COMMAND register */ ++ t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE ++ | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK; ++ ++ pci_write_config_word(dev, PCI_COMMAND, t); ++} ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ar71xx_pci_fixup); ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) ++{ ++ int irq = -1; ++ int i; ++ ++ slot -= PCI_IDSEL_ADL_START - PCI_IDSEL_BASE; ++ ++ for (i = 0; i < ar71xx_pci_nr_irqs; i++) { ++ struct ar71xx_pci_irq *entry; ++ ++ entry = &ar71xx_pci_irq_map[i]; ++ if (entry->slot == slot && entry->pin == pin) { ++ irq = entry->irq; ++ break; ++ } ++ } ++ ++ if (irq < 0) { ++ printk(KERN_ALERT "PCI: no irq found for pin%u@%s\n", ++ pin, pci_name((struct pci_dev *)dev)); ++ } else { ++ printk(KERN_INFO "PCI: mapping irq %d to pin%u@%s\n", ++ irq, pin, pci_name((struct pci_dev *)dev)); ++ } ++ ++ return irq; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static struct pci_ops ar71xx_pci_ops = { ++ .read = ar71xx_pci_read_config, ++ .write = ar71xx_pci_write_config, ++}; ++ ++static struct resource ar71xx_pci_io_resource = { ++ .name = "PCI IO space", ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_IO, ++}; ++ ++static struct resource ar71xx_pci_mem_resource = { ++ .name = "PCI memory space", ++ .start = AR71XX_PCI_MEM_BASE, ++ .end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1, ++ .flags = IORESOURCE_MEM ++}; ++ ++static struct pci_controller ar71xx_pci_controller = { ++ .pci_ops = &ar71xx_pci_ops, ++ .mem_resource = &ar71xx_pci_mem_resource, ++ .io_resource = &ar71xx_pci_io_resource, ++}; ++ ++static int __init __ar71xx_pci_bios_init(unsigned nr_irqs, ++ struct ar71xx_pci_irq *map) ++{ ++ ar71xx_device_stop(RESET_MODULE_PCI_BUS | RESET_MODULE_PCI_CORE); ++ ar71xx_pci_delay(); ++ ++ ar71xx_device_start(RESET_MODULE_PCI_BUS | RESET_MODULE_PCI_CORE); ++ ar71xx_pci_delay(); ++ ++ ar71xx_pcicfg_base = ioremap_nocache(AR71XX_PCI_CFG_BASE, ++ AR71XX_PCI_CFG_SIZE); ++ ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN0, PCI_WIN0_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN1, PCI_WIN1_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN2, PCI_WIN2_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN3, PCI_WIN3_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN4, PCI_WIN4_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN5, PCI_WIN5_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN6, PCI_WIN6_OFFS); ++ ar71xx_ddr_wr(AR71XX_DDR_REG_PCI_WIN7, PCI_WIN7_OFFS); ++ ++ ar71xx_pci_delay(); ++ ++ /* clear bus errors */ ++ (void)__ar71xx_pci_be_handler(1); ++ ++ ar71xx_pci_nr_irqs = nr_irqs; ++ ar71xx_pci_irq_map = map; ++ ar71xx_pci_be_handler = __ar71xx_pci_be_handler; ++ ++ register_pci_controller(&ar71xx_pci_controller); ++ ++ return 0; ++} ++ ++static int __init __ar71xx_pci_init(void) ++{ ++ ar71xx_pci_bios_init = __ar71xx_pci_bios_init; ++ return 0; ++} ++pure_initcall(__ar71xx_pci_init); +diff -Nur linux-2.6.29.1.orig/drivers/input/misc/Kconfig linux-2.6.29.1/drivers/input/misc/Kconfig +--- linux-2.6.29.1.orig/drivers/input/misc/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/input/misc/Kconfig 2009-04-13 14:27:34.723078345 +0200 +@@ -227,4 +227,20 @@ + Say Y to include support for delivering PMU events via input + layer on NXP PCF50633. + ++config INPUT_GPIO_BUTTONS ++ tristate "Polled GPIO buttons interface" ++ depends on GENERIC_GPIO ++ select INPUT_POLLDEV ++ help ++ This driver implements support for buttons connected ++ to GPIO pins of various CPUs (and some other chips). ++ ++ Say Y here if your device has buttons connected ++ directly to such GPIO pins. Your board-specific ++ setup logic must also provide a platform device, ++ with configuration data saying which GPIOs are used. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called gpio-buttons. ++ + endif +diff -Nur linux-2.6.29.1.orig/drivers/input/misc/Makefile linux-2.6.29.1/drivers/input/misc/Makefile +--- linux-2.6.29.1.orig/drivers/input/misc/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/input/misc/Makefile 2009-04-13 14:29:03.564632184 +0200 +@@ -22,3 +22,4 @@ + obj-$(CONFIG_INPUT_APANEL) += apanel.o + obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o + obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o ++obj-$(CONFIG_INPUT_GPIO_BUTTONS) += gpio_buttons.o +diff -Nur linux-2.6.29.1.orig/drivers/input/misc/gpio_buttons.c linux-2.6.29.1/drivers/input/misc/gpio_buttons.c +--- linux-2.6.29.1.orig/drivers/input/misc/gpio_buttons.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/input/misc/gpio_buttons.c 2009-04-13 14:27:34.727079413 +0200 +@@ -0,0 +1,209 @@ ++/* ++ * Driver for buttons on GPIO lines not capable of generating interrupts ++ * ++ * Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org> ++ * ++ * This file was based on: /drivers/input/misc/cobalt_btns.c ++ * Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> ++ * ++ * also was based on: /drivers/input/keyboard/gpio_keys.c ++ * Copyright 2005 Phil Blundell ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#include <linux/input.h> ++#include <linux/input-polldev.h> ++#include <linux/ioport.h> ++#include <linux/platform_device.h> ++ ++#include <linux/gpio_buttons.h> ++ ++#include <asm/gpio.h> ++ ++#define DRV_NAME "gpio-buttons" ++#define DRV_VERSION "0.1.1" ++#define PFX DRV_NAME ": " ++ ++struct gpio_buttons_dev { ++ struct input_polled_dev *poll_dev; ++ struct gpio_buttons_platform_data *pdata; ++}; ++ ++static void gpio_buttons_poll(struct input_polled_dev *dev) ++{ ++ struct gpio_buttons_dev *bdev = dev->private; ++ struct gpio_buttons_platform_data *pdata = bdev->pdata; ++ struct input_dev *input = dev->input; ++ int i; ++ ++ for (i = 0; i < bdev->pdata->nbuttons; i++) { ++ struct gpio_button *button = &pdata->buttons[i]; ++ unsigned int type = button->type ?: EV_KEY; ++ int state; ++ ++ state = gpio_get_value(button->gpio) ? 1 : 0; ++ state ^= button->active_low; ++ ++ if (state) { ++ button->count++; ++ } else { ++ if (button->count >= button->threshold) { ++ input_event(input, type, button->code, 1); ++ input_sync(input); ++ } ++ button->count = 0; ++ } ++ ++ if (button->count == button->threshold) { ++ input_event(input, type, button->code, 0); ++ input_sync(input); ++ } ++ } ++} ++ ++static int __devinit gpio_buttons_probe(struct platform_device *pdev) ++{ ++ struct gpio_buttons_platform_data *pdata = pdev->dev.platform_data; ++ struct gpio_buttons_dev *bdev; ++ struct input_polled_dev *poll_dev; ++ struct input_dev *input; ++ int error, i; ++ ++ ++ if (!pdata) ++ return -ENXIO; ++ ++ bdev = kzalloc(sizeof(*bdev), GFP_KERNEL); ++ if (!bdev) { ++ printk(KERN_ERR DRV_NAME "no memory for device\n"); ++ return -ENOMEM; ++ } ++ ++ poll_dev = input_allocate_polled_device(); ++ if (!poll_dev) { ++ printk(KERN_ERR DRV_NAME "no memory for polled device\n"); ++ error = -ENOMEM; ++ goto err_free_bdev; ++ } ++ ++ poll_dev->private = bdev; ++ poll_dev->poll = gpio_buttons_poll; ++ poll_dev->poll_interval = pdata->poll_interval; ++ ++ input = poll_dev->input; ++ ++ input->evbit[0] = BIT(EV_KEY); ++ input->name = pdev->name; ++ input->phys = "gpio-buttons/input0"; ++ input->dev.parent = &pdev->dev; ++ ++ input->id.bustype = BUS_HOST; ++ input->id.vendor = 0x0001; ++ input->id.product = 0x0001; ++ input->id.version = 0x0100; ++ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ struct gpio_button *button = &pdata->buttons[i]; ++ unsigned int gpio = button->gpio; ++ unsigned int type = button->type ?: EV_KEY; ++ ++ error = gpio_request(gpio, button->desc ? ++ button->desc : DRV_NAME); ++ if (error) { ++ printk(KERN_ERR PFX "unable to claim gpio %u, " ++ "error %d\n", gpio, error); ++ goto err_free_gpio; ++ } ++ ++ error = gpio_direction_input(gpio); ++ if (error) { ++ printk(KERN_ERR PFX "unable to set direction on " ++ "gpio %u, error %d\n", gpio, error); ++ goto err_free_gpio; ++ } ++ ++ input_set_capability(input, type, button->code); ++ button->count = 0; ++ } ++ ++ bdev->poll_dev = poll_dev; ++ bdev->pdata = pdata; ++ platform_set_drvdata(pdev, bdev); ++ ++ error = input_register_polled_device(poll_dev); ++ if (error) { ++ printk(KERN_ERR PFX "unable to register polled device, " ++ "error %d\n", error); ++ goto err_free_gpio; ++ } ++ ++ return 0; ++ ++err_free_gpio: ++ for (i = i - 1; i >= 0; i--) ++ gpio_free(pdata->buttons[i].gpio); ++ ++ input_free_polled_device(poll_dev); ++ ++err_free_bdev: ++ kfree(bdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ return error; ++} ++ ++static int __devexit gpio_buttons_remove(struct platform_device *pdev) ++{ ++ struct gpio_buttons_dev *bdev = platform_get_drvdata(pdev); ++ struct gpio_buttons_platform_data *pdata = bdev->pdata; ++ int i; ++ ++ input_unregister_polled_device(bdev->poll_dev); ++ ++ for (i = 0; i < pdata->nbuttons; i++) ++ gpio_free(pdata->buttons[i].gpio); ++ ++ input_free_polled_device(bdev->poll_dev); ++ ++ kfree(bdev); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_buttons_driver = { ++ .probe = gpio_buttons_probe, ++ .remove = __devexit_p(gpio_buttons_remove), ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init gpio_buttons_init(void) ++{ ++ printk(KERN_INFO DRV_NAME " driver version " DRV_VERSION "\n"); ++ return platform_driver_register(&gpio_buttons_driver); ++} ++ ++static void __exit gpio_buttons_exit(void) ++{ ++ platform_driver_unregister(&gpio_buttons_driver); ++} ++ ++module_init(gpio_buttons_init); ++module_exit(gpio_buttons_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); ++MODULE_VERSION(DRV_VERSION); ++MODULE_DESCRIPTION("Polled buttons driver for CPU GPIOs"); ++ +diff -Nur linux-2.6.29.1.orig/drivers/mtd/nand/Kconfig linux-2.6.29.1/drivers/mtd/nand/Kconfig +--- linux-2.6.29.1.orig/drivers/mtd/nand/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/mtd/nand/Kconfig 2009-04-13 14:27:34.731079923 +0200 +@@ -427,4 +427,8 @@ + Several Renesas SuperH CPU has FLCTL. This option enables support + for NAND Flash using FLCTL. This driver support SH7723. + ++config MTD_NAND_RB4XX ++ tristate "NAND flash driver for RouterBoard 4xx series" ++ depends on MTD_NAND && ATHEROS_AR71XX ++ + endif # MTD_NAND +diff -Nur linux-2.6.29.1.orig/drivers/mtd/nand/Makefile linux-2.6.29.1/drivers/mtd/nand/Makefile +--- linux-2.6.29.1.orig/drivers/mtd/nand/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/mtd/nand/Makefile 2009-04-13 14:27:34.731079923 +0200 +@@ -29,6 +29,7 @@ + obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o + obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o + obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o ++obj-$(CONFIG_MTD_NAND_RB4XX) += rb4xx_nand.o + obj-$(CONFIG_MTD_ALAUDA) += alauda.o + obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o + obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o +diff -Nur linux-2.6.29.1.orig/drivers/mtd/nand/rb4xx_nand.c linux-2.6.29.1/drivers/mtd/nand/rb4xx_nand.c +--- linux-2.6.29.1.orig/drivers/mtd/nand/rb4xx_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/mtd/nand/rb4xx_nand.c 2009-04-13 14:27:34.735080153 +0200 +@@ -0,0 +1,507 @@ ++/* ++ * NAND flash driver for the MikroTik RouterBoard 4xx series ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This file was based on the driver for Linux 2.6.22 published by ++ * MikroTik for their RouterBoard 4xx series devices. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/gpio.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#define DRV_NAME "rb4xx-nand" ++#define DRV_VERSION "0.1.10" ++#define DRV_DESC "NAND flash driver for RouterBoard 4xx series" ++ ++#define USE_FAST_READ 1 ++#define USE_FAST_WRITE 1 ++#undef RB4XX_NAND_DEBUG ++ ++#ifdef RB4XX_NAND_DEBUG ++#define DBG(fmt, arg...) printk(KERN_DEBUG DRV_NAME ": " fmt, ## arg) ++#else ++#define DBG(fmt, arg...) do {} while (0) ++#endif ++ ++#define RB4XX_NAND_GPIO_RDY 5 ++#define RB4XX_FLASH_HZ 33333334 ++#define RB4XX_NAND_HZ 33333334 ++ ++#define SPI_CTRL_FASTEST 0x40 ++#define SPI_CTRL_SAFE 0x43 /* 25 MHz for AHB 200 MHz */ ++#define SBIT_IOC_BASE SPI_IOC_CS1 ++#define SBIT_IOC_DO_SHIFT 0 ++#define SBIT_IOC_DO (1u << SBIT_IOC_DO_SHIFT) ++#define SBIT_IOC_DO2_SHIFT 18 ++#define SBIT_IOC_DO2 (1u << SBIT_IOC_DO2_SHIFT) ++ ++#define CPLD_CMD_WRITE_MULT 0x08 /* send cmd, n x send data, read data */ ++#define CPLD_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */ ++#define CPLD_CMD_READ_MULT 0x0a /* send cmd, send idle, n x read data */ ++#define CPLD_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */ ++ ++#define CFG_BIT_nCE 0x80 ++#define CFG_BIT_CLE 0x40 ++#define CFG_BIT_ALE 0x20 ++#define CFG_BIT_FAN 0x10 ++#define CFG_BIT_nLED4 0x08 ++#define CFG_BIT_nLED3 0x04 ++#define CFG_BIT_nLED2 0x02 ++#define CFG_BIT_nLED1 0x01 ++ ++#define CFG_BIT_nLEDS \ ++ (CFG_BIT_nLED1 | CFG_BIT_nLED2 | CFG_BIT_nLED3 | CFG_BIT_nLED4) ++ ++struct rb4xx_nand_info { ++ struct nand_chip chip; ++ struct mtd_info mtd; ++}; ++ ++/* ++ * We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader ++ * will not be able to find the kernel that we load. ++ */ ++static struct nand_ecclayout rb4xx_nand_ecclayout = { ++ .eccbytes = 6, ++ .eccpos = { 8, 9, 10, 13, 14, 15 }, ++ .oobavail = 9, ++ .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } ++}; ++ ++static struct mtd_partition rb4xx_nand_partitions[] = { ++ { ++ .name = "booter", ++ .offset = 0, ++ .size = (256 * 1024), ++ .mask_flags = MTD_WRITEABLE, ++ }, ++ { ++ .name = "kernel", ++ .offset = (256 * 1024), ++ .size = (4 * 1024 * 1024) - (256 * 1024), ++ }, ++ { ++ .name = "rootfs", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, ++ }, ++}; ++ ++#if USE_FAST_READ ++#define SPI_NDATA_BASE 0x00800000 ++static unsigned spi_ctrl_fread = SPI_CTRL_SAFE; ++static unsigned spi_ctrl_flash = SPI_CTRL_SAFE; ++extern unsigned mips_hpt_frequency; ++#endif ++ ++static inline unsigned rb4xx_spi_rreg(unsigned r) ++{ ++ return __raw_readl((void * __iomem)(KSEG1ADDR(AR71XX_SPI_BASE) + r)); ++} ++ ++static inline void rb4xx_spi_wreg(unsigned r, unsigned v) ++{ ++ __raw_writel(v, (void * __iomem)(KSEG1ADDR(AR71XX_SPI_BASE) + r)); ++} ++ ++static inline void do_spi_clk(int bit) ++{ ++ unsigned bval = SBIT_IOC_BASE | (bit & 1); ++ ++ rb4xx_spi_wreg(SPI_REG_IOC, bval); ++ rb4xx_spi_wreg(SPI_REG_IOC, bval | SPI_IOC_CLK); ++} ++ ++static void do_spi_byte(uint8_t byte) ++{ ++ do_spi_clk(byte >> 7); ++ do_spi_clk(byte >> 6); ++ do_spi_clk(byte >> 5); ++ do_spi_clk(byte >> 4); ++ do_spi_clk(byte >> 3); ++ do_spi_clk(byte >> 2); ++ do_spi_clk(byte >> 1); ++ do_spi_clk(byte); ++ ++ DBG("spi_byte sent 0x%02x got 0x%x\n", ++ byte, rb4xx_spi_rreg(SPI_REG_RDS)); ++} ++ ++#if USE_FAST_WRITE ++static inline void do_spi_clk_fast(int bit1, int bit2) ++{ ++ unsigned bval = (SBIT_IOC_BASE | ++ ((bit1 << SBIT_IOC_DO_SHIFT) & SBIT_IOC_DO) | ++ ((bit2 << SBIT_IOC_DO2_SHIFT) & SBIT_IOC_DO2)); ++ ++ rb4xx_spi_wreg(SPI_REG_IOC, bval); ++ rb4xx_spi_wreg(SPI_REG_IOC, bval | SPI_IOC_CLK); ++} ++ ++static inline void do_spi_byte_fast(uint8_t byte) ++{ ++ do_spi_clk_fast(byte >> 7, byte >> 6); ++ do_spi_clk_fast(byte >> 5, byte >> 4); ++ do_spi_clk_fast(byte >> 3, byte >> 2); ++ do_spi_clk_fast(byte >> 1, byte >> 0); ++ ++ DBG("spi_byte_fast sent 0x%02x got 0x%x\n", ++ byte, rb4xx_spi_rreg(SPI_REG_RDS)); ++} ++#else ++static inline void do_spi_byte_fast(uint8_t byte) ++{ ++ do_spi_byte(byte); ++} ++#endif /* USE_FAST_WRITE */ ++ ++static int do_spi_cmd(unsigned cmd, unsigned sendCnt, const uint8_t *sendData, ++ unsigned recvCnt, uint8_t *recvData, ++ const uint8_t *verifyData, int fastWrite) ++{ ++ unsigned i; ++ ++ DBG("SPI cmd 0x%x send %u recv %u\n", cmd, sendCnt, recvCnt); ++ ++ rb4xx_spi_wreg(SPI_REG_FS, SPI_FS_GPIO); ++ rb4xx_spi_wreg(SPI_REG_CTRL, SPI_CTRL_FASTEST); ++ ++ do_spi_byte(cmd); ++#if 0 ++ if (cmd == CPLD_CMD_READ_FAST) { ++ do_spi_byte(0x80); ++ do_spi_byte(0); ++ do_spi_byte(0); ++ } ++#endif ++ for (i = 0; i < sendCnt; ++i) { ++ if (fastWrite) ++ do_spi_byte_fast(sendData[i]); ++ else ++ do_spi_byte(sendData[i]); ++ } ++ ++ for (i = 0; i < recvCnt; ++i) { ++ if (fastWrite) ++ do_spi_byte_fast(0); ++ else ++ do_spi_byte(0); ++ ++ if (recvData) { ++ recvData[i] = rb4xx_spi_rreg(SPI_REG_RDS) & 0xff; ++ } else if (verifyData) { ++ if (verifyData[i] != (rb4xx_spi_rreg(SPI_REG_RDS) ++ & 0xff)) ++ break; ++ } ++ } ++ ++ rb4xx_spi_wreg(SPI_REG_IOC, SBIT_IOC_BASE | SPI_IOC_CS0); ++ rb4xx_spi_wreg(SPI_REG_CTRL, spi_ctrl_flash); ++ rb4xx_spi_wreg(SPI_REG_FS, 0); ++ ++ return i == recvCnt; ++} ++ ++static int got_write = 1; ++ ++static void rb4xx_nand_write_data(const uint8_t *byte, unsigned cnt) ++{ ++ do_spi_cmd(CPLD_CMD_WRITE_MULT, cnt, byte, 1, NULL, NULL, 1); ++ got_write = 1; ++} ++ ++static void rb4xx_nand_write_byte(uint8_t byte) ++{ ++ rb4xx_nand_write_data(&byte, 1); ++} ++ ++#if USE_FAST_READ ++static uint8_t *rb4xx_nand_read_getaddr(unsigned cnt) ++{ ++ static unsigned nboffset = 0x100000; ++ unsigned addr; ++ ++ if (got_write) { ++ nboffset = (nboffset + 31) & ~31; ++ if (nboffset >= 0x100000) /* 1MB */ ++ nboffset = 0; ++ ++ got_write = 0; ++ rb4xx_spi_wreg(SPI_REG_FS, SPI_FS_GPIO); ++ rb4xx_spi_wreg(SPI_REG_CTRL, spi_ctrl_fread); ++ rb4xx_spi_wreg(SPI_REG_FS, 0); ++ } ++ ++ addr = KSEG1ADDR(AR71XX_SPI_BASE + SPI_NDATA_BASE) + nboffset; ++ DBG("rb4xx_nand_read_getaddr 0x%x cnt 0x%x\n", addr, cnt); ++ ++ nboffset += cnt; ++ return (uint8_t *)addr; ++} ++ ++static void rb4xx_nand_read_data(uint8_t *buf, unsigned cnt) ++{ ++ unsigned size32 = cnt & ~31; ++ unsigned remain = cnt & 31; ++ ++ if (size32) { ++ uint8_t *addr = rb4xx_nand_read_getaddr(size32); ++ memcpy(buf, (void *)addr, size32); ++ } ++ ++ if (remain) { ++ do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, remain, ++ buf + size32, NULL, 0); ++ } ++} ++ ++static int rb4xx_nand_verify_data(const uint8_t *buf, unsigned cnt) ++{ ++ unsigned size32 = cnt & ~31; ++ unsigned remain = cnt & 31; ++ ++ if (size32) { ++ uint8_t *addr = rb4xx_nand_read_getaddr(size32); ++ if (memcmp(buf, (void *)addr, size32) != 0) ++ return 0; ++ } ++ ++ if (remain) { ++ return do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, remain, ++ NULL, buf + size32, 0); ++ } ++ return 1; ++} ++#else /* USE_FAST_READ */ ++static void rb4xx_nand_read_data(uint8_t *buf, unsigned cnt) ++{ ++ do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, cnt, buf, NULL, 0); ++} ++ ++static int rb4xx_nand_verify_data(const uint8_t *buf, unsigned cnt) ++{ ++ return do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, cnt, NULL, buf, 0); ++} ++#endif /* USE_FAST_READ */ ++ ++static void rb4xx_nand_write_cfg(uint8_t byte) ++{ ++ do_spi_cmd(CPLD_CMD_WRITE_CFG, 1, &byte, 0, NULL, NULL, 0); ++ got_write = 1; ++} ++ ++static int rb4xx_nand_dev_ready(struct mtd_info *mtd) ++{ ++ return gpio_get_value(RB4XX_NAND_GPIO_RDY); ++} ++ ++static void rb4xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ uint8_t cfg = CFG_BIT_nLEDS; ++ ++ cfg |= (ctrl & NAND_CLE) ? CFG_BIT_CLE : 0; ++ cfg |= (ctrl & NAND_ALE) ? CFG_BIT_ALE : 0; ++ cfg |= (ctrl & NAND_NCE) ? 0 : CFG_BIT_nCE; ++ ++ rb4xx_nand_write_cfg(cfg); ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ rb4xx_nand_write_byte(cmd); ++} ++ ++static uint8_t rb4xx_nand_read_byte(struct mtd_info *mtd) ++{ ++ uint8_t byte = 0; ++ ++ rb4xx_nand_read_data(&byte, 1); ++ return byte; ++} ++ ++static void rb4xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ rb4xx_nand_write_data(buf, len); ++} ++ ++static void rb4xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, ++ int len) ++{ ++ rb4xx_nand_read_data(buf, len); ++} ++ ++static int rb4xx_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ if (!rb4xx_nand_verify_data(buf, len)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static unsigned get_spi_ctrl(unsigned hz_max, const char *name) ++{ ++ unsigned div; ++ ++ div = (ar71xx_ahb_freq - 1) / (2 * hz_max); ++ /* ++ * CPU has a bug at (div == 0) - first bit read is random ++ */ ++ if (div == 0) ++ ++div; ++ ++ if (name) { ++ unsigned ahb_khz = (ar71xx_ahb_freq + 500) / 1000; ++ unsigned div_real = 2 * (div + 1); ++ printk(KERN_INFO "%s SPI clock %u kHz (AHB %u kHz / %u)\n", ++ name, ++ ahb_khz / div_real, ++ ahb_khz, div_real); ++ } ++ ++ return SPI_CTRL_FASTEST + div; ++} ++ ++static int __init rb4xx_nand_probe(struct platform_device *pdev) ++{ ++ struct rb4xx_nand_info *info; ++ int ret; ++ ++ printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n"); ++ ++ ret = gpio_request(RB4XX_NAND_GPIO_RDY, "NAND RDY"); ++ if (ret) { ++ printk(KERN_ERR "rb4xx-nand: gpio request failed\n"); ++ return ret; ++ } ++ ++ ret = gpio_direction_input(RB4XX_NAND_GPIO_RDY); ++ if (ret) { ++ printk(KERN_ERR "rb4xx-nand: unable to set input mode " ++ "on gpio%d\n", RB4XX_NAND_GPIO_RDY); ++ goto err_free_gpio; ++ } ++ ++ info = kzalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) { ++ printk(KERN_ERR "rb4xx-nand: no memory for private data\n"); ++ ret = -ENOMEM; ++ goto err_free_gpio; ++ } ++ ++#if USE_FAST_READ ++ spi_ctrl_fread = get_spi_ctrl(RB4XX_NAND_HZ, "NAND"); ++#endif ++ spi_ctrl_flash = get_spi_ctrl(RB4XX_FLASH_HZ, "FLASH"); ++ ++ rb4xx_nand_write_cfg(CFG_BIT_nLEDS | CFG_BIT_nCE); ++ ++ info->chip.priv = &info; ++ info->mtd.priv = &info->chip; ++ info->mtd.owner = THIS_MODULE; ++ ++ info->chip.cmd_ctrl = rb4xx_nand_cmd_ctrl; ++ info->chip.dev_ready = rb4xx_nand_dev_ready; ++ info->chip.read_byte = rb4xx_nand_read_byte; ++ info->chip.write_buf = rb4xx_nand_write_buf; ++ info->chip.read_buf = rb4xx_nand_read_buf; ++ info->chip.verify_buf = rb4xx_nand_verify_buf; ++ ++ info->chip.chip_delay = 25; ++ info->chip.ecc.mode = NAND_ECC_SOFT; ++ info->chip.options |= NAND_NO_AUTOINCR; ++ ++ platform_set_drvdata(pdev, info); ++ ++ ret = nand_scan_ident(&info->mtd, 1); ++ if (ret) { ++ ret = -ENXIO; ++ goto err_free_info; ++ } ++ ++ if (info->mtd.writesize == 512) ++ info->chip.ecc.layout = &rb4xx_nand_ecclayout; ++ ++ ret = nand_scan_tail(&info->mtd); ++ if (ret) { ++ return -ENXIO; ++ goto err_set_drvdata; ++ } ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ret = add_mtd_partitions(&info->mtd, rb4xx_nand_partitions, ++ ARRAY_SIZE(rb4xx_nand_partitions)); ++#else ++ ret = add_mtd_device(&info->mtd); ++#endif ++ if (ret) ++ goto err_release_nand; ++ ++ return 0; ++ ++err_release_nand: ++ nand_release(&info->mtd); ++err_set_drvdata: ++ platform_set_drvdata(pdev, NULL); ++err_free_info: ++ kfree(info); ++err_free_gpio: ++ gpio_free(RB4XX_NAND_GPIO_RDY); ++ return ret; ++} ++ ++static int __devexit rb4xx_nand_remove(struct platform_device *pdev) ++{ ++ struct rb4xx_nand_info *info = platform_get_drvdata(pdev); ++ ++ nand_release(&info->mtd); ++ platform_set_drvdata(pdev, NULL); ++ kfree(info); ++ ++ return 0; ++} ++ ++static struct platform_driver rb4xx_nand_driver = { ++ .probe = rb4xx_nand_probe, ++ .remove = __devexit_p(rb4xx_nand_remove), ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init rb4xx_nand_init(void) ++{ ++ return platform_driver_register(&rb4xx_nand_driver); ++} ++ ++static void __exit rb4xx_nand_exit(void) ++{ ++ platform_driver_unregister(&rb4xx_nand_driver); ++} ++ ++module_init(rb4xx_nand_init); ++module_exit(rb4xx_nand_exit); ++ ++MODULE_DESCRIPTION(DRV_DESC); ++MODULE_VERSION(DRV_VERSION); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); ++MODULE_LICENSE("GPL v2"); +diff -Nur linux-2.6.29.1.orig/drivers/net/Kconfig linux-2.6.29.1/drivers/net/Kconfig +--- linux-2.6.29.1.orig/drivers/net/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/net/Kconfig 2009-04-13 14:27:34.763080366 +0200 +@@ -1945,6 +1945,8 @@ + + The safe and default value for this is N. + ++source drivers/net/ag71xx/Kconfig ++ + config DL2K + tristate "DL2000/TC902x-based Gigabit Ethernet support" + depends on PCI +diff -Nur linux-2.6.29.1.orig/drivers/net/Makefile linux-2.6.29.1/drivers/net/Makefile +--- linux-2.6.29.1.orig/drivers/net/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/net/Makefile 2009-04-13 14:27:34.767081713 +0200 +@@ -2,6 +2,7 @@ + # Makefile for the Linux network (ethercard) device drivers. + # + ++obj-$(CONFIG_AG71XX) += ag71xx/ + obj-$(CONFIG_E1000) += e1000/ + obj-$(CONFIG_E1000E) += e1000e/ + obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/ +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/Kconfig linux-2.6.29.1/drivers/net/ag71xx/Kconfig +--- linux-2.6.29.1.orig/drivers/net/ag71xx/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/Kconfig 2009-04-13 14:27:34.767081713 +0200 +@@ -0,0 +1,23 @@ ++config AG71XX ++ tristate "Atheros AR71xx built-in ethernet mac support" ++ depends on ATHEROS_AR71XX ++ select PHYLIB ++ help ++ If you wish to compile a kernel for AR71xx/91xx and enable ++ ethernet support, then you should always answer Y to this. ++ ++config AG71XX_DEBUG ++ bool "Atheros AR71xx built-in ethernet driver debugging" ++ depends on AG71XX ++ default n ++ help ++ Atheros AR71xx built-in ethernet driver debugging messages. ++ ++config AG71XX_AR8216_SUPPORT ++ bool "special support for the Atheros AR8216 switch" ++ depends on AG71XX ++ default n ++ default y if AR71XX_MACH_WNR2000 || AR71XX_MACH_MZK_W04NU ++ help ++ Say 'y' here if you want to enable special support for the ++ Atheros AR8216 switch found on some boards. +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/Makefile linux-2.6.29.1/drivers/net/ag71xx/Makefile +--- linux-2.6.29.1.orig/drivers/net/ag71xx/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/Makefile 2009-04-13 14:27:34.771082502 +0200 +@@ -0,0 +1,13 @@ ++# ++# Makefile for the Atheros AR71xx built-in ethernet macs ++# ++ ++ag71xx-y += ag71xx_main.o ++ag71xx-y += ag71xx_ethtool.o ++ag71xx-y += ag71xx_phy.o ++ag71xx-y += ag71xx_mdio.o ++ ++ag71xx-$(CONFIG_AG71XX_AR8216_SUPPORT) += ag71xx_ar8216.o ++ ++obj-$(CONFIG_AG71XX) += ag71xx.o ++ +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx.h linux-2.6.29.1/drivers/net/ag71xx/ag71xx.h +--- linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/ag71xx.h 2009-04-13 14:27:34.799084111 +0200 +@@ -0,0 +1,449 @@ ++/* ++ * Atheros AR71xx built-in ethernet mac driver ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Based on Atheros' AG7100 driver ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef __AG71XX_H ++#define __AG71XX_H ++ ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/random.h> ++#include <linux/spinlock.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/ethtool.h> ++#include <linux/etherdevice.h> ++#include <linux/phy.h> ++#include <linux/skbuff.h> ++#include <linux/dma-mapping.h> ++#include <linux/workqueue.h> ++ ++#include <linux/bitops.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/platform.h> ++ ++#define ETH_FCS_LEN 4 ++ ++#define AG71XX_DRV_NAME "ag71xx" ++#define AG71XX_DRV_VERSION "0.5.21" ++ ++#define AG71XX_NAPI_WEIGHT 64 ++#define AG71XX_OOM_REFILL (1 + HZ/10) ++ ++#define AG71XX_INT_ERR (AG71XX_INT_RX_BE | AG71XX_INT_TX_BE) ++#define AG71XX_INT_TX (AG71XX_INT_TX_PS) ++#define AG71XX_INT_RX (AG71XX_INT_RX_PR | AG71XX_INT_RX_OF) ++ ++#define AG71XX_INT_POLL (AG71XX_INT_RX | AG71XX_INT_TX) ++#define AG71XX_INT_INIT (AG71XX_INT_ERR | AG71XX_INT_POLL) ++ ++#define AG71XX_TX_FIFO_LEN 2048 ++#define AG71XX_TX_MTU_LEN 1536 ++#define AG71XX_RX_PKT_RESERVE 64 ++#define AG71XX_RX_PKT_SIZE \ ++ (AG71XX_RX_PKT_RESERVE + ETH_HLEN + ETH_FRAME_LEN + ETH_FCS_LEN) ++ ++#define AG71XX_TX_RING_SIZE 64 ++#define AG71XX_TX_THRES_STOP (AG71XX_TX_RING_SIZE - 4) ++#define AG71XX_TX_THRES_WAKEUP \ ++ (AG71XX_TX_RING_SIZE - (AG71XX_TX_RING_SIZE / 4)) ++ ++#define AG71XX_RX_RING_SIZE 128 ++ ++#ifdef CONFIG_AG71XX_DEBUG ++#define DBG(fmt, args...) printk(KERN_DEBUG fmt, ## args) ++#else ++#define DBG(fmt, args...) do {} while (0) ++#endif ++ ++#define ag71xx_assert(_cond) \ ++do { \ ++ if (_cond) \ ++ break; \ ++ printk("%s,%d: assertion failed\n", __FILE__, __LINE__); \ ++ BUG(); \ ++} while (0) ++ ++struct ag71xx_desc { ++ u32 data; ++ u32 ctrl; ++#define DESC_EMPTY BIT(31) ++#define DESC_MORE BIT(24) ++#define DESC_PKTLEN_M 0x1fff ++ u32 next; ++ u32 pad; ++}; ++ ++struct ag71xx_buf { ++ struct sk_buff *skb; ++}; ++ ++struct ag71xx_ring { ++ struct ag71xx_buf *buf; ++ struct ag71xx_desc *descs; ++ dma_addr_t descs_dma; ++ unsigned int curr; ++ unsigned int dirty; ++ unsigned int size; ++}; ++ ++struct ag71xx_mdio { ++ struct mii_bus *mii_bus; ++ int mii_irq[PHY_MAX_ADDR]; ++ void __iomem *mdio_base; ++}; ++ ++struct ag71xx { ++ void __iomem *mac_base; ++ void __iomem *mac_base2; ++ void __iomem *mii_ctrl; ++ ++ spinlock_t lock; ++ struct platform_device *pdev; ++ struct net_device *dev; ++ struct napi_struct napi; ++ u32 msg_enable; ++ ++ struct ag71xx_ring rx_ring; ++ struct ag71xx_ring tx_ring; ++ ++ struct mii_bus *mii_bus; ++ struct phy_device *phy_dev; ++ ++ unsigned int link; ++ unsigned int speed; ++ int duplex; ++ ++ struct work_struct restart_work; ++ struct timer_list oom_timer; ++}; ++ ++extern struct ethtool_ops ag71xx_ethtool_ops; ++ ++extern struct ag71xx_mdio *ag71xx_mdio_bus; ++extern int ag71xx_mdio_driver_init(void) __init; ++extern void ag71xx_mdio_driver_exit(void); ++ ++extern int ag71xx_phy_connect(struct ag71xx *ag); ++extern void ag71xx_phy_disconnect(struct ag71xx *ag); ++extern void ag71xx_phy_start(struct ag71xx *ag); ++extern void ag71xx_phy_stop(struct ag71xx *ag); ++ ++static inline struct ag71xx_platform_data *ag71xx_get_pdata(struct ag71xx *ag) ++{ ++ return ag->pdev->dev.platform_data; ++} ++ ++static inline int ag71xx_desc_empty(struct ag71xx_desc *desc) ++{ ++ return ((desc->ctrl & DESC_EMPTY) != 0); ++} ++ ++static inline int ag71xx_desc_pktlen(struct ag71xx_desc *desc) ++{ ++ return (desc->ctrl & DESC_PKTLEN_M); ++} ++ ++/* Register offsets */ ++#define AG71XX_REG_MAC_CFG1 0x0000 ++#define AG71XX_REG_MAC_CFG2 0x0004 ++#define AG71XX_REG_MAC_IPG 0x0008 ++#define AG71XX_REG_MAC_HDX 0x000c ++#define AG71XX_REG_MAC_MFL 0x0010 ++#define AG71XX_REG_MII_CFG 0x0020 ++#define AG71XX_REG_MII_CMD 0x0024 ++#define AG71XX_REG_MII_ADDR 0x0028 ++#define AG71XX_REG_MII_CTRL 0x002c ++#define AG71XX_REG_MII_STATUS 0x0030 ++#define AG71XX_REG_MII_IND 0x0034 ++#define AG71XX_REG_MAC_IFCTL 0x0038 ++#define AG71XX_REG_MAC_ADDR1 0x0040 ++#define AG71XX_REG_MAC_ADDR2 0x0044 ++#define AG71XX_REG_FIFO_CFG0 0x0048 ++#define AG71XX_REG_FIFO_CFG1 0x004c ++#define AG71XX_REG_FIFO_CFG2 0x0050 ++#define AG71XX_REG_FIFO_CFG3 0x0054 ++#define AG71XX_REG_FIFO_CFG4 0x0058 ++#define AG71XX_REG_FIFO_CFG5 0x005c ++#define AG71XX_REG_FIFO_RAM0 0x0060 ++#define AG71XX_REG_FIFO_RAM1 0x0064 ++#define AG71XX_REG_FIFO_RAM2 0x0068 ++#define AG71XX_REG_FIFO_RAM3 0x006c ++#define AG71XX_REG_FIFO_RAM4 0x0070 ++#define AG71XX_REG_FIFO_RAM5 0x0074 ++#define AG71XX_REG_FIFO_RAM6 0x0078 ++#define AG71XX_REG_FIFO_RAM7 0x007c ++ ++#define AG71XX_REG_TX_CTRL 0x0180 ++#define AG71XX_REG_TX_DESC 0x0184 ++#define AG71XX_REG_TX_STATUS 0x0188 ++#define AG71XX_REG_RX_CTRL 0x018c ++#define AG71XX_REG_RX_DESC 0x0190 ++#define AG71XX_REG_RX_STATUS 0x0194 ++#define AG71XX_REG_INT_ENABLE 0x0198 ++#define AG71XX_REG_INT_STATUS 0x019c ++ ++#define MAC_CFG1_TXE BIT(0) /* Tx Enable */ ++#define MAC_CFG1_STX BIT(1) /* Synchronize Tx Enable */ ++#define MAC_CFG1_RXE BIT(2) /* Rx Enable */ ++#define MAC_CFG1_SRX BIT(3) /* Synchronize Rx Enable */ ++#define MAC_CFG1_TFC BIT(4) /* Tx Flow Control Enable */ ++#define MAC_CFG1_RFC BIT(5) /* Rx Flow Control Enable */ ++#define MAC_CFG1_LB BIT(8) /* Loopback mode */ ++#define MAC_CFG1_SR BIT(31) /* Soft Reset */ ++ ++#define MAC_CFG2_FDX BIT(0) ++#define MAC_CFG2_CRC_EN BIT(1) ++#define MAC_CFG2_PAD_CRC_EN BIT(2) ++#define MAC_CFG2_LEN_CHECK BIT(4) ++#define MAC_CFG2_HUGE_FRAME_EN BIT(5) ++#define MAC_CFG2_IF_1000 BIT(9) ++#define MAC_CFG2_IF_10_100 BIT(8) ++ ++#define FIFO_CFG0_WTM BIT(0) /* Watermark Module */ ++#define FIFO_CFG0_RXS BIT(1) /* Rx System Module */ ++#define FIFO_CFG0_RXF BIT(2) /* Rx Fabric Module */ ++#define FIFO_CFG0_TXS BIT(3) /* Tx System Module */ ++#define FIFO_CFG0_TXF BIT(4) /* Tx Fabric Module */ ++#define FIFO_CFG0_ALL (FIFO_CFG0_WTM | FIFO_CFG0_RXS | FIFO_CFG0_RXF \ ++ | FIFO_CFG0_TXS | FIFO_CFG0_TXF) ++ ++#define FIFO_CFG0_ENABLE_SHIFT 8 ++ ++#define FIFO_CFG4_DE BIT(0) /* Drop Event */ ++#define FIFO_CFG4_DV BIT(1) /* RX_DV Event */ ++#define FIFO_CFG4_FC BIT(2) /* False Carrier */ ++#define FIFO_CFG4_CE BIT(3) /* Code Error */ ++#define FIFO_CFG4_CR BIT(4) /* CRC error */ ++#define FIFO_CFG4_LM BIT(5) /* Length Mismatch */ ++#define FIFO_CFG4_LO BIT(6) /* Length out of range */ ++#define FIFO_CFG4_OK BIT(7) /* Packet is OK */ ++#define FIFO_CFG4_MC BIT(8) /* Multicast Packet */ ++#define FIFO_CFG4_BC BIT(9) /* Broadcast Packet */ ++#define FIFO_CFG4_DR BIT(10) /* Dribble */ ++#define FIFO_CFG4_LE BIT(11) /* Long Event */ ++#define FIFO_CFG4_CF BIT(12) /* Control Frame */ ++#define FIFO_CFG4_PF BIT(13) /* Pause Frame */ ++#define FIFO_CFG4_UO BIT(14) /* Unsupported Opcode */ ++#define FIFO_CFG4_VT BIT(15) /* VLAN tag detected */ ++#define FIFO_CFG4_FT BIT(16) /* Frame Truncated */ ++#define FIFO_CFG4_UC BIT(17) /* Unicast Packet */ ++ ++#define FIFO_CFG5_DE BIT(0) /* Drop Event */ ++#define FIFO_CFG5_DV BIT(1) /* RX_DV Event */ ++#define FIFO_CFG5_FC BIT(2) /* False Carrier */ ++#define FIFO_CFG5_CE BIT(3) /* Code Error */ ++#define FIFO_CFG5_LM BIT(4) /* Length Mismatch */ ++#define FIFO_CFG5_LO BIT(5) /* Length Out of Range */ ++#define FIFO_CFG5_OK BIT(6) /* Packet is OK */ ++#define FIFO_CFG5_MC BIT(7) /* Multicast Packet */ ++#define FIFO_CFG5_BC BIT(8) /* Broadcast Packet */ ++#define FIFO_CFG5_DR BIT(9) /* Dribble */ ++#define FIFO_CFG5_CF BIT(10) /* Control Frame */ ++#define FIFO_CFG5_PF BIT(11) /* Pause Frame */ ++#define FIFO_CFG5_UO BIT(12) /* Unsupported Opcode */ ++#define FIFO_CFG5_VT BIT(13) /* VLAN tag detected */ ++#define FIFO_CFG5_LE BIT(14) /* Long Event */ ++#define FIFO_CFG5_FT BIT(15) /* Frame Truncated */ ++#define FIFO_CFG5_16 BIT(16) /* unknown */ ++#define FIFO_CFG5_17 BIT(17) /* unknown */ ++#define FIFO_CFG5_SF BIT(18) /* Short Frame */ ++#define FIFO_CFG5_BM BIT(19) /* Byte Mode */ ++ ++#define AG71XX_INT_TX_PS BIT(0) ++#define AG71XX_INT_TX_UR BIT(1) ++#define AG71XX_INT_TX_BE BIT(3) ++#define AG71XX_INT_RX_PR BIT(4) ++#define AG71XX_INT_RX_OF BIT(6) ++#define AG71XX_INT_RX_BE BIT(7) ++ ++#define MAC_IFCTL_SPEED BIT(16) ++ ++#define MII_CFG_CLK_DIV_4 0 ++#define MII_CFG_CLK_DIV_6 2 ++#define MII_CFG_CLK_DIV_8 3 ++#define MII_CFG_CLK_DIV_10 4 ++#define MII_CFG_CLK_DIV_14 5 ++#define MII_CFG_CLK_DIV_20 6 ++#define MII_CFG_CLK_DIV_28 7 ++#define MII_CFG_RESET BIT(31) ++ ++#define MII_CMD_WRITE 0x0 ++#define MII_CMD_READ 0x1 ++#define MII_ADDR_SHIFT 8 ++#define MII_IND_BUSY BIT(0) ++#define MII_IND_INVALID BIT(2) ++ ++#define TX_CTRL_TXE BIT(0) /* Tx Enable */ ++ ++#define TX_STATUS_PS BIT(0) /* Packet Sent */ ++#define TX_STATUS_UR BIT(1) /* Tx Underrun */ ++#define TX_STATUS_BE BIT(3) /* Bus Error */ ++ ++#define RX_CTRL_RXE BIT(0) /* Rx Enable */ ++ ++#define RX_STATUS_PR BIT(0) /* Packet Received */ ++#define RX_STATUS_OF BIT(2) /* Rx Overflow */ ++#define RX_STATUS_BE BIT(3) /* Bus Error */ ++ ++#define MII_CTRL_IF_MASK 3 ++#define MII_CTRL_SPEED_SHIFT 4 ++#define MII_CTRL_SPEED_MASK 3 ++#define MII_CTRL_SPEED_10 0 ++#define MII_CTRL_SPEED_100 1 ++#define MII_CTRL_SPEED_1000 2 ++ ++static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value) ++{ ++ void __iomem *r; ++ ++ switch (reg) { ++ case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL: ++ r = ag->mac_base + reg; ++ __raw_writel(value, r); ++ __raw_readl(r); ++ break; ++ case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS: ++ r = ag->mac_base2 + reg - AG71XX_REG_MAC_IFCTL; ++ __raw_writel(value, r); ++ __raw_readl(r); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg) ++{ ++ void __iomem *r; ++ u32 ret; ++ ++ switch (reg) { ++ case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL: ++ r = ag->mac_base + reg; ++ ret = __raw_readl(r); ++ break; ++ case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS: ++ r = ag->mac_base2 + reg - AG71XX_REG_MAC_IFCTL; ++ ret = __raw_readl(r); ++ break; ++ default: ++ BUG(); ++ } ++ ++ return ret; ++} ++ ++static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask) ++{ ++ void __iomem *r; ++ ++ switch (reg) { ++ case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL: ++ r = ag->mac_base + reg; ++ __raw_writel(__raw_readl(r) | mask, r); ++ __raw_readl(r); ++ break; ++ case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS: ++ r = ag->mac_base2 + reg - AG71XX_REG_MAC_IFCTL; ++ __raw_writel(__raw_readl(r) | mask, r); ++ __raw_readl(r); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask) ++{ ++ void __iomem *r; ++ ++ switch (reg) { ++ case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL: ++ r = ag->mac_base + reg; ++ __raw_writel(__raw_readl(r) & ~mask, r); ++ __raw_readl(r); ++ break; ++ case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS: ++ r = ag->mac_base2 + reg - AG71XX_REG_MAC_IFCTL; ++ __raw_writel(__raw_readl(r) & ~mask, r); ++ __raw_readl(r); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static inline void ag71xx_int_enable(struct ag71xx *ag, u32 ints) ++{ ++ ag71xx_sb(ag, AG71XX_REG_INT_ENABLE, ints); ++} ++ ++static inline void ag71xx_int_disable(struct ag71xx *ag, u32 ints) ++{ ++ ag71xx_cb(ag, AG71XX_REG_INT_ENABLE, ints); ++} ++ ++static inline void ag71xx_mii_ctrl_wr(struct ag71xx *ag, u32 value) ++{ ++ __raw_writel(value, ag->mii_ctrl); ++ __raw_readl(ag->mii_ctrl); ++} ++ ++static inline u32 ag71xx_mii_ctrl_rr(struct ag71xx *ag) ++{ ++ return __raw_readl(ag->mii_ctrl); ++} ++ ++static void inline ag71xx_mii_ctrl_set_if(struct ag71xx *ag, ++ unsigned int mii_if) ++{ ++ u32 t; ++ ++ t = ag71xx_mii_ctrl_rr(ag); ++ t &= ~(MII_CTRL_IF_MASK); ++ t |= (mii_if & MII_CTRL_IF_MASK); ++ ag71xx_mii_ctrl_wr(ag, t); ++} ++ ++static void inline ag71xx_mii_ctrl_set_speed(struct ag71xx *ag, ++ unsigned int speed) ++{ ++ u32 t; ++ ++ t = ag71xx_mii_ctrl_rr(ag); ++ t &= ~(MII_CTRL_SPEED_MASK << MII_CTRL_SPEED_SHIFT); ++ t |= (speed & MII_CTRL_SPEED_MASK) << MII_CTRL_SPEED_SHIFT; ++ ag71xx_mii_ctrl_wr(ag, t); ++} ++ ++#ifdef CONFIG_AG71XX_AR8216_SUPPORT ++void ag71xx_add_ar8216_header(struct ag71xx *ag, struct sk_buff *skb); ++int ag71xx_remove_ar8216_header(struct ag71xx *ag, struct sk_buff *skb); ++#else ++static inline void ag71xx_add_ar8216_header(struct ag71xx *ag, ++ struct sk_buff *skb) ++{ ++} ++ ++static inline int ag71xx_remove_ar8216_header(struct ag71xx *ag, ++ struct sk_buff *skb) ++{ ++ return 0; ++} ++#endif ++ ++#endif /* _AG71XX_H */ +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_ar8216.c linux-2.6.29.1/drivers/net/ag71xx/ag71xx_ar8216.c +--- linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_ar8216.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/ag71xx_ar8216.c 2009-04-13 14:27:34.803084620 +0200 +@@ -0,0 +1,53 @@ ++/* ++ * Atheros AR71xx built-in ethernet mac driver ++ * Special support for the Atheros ar8216 switch chip ++ * ++ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * Based on Atheros' AG7100 driver ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include "ag71xx.h" ++ ++#define AR8216_PACKET_TYPE_MASK 0xf ++#define AR8216_PACKET_TYPE_NORMAL 0 ++ ++#define AR8216_HEADER_LEN 2 ++ ++void ag71xx_add_ar8216_header(struct ag71xx *ag, struct sk_buff *skb) ++{ ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ ++ if (!pdata->has_ar8216) ++ return; ++ ++ skb_push(skb, AR8216_HEADER_LEN); ++ skb->data[0] = 0x10; ++ skb->data[1] = 0x80; ++} ++ ++int ag71xx_remove_ar8216_header(struct ag71xx *ag, ++ struct sk_buff *skb) ++{ ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ u8 type; ++ ++ if (!pdata->has_ar8216) ++ return 0; ++ ++ type = skb->data[1] & AR8216_PACKET_TYPE_MASK; ++ ++ switch (type) { ++ case AR8216_PACKET_TYPE_NORMAL: ++ skb_pull(skb, AR8216_HEADER_LEN); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_ethtool.c linux-2.6.29.1/drivers/net/ag71xx/ag71xx_ethtool.c +--- linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_ethtool.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/ag71xx_ethtool.c 2009-04-13 14:27:34.803084620 +0200 +@@ -0,0 +1,71 @@ ++/* ++ * Atheros AR71xx built-in ethernet mac driver ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Based on Atheros' AG7100 driver ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include "ag71xx.h" ++ ++static int ag71xx_ethtool_get_settings(struct net_device *dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ struct phy_device *phydev = ag->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_gset(phydev, cmd); ++} ++ ++static int ag71xx_ethtool_set_settings(struct net_device *dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ struct phy_device *phydev = ag->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_sset(phydev, cmd); ++} ++ ++static void ag71xx_ethtool_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ ++ strcpy(info->driver, ag->pdev->dev.driver->name); ++ strcpy(info->version, AG71XX_DRV_VERSION); ++ strcpy(info->bus_info, ag->pdev->dev.bus_id); ++} ++ ++static u32 ag71xx_ethtool_get_msglevel(struct net_device *dev) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ ++ return ag->msg_enable; ++} ++ ++static void ag71xx_ethtool_set_msglevel(struct net_device *dev, u32 msg_level) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ ++ ag->msg_enable = msg_level; ++} ++ ++struct ethtool_ops ag71xx_ethtool_ops = { ++ .set_settings = ag71xx_ethtool_set_settings, ++ .get_settings = ag71xx_ethtool_get_settings, ++ .get_drvinfo = ag71xx_ethtool_get_drvinfo, ++ .get_msglevel = ag71xx_ethtool_get_msglevel, ++ .set_msglevel = ag71xx_ethtool_set_msglevel, ++ .get_link = ethtool_op_get_link, ++}; +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_main.c linux-2.6.29.1/drivers/net/ag71xx/ag71xx_main.c +--- linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_main.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/ag71xx_main.c 2009-04-13 18:00:36.641934324 +0200 +@@ -0,0 +1,990 @@ ++/* ++ * Atheros AR71xx built-in ethernet mac driver ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Based on Atheros' AG7100 driver ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include "ag71xx.h" ++ ++#define AG71XX_DEFAULT_MSG_ENABLE \ ++ ( NETIF_MSG_DRV \ ++ | NETIF_MSG_PROBE \ ++ | NETIF_MSG_LINK \ ++ | NETIF_MSG_TIMER \ ++ | NETIF_MSG_IFDOWN \ ++ | NETIF_MSG_IFUP \ ++ | NETIF_MSG_RX_ERR \ ++ | NETIF_MSG_TX_ERR ) ++ ++static int ag71xx_debug = -1; ++ ++module_param(ag71xx_debug, int, 0); ++MODULE_PARM_DESC(ag71xx_debug, "Debug level (-1=defaults,0=none,...,16=all)"); ++ ++static void ag71xx_dump_dma_regs(struct ag71xx *ag) ++{ ++ DBG("%s: dma_tx_ctrl=%08x, dma_tx_desc=%08x, dma_tx_status=%08x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_TX_CTRL), ++ ag71xx_rr(ag, AG71XX_REG_TX_DESC), ++ ag71xx_rr(ag, AG71XX_REG_TX_STATUS)); ++ ++ DBG("%s: dma_rx_ctrl=%08x, dma_rx_desc=%08x, dma_rx_status=%08x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_RX_CTRL), ++ ag71xx_rr(ag, AG71XX_REG_RX_DESC), ++ ag71xx_rr(ag, AG71XX_REG_RX_STATUS)); ++} ++ ++static void ag71xx_dump_regs(struct ag71xx *ag) ++{ ++ DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_MAC_CFG1), ++ ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), ++ ag71xx_rr(ag, AG71XX_REG_MAC_IPG), ++ ag71xx_rr(ag, AG71XX_REG_MAC_HDX), ++ ag71xx_rr(ag, AG71XX_REG_MAC_MFL)); ++ DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), ++ ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1), ++ ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2)); ++ DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); ++ DBG("%s: fifo_cfg3=%08x, fifo_cfg4=%08x, fifo_cfg5=%08x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); ++} ++ ++static inline void ag71xx_dump_intr(struct ag71xx *ag, char *label, u32 intr) ++{ ++ DBG("%s: %s intr=%08x %s%s%s%s%s%s\n", ++ ag->dev->name, label, intr, ++ (intr & AG71XX_INT_TX_PS) ? "TXPS " : "", ++ (intr & AG71XX_INT_TX_UR) ? "TXUR " : "", ++ (intr & AG71XX_INT_TX_BE) ? "TXBE " : "", ++ (intr & AG71XX_INT_RX_PR) ? "RXPR " : "", ++ (intr & AG71XX_INT_RX_OF) ? "RXOF " : "", ++ (intr & AG71XX_INT_RX_BE) ? "RXBE " : ""); ++} ++ ++static void ag71xx_ring_free(struct ag71xx_ring *ring) ++{ ++ kfree(ring->buf); ++ ++ if (ring->descs) ++ dma_free_coherent(NULL, ring->size * sizeof(*ring->descs), ++ ring->descs, ring->descs_dma); ++} ++ ++static int ag71xx_ring_alloc(struct ag71xx_ring *ring, unsigned int size) ++{ ++ int err; ++ ++ ring->descs = dma_alloc_coherent(NULL, size * sizeof(*ring->descs), ++ &ring->descs_dma, ++ GFP_ATOMIC); ++ if (!ring->descs) { ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ ring->size = size; ++ ++ ring->buf = kzalloc(size * sizeof(*ring->buf), GFP_KERNEL); ++ if (!ring->buf) { ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ return 0; ++ ++ err: ++ return err; ++} ++ ++static void ag71xx_ring_tx_clean(struct ag71xx *ag) ++{ ++ struct ag71xx_ring *ring = &ag->tx_ring; ++ struct net_device *dev = ag->dev; ++ ++ while (ring->curr != ring->dirty) { ++ u32 i = ring->dirty % AG71XX_TX_RING_SIZE; ++ ++ if (!ag71xx_desc_empty(&ring->descs[i])) { ++ ring->descs[i].ctrl = 0; ++ dev->stats.tx_errors++; ++ } ++ ++ if (ring->buf[i].skb) ++ dev_kfree_skb_any(ring->buf[i].skb); ++ ++ ring->buf[i].skb = NULL; ++ ++ ring->dirty++; ++ } ++ ++ /* flush descriptors */ ++ wmb(); ++ ++} ++ ++static void ag71xx_ring_tx_init(struct ag71xx *ag) ++{ ++ struct ag71xx_ring *ring = &ag->tx_ring; ++ int i; ++ ++ for (i = 0; i < AG71XX_TX_RING_SIZE; i++) { ++ ring->descs[i].next = (u32) (ring->descs_dma + ++ sizeof(*ring->descs) * ((i + 1) % AG71XX_TX_RING_SIZE)); ++ ++ ring->descs[i].ctrl = DESC_EMPTY; ++ ring->buf[i].skb = NULL; ++ } ++ ++ /* flush descriptors */ ++ wmb(); ++ ++ ring->curr = 0; ++ ring->dirty = 0; ++} ++ ++static void ag71xx_ring_rx_clean(struct ag71xx *ag) ++{ ++ struct ag71xx_ring *ring = &ag->rx_ring; ++ int i; ++ ++ if (!ring->buf) ++ return; ++ ++ for (i = 0; i < AG71XX_RX_RING_SIZE; i++) ++ if (ring->buf[i].skb) ++ kfree_skb(ring->buf[i].skb); ++ ++} ++ ++static int ag71xx_ring_rx_init(struct ag71xx *ag) ++{ ++ struct ag71xx_ring *ring = &ag->rx_ring; ++ unsigned int i; ++ int ret; ++ ++ ret = 0; ++ for (i = 0; i < AG71XX_RX_RING_SIZE; i++) ++ ring->descs[i].next = (u32) (ring->descs_dma + ++ sizeof(*ring->descs) * ((i + 1) % AG71XX_RX_RING_SIZE)); ++ ++ for (i = 0; i < AG71XX_RX_RING_SIZE; i++) { ++ struct sk_buff *skb; ++ ++ skb = dev_alloc_skb(AG71XX_RX_PKT_SIZE); ++ if (!skb) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ dma_map_single(NULL, skb->data, AG71XX_RX_PKT_SIZE, ++ DMA_FROM_DEVICE); ++ ++ skb->dev = ag->dev; ++ skb_reserve(skb, AG71XX_RX_PKT_RESERVE); ++ ++ ring->buf[i].skb = skb; ++ ring->descs[i].data = virt_to_phys(skb->data); ++ ring->descs[i].ctrl = DESC_EMPTY; ++ } ++ ++ /* flush descriptors */ ++ wmb(); ++ ++ ring->curr = 0; ++ ring->dirty = 0; ++ ++ return ret; ++} ++ ++static int ag71xx_ring_rx_refill(struct ag71xx *ag) ++{ ++ struct ag71xx_ring *ring = &ag->rx_ring; ++ unsigned int count; ++ ++ count = 0; ++ for (; ring->curr - ring->dirty > 0; ring->dirty++) { ++ unsigned int i; ++ ++ i = ring->dirty % AG71XX_RX_RING_SIZE; ++ ++ if (ring->buf[i].skb == NULL) { ++ struct sk_buff *skb; ++ ++ skb = dev_alloc_skb(AG71XX_RX_PKT_SIZE); ++ if (skb == NULL) ++ break; ++ ++ dma_map_single(NULL, skb->data, AG71XX_RX_PKT_SIZE, ++ DMA_FROM_DEVICE); ++ ++ skb_reserve(skb, AG71XX_RX_PKT_RESERVE); ++ skb->dev = ag->dev; ++ ++ ring->buf[i].skb = skb; ++ ring->descs[i].data = virt_to_phys(skb->data); ++ } ++ ++ ring->descs[i].ctrl = DESC_EMPTY; ++ count++; ++ } ++ ++ /* flush descriptors */ ++ wmb(); ++ ++ DBG("%s: %u rx descriptors refilled\n", ag->dev->name, count); ++ ++ return count; ++} ++ ++static int ag71xx_rings_init(struct ag71xx *ag) ++{ ++ int ret; ++ ++ ret = ag71xx_ring_alloc(&ag->tx_ring, AG71XX_TX_RING_SIZE); ++ if (ret) ++ return ret; ++ ++ ag71xx_ring_tx_init(ag); ++ ++ ret = ag71xx_ring_alloc(&ag->rx_ring, AG71XX_RX_RING_SIZE); ++ if (ret) ++ return ret; ++ ++ ret = ag71xx_ring_rx_init(ag); ++ return ret; ++} ++ ++static void ag71xx_rings_cleanup(struct ag71xx *ag) ++{ ++ ag71xx_ring_rx_clean(ag); ++ ag71xx_ring_free(&ag->rx_ring); ++ ++ ag71xx_ring_tx_clean(ag); ++ ag71xx_ring_free(&ag->tx_ring); ++} ++ ++static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac) ++{ ++ u32 t; ++ ++ t = (((u32) mac[0]) << 24) | (((u32) mac[1]) << 16) ++ | (((u32) mac[2]) << 8) | ((u32) mac[3]); ++ ++ ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t); ++ ++ t = (((u32) mac[4]) << 24) | (((u32) mac[5]) << 16); ++ ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t); ++} ++ ++static void ag71xx_dma_reset(struct ag71xx *ag) ++{ ++ int i; ++ ++ ag71xx_dump_dma_regs(ag); ++ ++ /* stop RX and TX */ ++ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); ++ ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); ++ ++ /* clear descriptor addresses */ ++ ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0); ++ ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0); ++ ++ /* clear pending RX/TX interrupts */ ++ for (i = 0; i < 256; i++) { ++ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); ++ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); ++ } ++ ++ /* clear pending errors */ ++ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF); ++ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR); ++ ++ if (ag71xx_rr(ag, AG71XX_REG_RX_STATUS)) ++ printk(KERN_ALERT "%s: unable to clear DMA Rx status\n", ++ ag->dev->name); ++ ++ if (ag71xx_rr(ag, AG71XX_REG_TX_STATUS)) ++ printk(KERN_ALERT "%s: unable to clear DMA Tx status\n", ++ ag->dev->name); ++ ++ ag71xx_dump_dma_regs(ag); ++} ++ ++#define MAC_CFG1_INIT (MAC_CFG1_RXE | MAC_CFG1_TXE | \ ++ MAC_CFG1_SRX | MAC_CFG1_STX) ++ ++#define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) ++ ++#define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ ++ FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ ++ FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ ++ FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ ++ FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ ++ FIFO_CFG4_VT) ++ ++#define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ ++ FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ ++ FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ ++ FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ ++ FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ ++ FIFO_CFG5_17 | FIFO_CFG5_SF) ++ ++static void ag71xx_hw_init(struct ag71xx *ag) ++{ ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ ++ ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR); ++ udelay(20); ++ ++ ar71xx_device_stop(pdata->reset_bit); ++ mdelay(100); ++ ar71xx_device_start(pdata->reset_bit); ++ mdelay(100); ++ ++ /* setup MAC configuration registers */ ++ ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_INIT); ++ ag71xx_sb(ag, AG71XX_REG_MAC_CFG2, ++ MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK); ++ ++ /* setup max frame length */ ++ ag71xx_wr(ag, AG71XX_REG_MAC_MFL, AG71XX_TX_MTU_LEN); ++ ++ /* setup MII interface type */ ++ ag71xx_mii_ctrl_set_if(ag, pdata->mii_if); ++ ++ /* setup FIFO configuration registers */ ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT); ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000); ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff); ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT); ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT); ++ ++ ag71xx_dma_reset(ag); ++} ++ ++static void ag71xx_hw_start(struct ag71xx *ag) ++{ ++ /* start RX engine */ ++ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); ++ ++ /* enable interrupts */ ++ ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT); ++} ++ ++static void ag71xx_hw_stop(struct ag71xx *ag) ++{ ++ /* disable all interrupts */ ++ ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0); ++ ++ ag71xx_dma_reset(ag); ++} ++ ++static int ag71xx_open(struct net_device *dev) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ int ret; ++ ++ ret = ag71xx_rings_init(ag); ++ if (ret) ++ goto err; ++ ++ napi_enable(&ag->napi); ++ ++ netif_carrier_off(dev); ++ ag71xx_phy_start(ag); ++ ++ ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); ++ ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma); ++ ++ ag71xx_hw_set_macaddr(ag, dev->dev_addr); ++ ++ ag71xx_hw_start(ag); ++ ++ netif_start_queue(dev); ++ ++ return 0; ++ ++ err: ++ ag71xx_rings_cleanup(ag); ++ return ret; ++} ++ ++static int ag71xx_stop(struct net_device *dev) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ag->lock, flags); ++ ++ netif_stop_queue(dev); ++ ++ ag71xx_hw_stop(ag); ++ ++ netif_carrier_off(dev); ++ ag71xx_phy_stop(ag); ++ ++ napi_disable(&ag->napi); ++ del_timer_sync(&ag->oom_timer); ++ ++ spin_unlock_irqrestore(&ag->lock, flags); ++ ++ ag71xx_rings_cleanup(ag); ++ ++ return 0; ++} ++ ++static int ag71xx_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ struct ag71xx_ring *ring = &ag->tx_ring; ++ struct ag71xx_desc *desc; ++ unsigned long flags; ++ int i; ++ ++ i = ring->curr % AG71XX_TX_RING_SIZE; ++ desc = &ring->descs[i]; ++ ++ spin_lock_irqsave(&ag->lock, flags); ++ pdata->ddr_flush(); ++ spin_unlock_irqrestore(&ag->lock, flags); ++ ++ if (!ag71xx_desc_empty(desc)) ++ goto err_drop; ++ ++ ag71xx_add_ar8216_header(ag, skb); ++ ++ if (skb->len <= 0) { ++ DBG("%s: packet len is too small\n", ag->dev->name); ++ goto err_drop; ++ } ++ ++ dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); ++ ++ ring->buf[i].skb = skb; ++ ++ /* setup descriptor fields */ ++ desc->data = virt_to_phys(skb->data); ++ desc->ctrl = (skb->len & DESC_PKTLEN_M); ++ ++ /* flush descriptor */ ++ wmb(); ++ ++ ring->curr++; ++ if (ring->curr == (ring->dirty + AG71XX_TX_THRES_STOP)) { ++ DBG("%s: tx queue full\n", ag->dev->name); ++ netif_stop_queue(dev); ++ } ++ ++ DBG("%s: packet injected into TX queue\n", ag->dev->name); ++ ++ /* enable TX engine */ ++ ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); ++ ++ dev->trans_start = jiffies; ++ ++ return 0; ++ ++ err_drop: ++ dev->stats.tx_dropped++; ++ ++ dev_kfree_skb(skb); ++ return 0; ++} ++ ++static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ++{ ++ struct mii_ioctl_data *data = (struct mii_ioctl_data *) &ifr->ifr_data; ++ struct ag71xx *ag = netdev_priv(dev); ++ int ret; ++ ++ switch (cmd) { ++ case SIOCETHTOOL: ++ if (ag->phy_dev == NULL) ++ break; ++ ++ spin_lock_irq(&ag->lock); ++ ret = phy_ethtool_ioctl(ag->phy_dev, (void *) ifr->ifr_data); ++ spin_unlock_irq(&ag->lock); ++ return ret; ++ ++ case SIOCSIFHWADDR: ++ if (copy_from_user ++ (dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr))) ++ return -EFAULT; ++ return 0; ++ ++ case SIOCGIFHWADDR: ++ if (copy_to_user ++ (ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr))) ++ return -EFAULT; ++ return 0; ++ ++ case SIOCGMIIPHY: ++ case SIOCGMIIREG: ++ case SIOCSMIIREG: ++ if (ag->phy_dev == NULL) ++ break; ++ ++ return phy_mii_ioctl(ag->phy_dev, data, cmd); ++ ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static void ag71xx_oom_timer_handler(unsigned long data) ++{ ++ struct net_device *dev = (struct net_device *) data; ++ struct ag71xx *ag = netdev_priv(dev); ++ ++ netif_rx_schedule(&ag->napi); ++} ++ ++static void ag71xx_tx_timeout(struct net_device *dev) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ ++ if (netif_msg_tx_err(ag)) ++ printk(KERN_DEBUG "%s: tx timeout\n", ag->dev->name); ++ ++ schedule_work(&ag->restart_work); ++} ++ ++static void ag71xx_restart_work_func(struct work_struct *work) ++{ ++ struct ag71xx *ag = container_of(work, struct ag71xx, restart_work); ++ ++ ag71xx_stop(ag->dev); ++ ag71xx_open(ag->dev); ++} ++ ++static void ag71xx_tx_packets(struct ag71xx *ag) ++{ ++ struct ag71xx_ring *ring = &ag->tx_ring; ++ unsigned int sent; ++ ++ DBG("%s: processing TX ring\n", ag->dev->name); ++ ++ sent = 0; ++ while (ring->dirty != ring->curr) { ++ unsigned int i = ring->dirty % AG71XX_TX_RING_SIZE; ++ struct ag71xx_desc *desc = &ring->descs[i]; ++ struct sk_buff *skb = ring->buf[i].skb; ++ ++ if (!ag71xx_desc_empty(desc)) ++ break; ++ ++ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); ++ ++ ag->dev->stats.tx_bytes += skb->len; ++ ag->dev->stats.tx_packets++; ++ ++ dev_kfree_skb_any(skb); ++ ring->buf[i].skb = NULL; ++ ++ ring->dirty++; ++ sent++; ++ } ++ ++ DBG("%s: %d packets sent out\n", ag->dev->name, sent); ++ ++ if ((ring->curr - ring->dirty) < AG71XX_TX_THRES_WAKEUP) ++ netif_wake_queue(ag->dev); ++ ++} ++ ++static int ag71xx_rx_packets(struct ag71xx *ag, int limit) ++{ ++ struct net_device *dev = ag->dev; ++ struct ag71xx_ring *ring = &ag->rx_ring; ++ int done = 0; ++ ++ DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n", ++ dev->name, limit, ring->curr, ring->dirty); ++ ++ while (done < limit) { ++ unsigned int i = ring->curr % AG71XX_RX_RING_SIZE; ++ struct ag71xx_desc *desc = &ring->descs[i]; ++ struct sk_buff *skb; ++ int pktlen; ++ ++ if (ag71xx_desc_empty(desc)) ++ break; ++ ++ if ((ring->dirty + AG71XX_RX_RING_SIZE) == ring->curr) { ++ ag71xx_assert(0); ++ break; ++ } ++ ++ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); ++ ++ skb = ring->buf[i].skb; ++ pktlen = ag71xx_desc_pktlen(desc); ++ pktlen -= ETH_FCS_LEN; ++ ++ skb_put(skb, pktlen); ++ ++ skb->dev = dev; ++ skb->ip_summed = CHECKSUM_NONE; ++ ++ dev->last_rx = jiffies; ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += pktlen; ++ ++ if (ag71xx_remove_ar8216_header(ag, skb) != 0) { ++ dev->stats.rx_dropped++; ++ kfree_skb(skb); ++ } else { ++ skb->protocol = eth_type_trans(skb, dev); ++ netif_receive_skb(skb); ++ } ++ ++ ring->buf[i].skb = NULL; ++ done++; ++ ++ ring->curr++; ++ } ++ ++ ag71xx_ring_rx_refill(ag); ++ ++ DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n", ++ dev->name, ring->curr, ring->dirty, done); ++ ++ return done; ++} ++ ++static int ag71xx_poll(struct napi_struct *napi, int limit) ++{ ++ struct ag71xx *ag = container_of(napi, struct ag71xx, napi); ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ struct net_device *dev = ag->dev; ++ struct ag71xx_ring *rx_ring; ++ unsigned long flags; ++ u32 status; ++ int done; ++ ++ pdata->ddr_flush(); ++ ag71xx_tx_packets(ag); ++ ++ DBG("%s: processing RX ring\n", dev->name); ++ done = ag71xx_rx_packets(ag, limit); ++ ++ rx_ring = &ag->rx_ring; ++ if (rx_ring->buf[rx_ring->dirty % AG71XX_RX_RING_SIZE].skb == NULL) ++ goto oom; ++ ++ status = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); ++ if (unlikely(status & RX_STATUS_OF)) { ++ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_OF); ++ dev->stats.rx_fifo_errors++; ++ ++ /* restart RX */ ++ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); ++ } ++ ++ if (done < limit) { ++ if (status & RX_STATUS_PR) ++ goto more; ++ ++ status = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); ++ if (status & TX_STATUS_PS) ++ goto more; ++ ++ DBG("%s: disable polling mode, done=%d, limit=%d\n", ++ dev->name, done, limit); ++ ++ netif_rx_complete(napi); ++ ++ /* enable interrupts */ ++ spin_lock_irqsave(&ag->lock, flags); ++ ag71xx_int_enable(ag, AG71XX_INT_POLL); ++ spin_unlock_irqrestore(&ag->lock, flags); ++ return done; ++ } ++ ++ more: ++ DBG("%s: stay in polling mode, done=%d, limit=%d\n", ++ dev->name, done, limit); ++ return done; ++ ++ oom: ++ if (netif_msg_rx_err(ag)) ++ printk(KERN_DEBUG "%s: out of memory\n", dev->name); ++ ++ mod_timer(&ag->oom_timer, jiffies + AG71XX_OOM_REFILL); ++ netif_rx_complete(napi); ++ return 0; ++} ++ ++static irqreturn_t ag71xx_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *dev = dev_id; ++ struct ag71xx *ag = netdev_priv(dev); ++ u32 status; ++ ++ status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS); ++ ag71xx_dump_intr(ag, "raw", status); ++ ++ if (unlikely(!status)) ++ return IRQ_NONE; ++ ++ if (unlikely(status & AG71XX_INT_ERR)) { ++ if (status & AG71XX_INT_TX_BE) { ++ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE); ++ dev_err(&dev->dev, "TX BUS error\n"); ++ } ++ if (status & AG71XX_INT_RX_BE) { ++ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE); ++ dev_err(&dev->dev, "RX BUS error\n"); ++ } ++ } ++ ++ if (likely(status & AG71XX_INT_POLL)) { ++ ag71xx_int_disable(ag, AG71XX_INT_POLL); ++ DBG("%s: enable polling mode\n", dev->name); ++ netif_rx_schedule(&ag->napi); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void ag71xx_set_multicast_list(struct net_device *dev) ++{ ++ /* TODO */ ++} ++ ++static int __init ag71xx_probe(struct platform_device *pdev) ++{ ++ struct net_device *dev; ++ struct resource *res; ++ struct ag71xx *ag; ++ struct ag71xx_platform_data *pdata; ++ int err; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, "no platform data specified\n"); ++ err = -ENXIO; ++ goto err_out; ++ } ++ ++ dev = alloc_etherdev(sizeof(*ag)); ++ if (!dev) { ++ dev_err(&pdev->dev, "alloc_etherdev failed\n"); ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ SET_NETDEV_DEV(dev, &pdev->dev); ++ ++ ag = netdev_priv(dev); ++ ag->pdev = pdev; ++ ag->dev = dev; ++ ag->mii_bus = ag71xx_mdio_bus->mii_bus; ++ ag->msg_enable = netif_msg_init(ag71xx_debug, ++ AG71XX_DEFAULT_MSG_ENABLE); ++ spin_lock_init(&ag->lock); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base"); ++ if (!res) { ++ dev_err(&pdev->dev, "no mac_base resource found\n"); ++ err = -ENXIO; ++ goto err_out; ++ } ++ ++ ag->mac_base = ioremap_nocache(res->start, res->end - res->start + 1); ++ if (!ag->mac_base) { ++ dev_err(&pdev->dev, "unable to ioremap mac_base\n"); ++ err = -ENOMEM; ++ goto err_free_dev; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base2"); ++ if (!res) { ++ dev_err(&pdev->dev, "no mac_base2 resource found\n"); ++ err = -ENXIO; ++ goto err_unmap_base1; ++ } ++ ++ ag->mac_base2 = ioremap_nocache(res->start, res->end - res->start + 1); ++ if (!ag->mac_base) { ++ dev_err(&pdev->dev, "unable to ioremap mac_base2\n"); ++ err = -ENOMEM; ++ goto err_unmap_base1; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mii_ctrl"); ++ if (!res) { ++ dev_err(&pdev->dev, "no mii_ctrl resource found\n"); ++ err = -ENXIO; ++ goto err_unmap_base2; ++ } ++ ++ ag->mii_ctrl = ioremap_nocache(res->start, res->end - res->start + 1); ++ if (!ag->mii_ctrl) { ++ dev_err(&pdev->dev, "unable to ioremap mii_ctrl\n"); ++ err = -ENOMEM; ++ goto err_unmap_base2; ++ } ++ ++ dev->irq = platform_get_irq(pdev, 0); ++ err = request_irq(dev->irq, ag71xx_interrupt, ++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM, ++ dev->name, dev); ++ if (err) { ++ dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq); ++ goto err_unmap_mii_ctrl; ++ } ++ ++ dev->base_addr = (unsigned long)ag->mac_base; ++ dev->open = ag71xx_open; ++ dev->stop = ag71xx_stop; ++ dev->hard_start_xmit = ag71xx_hard_start_xmit; ++ dev->set_multicast_list = ag71xx_set_multicast_list; ++ dev->do_ioctl = ag71xx_do_ioctl; ++ dev->ethtool_ops = &ag71xx_ethtool_ops; ++ ++ dev->tx_timeout = ag71xx_tx_timeout; ++ INIT_WORK(&ag->restart_work, ag71xx_restart_work_func); ++ ++ init_timer(&ag->oom_timer); ++ ag->oom_timer.data = (unsigned long) dev; ++ ag->oom_timer.function = ag71xx_oom_timer_handler; ++ ++ memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN); ++ ++ netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); ++ ++ err = register_netdev(dev); ++ if (err) { ++ dev_err(&pdev->dev, "unable to register net device\n"); ++ goto err_free_irq; ++ } ++ ++ printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n", ++ dev->name, dev->base_addr, dev->irq); ++ ++ ag71xx_dump_regs(ag); ++ ++ ag71xx_hw_init(ag); ++ ++ ag71xx_dump_regs(ag); ++ ++ /* Reset the mdio bus explicitly */ ++ if (ag->mii_bus) { ++ mutex_lock(&ag->mii_bus->mdio_lock); ++ ag->mii_bus->reset(ag->mii_bus); ++ mutex_unlock(&ag->mii_bus->mdio_lock); ++ } ++ ++ err = ag71xx_phy_connect(ag); ++ if (err) ++ goto err_unregister_netdev; ++ ++ platform_set_drvdata(pdev, dev); ++ ++ return 0; ++ ++ err_unregister_netdev: ++ unregister_netdev(dev); ++ err_free_irq: ++ free_irq(dev->irq, dev); ++ err_unmap_mii_ctrl: ++ iounmap(ag->mii_ctrl); ++ err_unmap_base2: ++ iounmap(ag->mac_base2); ++ err_unmap_base1: ++ iounmap(ag->mac_base); ++ err_free_dev: ++ kfree(dev); ++ err_out: ++ platform_set_drvdata(pdev, NULL); ++ return err; ++} ++ ++static int __exit ag71xx_remove(struct platform_device *pdev) ++{ ++ struct net_device *dev = platform_get_drvdata(pdev); ++ ++ if (dev) { ++ struct ag71xx *ag = netdev_priv(dev); ++ ++ ag71xx_phy_disconnect(ag); ++ unregister_netdev(dev); ++ free_irq(dev->irq, dev); ++ iounmap(ag->mii_ctrl); ++ iounmap(ag->mac_base2); ++ iounmap(ag->mac_base); ++ kfree(dev); ++ platform_set_drvdata(pdev, NULL); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver ag71xx_driver = { ++ .probe = ag71xx_probe, ++ .remove = __exit_p(ag71xx_remove), ++ .driver = { ++ .name = AG71XX_DRV_NAME, ++ } ++}; ++ ++static int __init ag71xx_module_init(void) ++{ ++ int ret; ++ ++ ret = ag71xx_mdio_driver_init(); ++ if (ret) ++ goto err_out; ++ ++ ret = platform_driver_register(&ag71xx_driver); ++ if (ret) ++ goto err_mdio_exit; ++ ++ return 0; ++ ++ err_mdio_exit: ++ ag71xx_mdio_driver_exit(); ++ err_out: ++ return ret; ++} ++ ++static void __exit ag71xx_module_exit(void) ++{ ++ platform_driver_unregister(&ag71xx_driver); ++ ag71xx_mdio_driver_exit(); ++} ++ ++module_init(ag71xx_module_init); ++module_exit(ag71xx_module_exit); ++ ++MODULE_VERSION(AG71XX_DRV_VERSION); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" AG71XX_DRV_NAME); +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_mdio.c linux-2.6.29.1/drivers/net/ag71xx/ag71xx_mdio.c +--- linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_mdio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/ag71xx_mdio.c 2009-04-13 14:27:34.827084882 +0200 +@@ -0,0 +1,233 @@ ++/* ++ * Atheros AR71xx built-in ethernet mac driver ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Based on Atheros' AG7100 driver ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include "ag71xx.h" ++ ++#define AG71XX_MDIO_RETRY 1000 ++#define AG71XX_MDIO_DELAY 5 ++ ++struct ag71xx_mdio *ag71xx_mdio_bus; ++ ++static inline void ag71xx_mdio_wr(struct ag71xx_mdio *am, unsigned reg, ++ u32 value) ++{ ++ __raw_writel(value, am->mdio_base + reg - AG71XX_REG_MII_CFG); ++} ++ ++static inline u32 ag71xx_mdio_rr(struct ag71xx_mdio *am, unsigned reg) ++{ ++ return __raw_readl(am->mdio_base + reg - AG71XX_REG_MII_CFG); ++} ++ ++static void ag71xx_mdio_dump_regs(struct ag71xx_mdio *am) ++{ ++ DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n", ++ am->mii_bus->name, ++ ag71xx_mdio_rr(am, AG71XX_REG_MII_CFG), ++ ag71xx_mdio_rr(am, AG71XX_REG_MII_CMD), ++ ag71xx_mdio_rr(am, AG71XX_REG_MII_ADDR)); ++ DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n", ++ am->mii_bus->name, ++ ag71xx_mdio_rr(am, AG71XX_REG_MII_CTRL), ++ ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS), ++ ag71xx_mdio_rr(am, AG71XX_REG_MII_IND)); ++} ++ ++static int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg) ++{ ++ int ret; ++ int i; ++ ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE); ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR, ++ ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_READ); ++ ++ i = AG71XX_MDIO_RETRY; ++ while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) { ++ if (i-- == 0) { ++ printk(KERN_ERR "%s: mii_read timed out\n", ++ am->mii_bus->name); ++ ret = 0xffff; ++ goto out; ++ } ++ udelay(AG71XX_MDIO_DELAY); ++ } ++ ++ ret = ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS) & 0xffff; ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE); ++ ++ DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret); ++ ++ out: ++ return ret; ++} ++ ++static void ag71xx_mdio_mii_write(struct ag71xx_mdio *am, ++ int addr, int reg, u16 val) ++{ ++ int i; ++ ++ DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val); ++ ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR, ++ ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_CTRL, val); ++ ++ i = AG71XX_MDIO_RETRY; ++ while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) { ++ if (i-- == 0) { ++ printk(KERN_ERR "%s: mii_write timed out\n", ++ am->mii_bus->name); ++ break; ++ } ++ udelay(AG71XX_MDIO_DELAY); ++ } ++} ++ ++static int ag71xx_mdio_reset(struct mii_bus *bus) ++{ ++ struct ag71xx_mdio *am = bus->priv; ++ ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, MII_CFG_RESET); ++ udelay(100); ++ ++ ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_28); ++ udelay(100); ++ ++ return 0; ++} ++ ++static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg) ++{ ++ struct ag71xx_mdio *am = bus->priv; ++ ++ return ag71xx_mdio_mii_read(am, addr, reg); ++} ++ ++static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) ++{ ++ struct ag71xx_mdio *am = bus->priv; ++ ++ ag71xx_mdio_mii_write(am, addr, reg, val); ++ return 0; ++} ++ ++static int __init ag71xx_mdio_probe(struct platform_device *pdev) ++{ ++ struct ag71xx_mdio_platform_data *pdata; ++ struct ag71xx_mdio *am; ++ struct resource *res; ++ int i; ++ int err; ++ ++ if (ag71xx_mdio_bus) ++ return -EBUSY; ++ ++ am = kzalloc(sizeof(*am), GFP_KERNEL); ++ if (!am) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no iomem resource found\n"); ++ err = -ENXIO; ++ goto err_out; ++ } ++ ++ am->mdio_base = ioremap_nocache(res->start, res->end - res->start + 1); ++ if (!am->mdio_base) { ++ dev_err(&pdev->dev, "unable to ioremap registers\n"); ++ err = -ENOMEM; ++ goto err_free_mdio; ++ } ++ ++ am->mii_bus = mdiobus_alloc(); ++ if (am->mii_bus == NULL) { ++ err = -ENOMEM; ++ goto err_iounmap; ++ } ++ ++ am->mii_bus->name = "ag71xx_mdio"; ++ am->mii_bus->read = ag71xx_mdio_read; ++ am->mii_bus->write = ag71xx_mdio_write; ++ am->mii_bus->reset = ag71xx_mdio_reset; ++ am->mii_bus->irq = am->mii_irq; ++ am->mii_bus->priv = am; ++ am->mii_bus->parent = &pdev->dev; ++ snprintf(am->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0); ++ ++ pdata = pdev->dev.platform_data; ++ if (pdata) ++ am->mii_bus->phy_mask = pdata->phy_mask; ++ ++ for (i = 0; i < PHY_MAX_ADDR; i++) ++ am->mii_irq[i] = PHY_POLL; ++ ++ ag71xx_mdio_wr(am, AG71XX_REG_MAC_CFG1, 0); ++ ++ err = mdiobus_register(am->mii_bus); ++ if (err) ++ goto err_free_bus; ++ ++ ag71xx_mdio_dump_regs(am); ++ ++ platform_set_drvdata(pdev, am); ++ ag71xx_mdio_bus = am; ++ return 0; ++ ++ err_free_bus: ++ mdiobus_free(am->mii_bus); ++ err_iounmap: ++ iounmap(am->mdio_base); ++ err_free_mdio: ++ kfree(am); ++ err_out: ++ return err; ++} ++ ++static int __exit ag71xx_mdio_remove(struct platform_device *pdev) ++{ ++ struct ag71xx_mdio *am = platform_get_drvdata(pdev); ++ ++ if (am) { ++ ag71xx_mdio_bus = NULL; ++ mdiobus_unregister(am->mii_bus); ++ mdiobus_free(am->mii_bus); ++ iounmap(am->mdio_base); ++ kfree(am); ++ platform_set_drvdata(pdev, NULL); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver ag71xx_mdio_driver = { ++ .probe = ag71xx_mdio_probe, ++ .remove = __exit_p(ag71xx_mdio_remove), ++ .driver = { ++ .name = "ag71xx-mdio", ++ } ++}; ++ ++int ag71xx_mdio_driver_init(void) ++{ ++ return platform_driver_register(&ag71xx_mdio_driver); ++} ++ ++void ag71xx_mdio_driver_exit(void) ++{ ++ platform_driver_unregister(&ag71xx_mdio_driver); ++} +diff -Nur linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_phy.c linux-2.6.29.1/drivers/net/ag71xx/ag71xx_phy.c +--- linux-2.6.29.1.orig/drivers/net/ag71xx/ag71xx_phy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/ag71xx/ag71xx_phy.c 2009-04-13 14:27:34.827084882 +0200 +@@ -0,0 +1,287 @@ ++/* ++ * Atheros AR71xx built-in ethernet mac driver ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Based on Atheros' AG7100 driver ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include "ag71xx.h" ++ ++static unsigned char *ag71xx_speed_str(struct ag71xx *ag) ++{ ++ switch (ag->speed) { ++ case SPEED_1000: ++ return "1000"; ++ case SPEED_100: ++ return "100"; ++ case SPEED_10: ++ return "10"; ++ } ++ ++ return "?"; ++} ++ ++#define AR71XX_PLL_VAL_1000 0x00110000 ++#define AR71XX_PLL_VAL_100 0x00001099 ++#define AR71XX_PLL_VAL_10 0x00991099 ++ ++#define AR91XX_PLL_VAL_1000 0x1a000000 ++#define AR91XX_PLL_VAL_100 0x13000a44 ++#define AR91XX_PLL_VAL_10 0x00441099 ++ ++static void ag71xx_phy_link_update(struct ag71xx *ag) ++{ ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ u32 cfg2; ++ u32 ifctl; ++ u32 pll; ++ u32 fifo5; ++ u32 mii_speed; ++ ++ if (!ag->link) { ++ netif_carrier_off(ag->dev); ++ if (netif_msg_link(ag)) ++ printk(KERN_INFO "%s: link down\n", ag->dev->name); ++ return; ++ } ++ ++ cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2); ++ cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX); ++ cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0; ++ ++ ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL); ++ ifctl &= ~(MAC_IFCTL_SPEED); ++ ++ fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5); ++ fifo5 &= ~FIFO_CFG5_BM; ++ ++ switch (ag->speed) { ++ case SPEED_1000: ++ mii_speed = MII_CTRL_SPEED_1000; ++ cfg2 |= MAC_CFG2_IF_1000; ++ pll = pdata->is_ar91xx ? AR91XX_PLL_VAL_1000 ++ : AR71XX_PLL_VAL_1000; ++ fifo5 |= FIFO_CFG5_BM; ++ break; ++ case SPEED_100: ++ mii_speed = MII_CTRL_SPEED_100; ++ cfg2 |= MAC_CFG2_IF_10_100; ++ ifctl |= MAC_IFCTL_SPEED; ++ pll = pdata->is_ar91xx ? AR91XX_PLL_VAL_100 ++ : AR71XX_PLL_VAL_100; ++ break; ++ case SPEED_10: ++ mii_speed = MII_CTRL_SPEED_10; ++ cfg2 |= MAC_CFG2_IF_10_100; ++ pll = pdata->is_ar91xx ? AR91XX_PLL_VAL_10 ++ : AR71XX_PLL_VAL_10; ++ break; ++ default: ++ BUG(); ++ return; ++ } ++ ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, ++ pdata->is_ar91xx ? 0x780fff : 0x008001ff); ++ pdata->set_pll(pll); ++ ag71xx_mii_ctrl_set_speed(ag, mii_speed); ++ ++ ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2); ++ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5); ++ ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl); ++ ++ netif_carrier_on(ag->dev); ++ if (netif_msg_link(ag)) ++ printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n", ++ ag->dev->name, ++ ag71xx_speed_str(ag), ++ (DUPLEX_FULL == ag->duplex) ? "Full" : "Half"); ++ ++ DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); ++ ++ DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), ++ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); ++ ++ DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x, mii_ctrl=%#x\n", ++ ag->dev->name, ++ ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), ++ ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), ++ ag71xx_mii_ctrl_rr(ag)); ++} ++ ++static void ag71xx_phy_link_adjust(struct net_device *dev) ++{ ++ struct ag71xx *ag = netdev_priv(dev); ++ struct phy_device *phydev = ag->phy_dev; ++ unsigned long flags; ++ int status_change = 0; ++ ++ spin_lock_irqsave(&ag->lock, flags); ++ ++ if (phydev->link) { ++ if (ag->duplex != phydev->duplex ++ || ag->speed != phydev->speed) { ++ status_change = 1; ++ } ++ } ++ ++ if (phydev->link != ag->link) ++ status_change = 1; ++ ++ ag->link = phydev->link; ++ ag->duplex = phydev->duplex; ++ ag->speed = phydev->speed; ++ ++ if (status_change) ++ ag71xx_phy_link_update(ag); ++ ++ spin_unlock_irqrestore(&ag->lock, flags); ++} ++ ++void ag71xx_phy_start(struct ag71xx *ag) ++{ ++ if (ag->phy_dev) { ++ phy_start(ag->phy_dev); ++ } else { ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ ++ ag->duplex = pdata->duplex; ++ ag->speed = pdata->speed; ++ ag->link = 1; ++ ag71xx_phy_link_update(ag); ++ } ++} ++ ++void ag71xx_phy_stop(struct ag71xx *ag) ++{ ++ if (ag->phy_dev) { ++ phy_stop(ag->phy_dev); ++ } else { ++ ag->duplex = -1; ++ ag->link = 0; ++ ag->speed = 0; ++ ag71xx_phy_link_update(ag); ++ } ++} ++ ++static int ag71xx_phy_connect_fixed(struct ag71xx *ag) ++{ ++ struct net_device *dev = ag->dev; ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ int ret = 0; ++ ++ /* use fixed settings */ ++ switch (pdata->speed) { ++ case SPEED_10: ++ case SPEED_100: ++ case SPEED_1000: ++ break; ++ default: ++ printk(KERN_ERR "%s: invalid speed specified\n", ++ dev->name); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int ag71xx_phy_connect_multi(struct ag71xx *ag) ++{ ++ struct net_device *dev = ag->dev; ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ struct phy_device *phydev = NULL; ++ int phy_count = 0; ++ int phy_addr; ++ int ret = 0; ++ ++ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { ++ if (!(pdata->phy_mask & (1 << phy_addr))) ++ continue; ++ ++ if (ag->mii_bus->phy_map[phy_addr] == NULL) ++ continue; ++ ++ DBG("%s: PHY found at %s, uid=%08x\n", ++ dev->name, ++ ag->mii_bus->phy_map[phy_addr]->dev.bus_id, ++ ag->mii_bus->phy_map[phy_addr]->phy_id); ++ ++ if (phydev == NULL) ++ phydev = ag->mii_bus->phy_map[phy_addr]; ++ ++ phy_count++; ++ } ++ ++ switch (phy_count) { ++ case 0: ++ printk(KERN_ERR "%s: no PHY found with phy_mask=%08x\n", ++ dev->name, pdata->phy_mask); ++ ret = -ENODEV; ++ break; ++ case 1: ++ ag->phy_dev = phy_connect(dev, phydev->dev.bus_id, ++ &ag71xx_phy_link_adjust, 0, pdata->phy_if_mode); ++ ++ if (IS_ERR(ag->phy_dev)) { ++ printk(KERN_ERR "%s: could not connect to PHY at %s\n", ++ dev->name, phydev->dev.bus_id); ++ return PTR_ERR(ag->phy_dev); ++ } ++ ++ /* mask with MAC supported features */ ++ if (pdata->has_gbit) ++ phydev->supported &= PHY_GBIT_FEATURES; ++ else ++ phydev->supported &= PHY_BASIC_FEATURES; ++ ++ phydev->advertising = phydev->supported; ++ ++ printk(KERN_DEBUG "%s: connected to PHY at %s " ++ "[uid=%08x, driver=%s]\n", ++ dev->name, phydev->dev.bus_id, ++ phydev->phy_id, phydev->drv->name); ++ ++ ag->link = 0; ++ ag->speed = 0; ++ ag->duplex = -1; ++ break; ++ ++ default: ++ printk(KERN_DEBUG "%s: connected to %d PHYs\n", ++ dev->name, phy_count); ++ ret = ag71xx_phy_connect_fixed(ag); ++ break; ++ } ++ ++ return ret; ++} ++ ++int ag71xx_phy_connect(struct ag71xx *ag) ++{ ++ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); ++ ++ if (pdata->phy_mask) ++ return ag71xx_phy_connect_multi(ag); ++ ++ return ag71xx_phy_connect_fixed(ag); ++} ++ ++void ag71xx_phy_disconnect(struct ag71xx *ag) ++{ ++ if (ag->phy_dev) ++ phy_disconnect(ag->phy_dev); ++} +diff -Nur linux-2.6.29.1.orig/drivers/net/phy/micrel.c linux-2.6.29.1/drivers/net/phy/micrel.c +--- linux-2.6.29.1.orig/drivers/net/phy/micrel.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/phy/micrel.c 2009-04-13 14:27:34.831085671 +0200 +@@ -0,0 +1,82 @@ ++/* ++ * Driver for Micrel/Kendin PHYs ++ * ++ * Copyright (c) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/skbuff.h> ++#include <linux/phy.h> ++ ++#define KSZ_REG_INT_CTRL 0x1b ++ ++#define KSZ_INT_LU_EN (1 << 8) /* enable Link Up interrupt */ ++#define KSZ_INT_RF_EN (1 << 9) /* enable Remote Fault interrupt */ ++#define KSZ_INT_LD_EN (1 << 10) /* enable Link Down interrupt */ ++ ++#define KSZ_INT_INIT (KSZ_INT_LU_EN | KSZ_INT_LD_EN) ++ ++static int ksz8041_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, KSZ_REG_INT_CTRL); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int ksz8041_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) ++ err = phy_write(phydev, KSZ_REG_INT_CTRL, ++ KSZ_INT_INIT); ++ else ++ err = phy_write(phydev, KSZ_REG_INT_CTRL, 0); ++ ++ return err; ++} ++ ++static struct phy_driver ksz8041_phy_driver = { ++ .phy_id = 0x00221512, ++ .name = "Micrel KSZ8041", ++ .phy_id_mask = 0x001fffff, ++ .features = PHY_BASIC_FEATURES, ++ .flags = PHY_HAS_INTERRUPT, ++ .config_aneg = genphy_config_aneg, ++ .read_status = genphy_read_status, ++ .ack_interrupt = ksz8041_ack_interrupt, ++ .config_intr = ksz8041_config_intr, ++ .driver = { ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init micrel_phy_init(void) ++{ ++ return phy_driver_register(&ksz8041_phy_driver); ++} ++ ++static void __exit micrel_phy_exit(void) ++{ ++ phy_driver_unregister(&ksz8041_phy_driver); ++} ++ ++#ifdef MODULE ++module_init(micrel_phy_init); ++module_exit(micrel_phy_exit); ++#else ++subsys_initcall(micrel_phy_init); ++#endif ++ ++MODULE_DESCRIPTION("Micrel/Kendin PHY driver"); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); ++MODULE_LICENSE("GPL v2"); +diff -Nur linux-2.6.29.1.orig/drivers/net/phy/phy.c linux-2.6.29.1/drivers/net/phy/phy.c +--- linux-2.6.29.1.orig/drivers/net/phy/phy.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/net/phy/phy.c 2009-04-13 17:29:36.946673592 +0200 +@@ -299,6 +299,50 @@ + } + EXPORT_SYMBOL(phy_ethtool_gset); + ++int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr) ++{ ++ u32 cmd; ++ int tmp; ++ struct ethtool_cmd ecmd = { ETHTOOL_GSET }; ++ struct ethtool_value edata = { ETHTOOL_GLINK }; ++ ++ if (get_user(cmd, (u32 *) useraddr)) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case ETHTOOL_GSET: ++ phy_ethtool_gset(phydev, &ecmd); ++ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) ++ return -EFAULT; ++ return 0; ++ ++ case ETHTOOL_SSET: ++ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) ++ return -EFAULT; ++ return phy_ethtool_sset(phydev, &ecmd); ++ ++ case ETHTOOL_NWAY_RST: ++ /* if autoneg is off, it's an error */ ++ tmp = phy_read(phydev, MII_BMCR); ++ if (tmp & BMCR_ANENABLE) { ++ tmp |= (BMCR_ANRESTART); ++ phy_write(phydev, MII_BMCR, tmp); ++ return 0; ++ } ++ return -EINVAL; ++ ++ case ETHTOOL_GLINK: ++ edata.data = (phy_read(phydev, ++ MII_BMSR) & BMSR_LSTATUS) ? 1 : 0; ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ return -EOPNOTSUPP; ++} ++EXPORT_SYMBOL(phy_ethtool_ioctl); ++ + /** + * phy_mii_ioctl - generic PHY MII ioctl interface + * @phydev: the phy_device struct +@@ -355,8 +399,8 @@ + } + + phy_write(phydev, mii_data->reg_num, val); +- +- if (mii_data->reg_num == MII_BMCR ++ ++ if (mii_data->reg_num == MII_BMCR + && val & BMCR_RESET + && phydev->drv->config_init) { + phy_scan_fixups(phydev); +@@ -476,7 +520,7 @@ + int idx; + + idx = phy_find_setting(phydev->speed, phydev->duplex); +- ++ + idx++; + + idx = phy_find_valid(idx, phydev->supported); +diff -Nur linux-2.6.29.1.orig/drivers/spi/Kconfig linux-2.6.29.1/drivers/spi/Kconfig +--- linux-2.6.29.1.orig/drivers/spi/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/spi/Kconfig 2009-04-13 14:27:34.831085671 +0200 +@@ -53,6 +53,13 @@ + + comment "SPI Master Controller Drivers" + ++config SPI_AR71XX ++ tristate "Atheros AR71xx SPI Controller" ++ depends on SPI_MASTER && ATHEROS_AR71XX ++ select SPI_BITBANG ++ help ++ This is the SPI contoller driver for Atheros AR71xx. ++ + config SPI_ATMEL + tristate "Atmel SPI Controller" + depends on (ARCH_AT91 || AVR32) +diff -Nur linux-2.6.29.1.orig/drivers/spi/Makefile linux-2.6.29.1/drivers/spi/Makefile +--- linux-2.6.29.1.orig/drivers/spi/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/spi/Makefile 2009-04-13 14:27:34.835086738 +0200 +@@ -11,6 +11,7 @@ + obj-$(CONFIG_SPI_MASTER) += spi.o + + # SPI master controller drivers (bus) ++obj-$(CONFIG_SPI_AR71XX) += ar71xx_spi.o + obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o + obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o + obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o +diff -Nur linux-2.6.29.1.orig/drivers/spi/ar71xx_spi.c linux-2.6.29.1/drivers/spi/ar71xx_spi.c +--- linux-2.6.29.1.orig/drivers/spi/ar71xx_spi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/spi/ar71xx_spi.c 2009-04-13 14:27:34.855087329 +0200 +@@ -0,0 +1,240 @@ ++/* ++ * Atheros AR71xx SPI Controller driver ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/spi_bitbang.h> ++#include <linux/bitops.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++#include <asm/mach-ar71xx/platform.h> ++ ++#define DRV_DESC "Atheros AR71xx SPI Controller driver" ++#define DRV_VERSION "0.2.2" ++#define DRV_NAME "ar71xx-spi" ++ ++#undef PER_BIT_READ ++ ++struct ar71xx_spi { ++ struct spi_bitbang bitbang; ++ u32 ioc_base; ++ u32 reg_ctrl; ++ ++ void __iomem *base; ++ ++ struct platform_device *pdev; ++ u32 (*get_ioc_base)(u8 chip_select, int cs_high, ++ int is_on); ++}; ++ ++static inline u32 ar71xx_spi_rr(struct ar71xx_spi *sp, unsigned reg) ++{ ++ return __raw_readl(sp->base + reg); ++} ++ ++static inline void ar71xx_spi_wr(struct ar71xx_spi *sp, unsigned reg, u32 val) ++{ ++ __raw_writel(val, sp->base + reg); ++} ++ ++static inline struct ar71xx_spi *spidev_to_sp(struct spi_device *spi) ++{ ++ return spi_master_get_devdata(spi->master); ++} ++ ++static u32 ar71xx_spi_get_ioc_base(u8 chip_select, int cs_high, int is_on) ++{ ++ u32 ret; ++ ++ if (is_on == AR71XX_SPI_CS_INACTIVE) ++ ret = SPI_IOC_CS_ALL; ++ else ++ ret = SPI_IOC_CS_ALL & ~SPI_IOC_CS(chip_select); ++ ++ return ret; ++} ++ ++static void ar71xx_spi_chipselect(struct spi_device *spi, int value) ++{ ++ struct ar71xx_spi *sp = spidev_to_sp(spi); ++ void __iomem *base = sp->base; ++ u32 ioc_base; ++ ++ switch (value) { ++ case BITBANG_CS_INACTIVE: ++ ioc_base = sp->get_ioc_base(spi->chip_select, ++ (spi->mode & SPI_CS_HIGH) != 0, ++ AR71XX_SPI_CS_INACTIVE); ++ ++ __raw_writel(ioc_base, base + SPI_REG_IOC); ++ __raw_writel(sp->reg_ctrl, base + SPI_REG_CTRL); ++ __raw_writel(0, base + SPI_REG_FS); ++ break; ++ ++ case BITBANG_CS_ACTIVE: ++ ioc_base = sp->get_ioc_base(spi->chip_select, ++ (spi->mode & SPI_CS_HIGH) != 0, ++ AR71XX_SPI_CS_ACTIVE); ++ ++ __raw_writel(SPI_FS_GPIO, base + SPI_REG_FS); ++ /* TODO: setup speed */ ++ __raw_writel(0x43, base + SPI_REG_CTRL); ++ __raw_writel(ioc_base, base + SPI_REG_IOC); ++ sp->ioc_base = ioc_base; ++ break; ++ } ++} ++ ++static u32 ar71xx_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, ++ u32 word, u8 bits) ++{ ++ struct ar71xx_spi *sp = spidev_to_sp(spi); ++ void __iomem *base = sp->base; ++ u32 ioc = sp->ioc_base; ++ u32 ret; ++ ++ /* clock starts at inactive polarity */ ++ for (word <<= (32 - bits); likely(bits); bits--) { ++ u32 out; ++ ++ if (word & (1 << 31)) ++ out = ioc | SPI_IOC_DO; ++ else ++ out = ioc & ~SPI_IOC_DO; ++ ++ /* setup MSB (to slave) on trailing edge */ ++ __raw_writel(out, base + SPI_REG_IOC); ++ ++ __raw_writel(out | SPI_IOC_CLK, base + SPI_REG_IOC); ++ ++ word <<= 1; ++ ++#ifdef PER_BIT_READ ++ /* sample MSB (from slave) on leading edge */ ++ ret = __raw_readl(base + SPI_REG_RDS); ++ __raw_writel(out, base + SPI_REG_IOC); ++#endif ++ ++ } ++ ++#ifndef PER_BIT_READ ++ ret = __raw_readl(base + SPI_REG_RDS); ++#endif ++ return ret; ++} ++ ++static int ar71xx_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct ar71xx_spi *sp; ++ struct ar71xx_spi_platform_data *pdata; ++ struct resource *r; ++ int ret; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*sp)); ++ if (master == NULL) { ++ dev_err(&pdev->dev, "failed to allocate spi master\n"); ++ return -ENOMEM; ++ } ++ ++ sp = spi_master_get_devdata(master); ++ platform_set_drvdata(pdev, sp); ++ ++ pdata = pdev->dev.platform_data; ++ ++ sp->bitbang.master = spi_master_get(master); ++ sp->bitbang.chipselect = ar71xx_spi_chipselect; ++ sp->bitbang.txrx_word[SPI_MODE_0] = ar71xx_spi_txrx_mode0; ++ ++ sp->get_ioc_base = ar71xx_spi_get_ioc_base; ++ if (pdata) { ++ sp->bitbang.master->bus_num = pdata->bus_num; ++ sp->bitbang.master->num_chipselect = pdata->num_chipselect; ++ if (pdata->get_ioc_base) ++ sp->get_ioc_base = pdata->get_ioc_base; ++ } else { ++ sp->bitbang.master->bus_num = 0; ++ sp->bitbang.master->num_chipselect = 3; ++ } ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (r == NULL) { ++ ret = -ENOENT; ++ goto err1; ++ } ++ ++ sp->base = ioremap_nocache(r->start, r->end - r->start + 1); ++ if (!sp->base) { ++ ret = -ENXIO; ++ goto err1; ++ } ++ ++ sp->reg_ctrl = ar71xx_spi_rr(sp, SPI_REG_IOC); ++ ++ ret = spi_bitbang_start(&sp->bitbang); ++ if (!ret) ++ return 0; ++ ++ iounmap(sp->base); ++err1: ++ platform_set_drvdata(pdev, NULL); ++ spi_master_put(sp->bitbang.master); ++ ++ return ret; ++} ++ ++static int ar71xx_spi_remove(struct platform_device *pdev) ++{ ++ struct ar71xx_spi *sp = platform_get_drvdata(pdev); ++ ++ spi_bitbang_stop(&sp->bitbang); ++ iounmap(sp->base); ++ platform_set_drvdata(pdev, NULL); ++ spi_master_put(sp->bitbang.master); ++ ++ return 0; ++} ++ ++static struct platform_driver ar71xx_spi_drv = { ++ .probe = ar71xx_spi_probe, ++ .remove = ar71xx_spi_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ar71xx_spi_init(void) ++{ ++ printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n"); ++ return platform_driver_register(&ar71xx_spi_drv); ++} ++module_init(ar71xx_spi_init); ++ ++static void __exit ar71xx_spi_exit(void) ++{ ++ platform_driver_unregister(&ar71xx_spi_drv); ++} ++module_exit(ar71xx_spi_exit); ++ ++MODULE_ALIAS("platform:" DRV_NAME); ++MODULE_DESCRIPTION(DRV_DESC); ++MODULE_VERSION(DRV_VERSION); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); ++MODULE_LICENSE("GPL v2"); +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/Kconfig linux-2.6.29.1/drivers/usb/host/Kconfig +--- linux-2.6.29.1.orig/drivers/usb/host/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/usb/host/Kconfig 2009-04-13 14:27:34.855087329 +0200 +@@ -81,6 +81,12 @@ + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX) + default y + ++config USB_EHCI_AR71XX ++ bool "USB EHCI support for AR71xx" ++ depends on USB_EHCI_HCD && ATHEROS_AR71XX ++ help ++ Support for Atheros AR71xx built-in EHCI controller ++ + config USB_EHCI_FSL + bool "Support for Freescale on-chip EHCI USB controller" + depends on USB_EHCI_HCD && FSL_SOC +@@ -154,6 +160,12 @@ + To compile this driver as a module, choose M here: the + module will be called ohci-hcd. + ++config USB_OHCI_AR71XX ++ bool "USB OHCI support for Atheros AR71xx" ++ depends on USB_OHCI_HCD && ATHEROS_AR71XX ++ help ++ Support for Atheros AR71xx built-in OHCI controller ++ + config USB_OHCI_HCD_PPC_SOC + bool "OHCI support for on-chip PPC USB controller" + depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx) +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/ehci-ar71xx.c linux-2.6.29.1/drivers/usb/host/ehci-ar71xx.c +--- linux-2.6.29.1.orig/drivers/usb/host/ehci-ar71xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/usb/host/ehci-ar71xx.c 2009-04-13 14:27:34.859087838 +0200 +@@ -0,0 +1,235 @@ ++/* ++ * Bus Glue for Atheros AR71xx built-in EHCI controller. ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * Copyright (C) 2007 Atheros Communications, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++ ++#include <asm/mach-ar71xx/platform.h> ++ ++extern int usb_disabled(void); ++ ++static int ehci_ar71xx_init(struct usb_hcd *hcd) ++{ ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ int ret; ++ ++ ehci->caps = hcd->regs; ++ ehci->regs = hcd->regs + ++ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); ++ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ ++ ehci->sbrn = 0x20; ++ ++ ehci_reset(ehci); ++ ++ ret = ehci_init(hcd); ++ if (ret) ++ return ret; ++ ++ ehci_port_power(ehci, 0); ++ ++ return 0; ++} ++ ++static int ehci_ar91xx_init(struct usb_hcd *hcd) ++{ ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ int ret; ++ ++ ehci->caps = hcd->regs + 0x100; ++ ehci->regs = hcd->regs + 0x100 + ++ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); ++ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ ++ hcd->has_tt = 1; ++ ehci->sbrn = 0x20; ++ ++ ehci_reset(ehci); ++ ++ ret = ehci_init(hcd); ++ if (ret) ++ return ret; ++ ++ ehci_port_power(ehci, 0); ++ ++ return 0; ++} ++ ++static int ehci_ar71xx_probe(const struct hc_driver *driver, ++ struct usb_hcd **hcd_out, ++ struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd; ++ struct resource *res; ++ int irq; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res) { ++ dev_dbg(&pdev->dev, "no IRQ specified for %s\n", ++ pdev->dev.bus_id); ++ return -ENODEV; ++ } ++ irq = res->start; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_dbg(&pdev->dev, "no base address specified for %s\n", ++ pdev->dev.bus_id); ++ return -ENODEV; ++ } ++ ++ hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); ++ if (!hcd) ++ return -ENOMEM; ++ ++ hcd->rsrc_start = res->start; ++ hcd->rsrc_len = res->end - res->start + 1; ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ dev_dbg(&pdev->dev, "controller already in use\n"); ++ ret = -EBUSY; ++ goto err_put_hcd; ++ } ++ ++ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ dev_dbg(&pdev->dev, "error mapping memory\n"); ++ ret = -EFAULT; ++ goto err_release_region; ++ } ++ ++ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); ++ if (ret) ++ goto err_iounmap; ++ ++ return 0; ++ ++ err_iounmap: ++ iounmap(hcd->regs); ++ ++ err_release_region: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ err_put_hcd: ++ usb_put_hcd(hcd); ++ return ret; ++} ++ ++static void ehci_ar71xx_remove(struct usb_hcd *hcd, ++ struct platform_device *pdev) ++{ ++ usb_remove_hcd(hcd); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++} ++ ++static const struct hc_driver ehci_ar71xx_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Atheros AR71xx built-in EHCI controller", ++ .hcd_priv_size = sizeof(struct ehci_hcd), ++ ++ .irq = ehci_irq, ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ .reset = ehci_ar71xx_init, ++ .start = ehci_run, ++ .stop = ehci_stop, ++ .shutdown = ehci_shutdown, ++ ++ .urb_enqueue = ehci_urb_enqueue, ++ .urb_dequeue = ehci_urb_dequeue, ++ .endpoint_disable = ehci_endpoint_disable, ++ ++ .get_frame_number = ehci_get_frame, ++ ++ .hub_status_data = ehci_hub_status_data, ++ .hub_control = ehci_hub_control, ++#ifdef CONFIG_PM ++ .hub_suspend = ehci_hub_suspend, ++ .hub_resume = ehci_hub_resume, ++#endif ++ .relinquish_port = ehci_relinquish_port, ++ .port_handed_over = ehci_port_handed_over, ++}; ++ ++static const struct hc_driver ehci_ar91xx_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Atheros AR91xx built-in EHCI controller", ++ .hcd_priv_size = sizeof(struct ehci_hcd), ++ .irq = ehci_irq, ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ .reset = ehci_ar91xx_init, ++ .start = ehci_run, ++ .stop = ehci_stop, ++ .shutdown = ehci_shutdown, ++ ++ .urb_enqueue = ehci_urb_enqueue, ++ .urb_dequeue = ehci_urb_dequeue, ++ .endpoint_disable = ehci_endpoint_disable, ++ ++ .get_frame_number = ehci_get_frame, ++ ++ .hub_status_data = ehci_hub_status_data, ++ .hub_control = ehci_hub_control, ++#ifdef CONFIG_PM ++ .hub_suspend = ehci_hub_suspend, ++ .hub_resume = ehci_hub_resume, ++#endif ++ .relinquish_port = ehci_relinquish_port, ++ .port_handed_over = ehci_port_handed_over, ++}; ++ ++static int ehci_ar71xx_driver_probe(struct platform_device *pdev) ++{ ++ struct ar71xx_ehci_platform_data *pdata; ++ struct usb_hcd *hcd = NULL; ++ int ret; ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, "no platform data specified for %s\n", ++ pdev->dev.bus_id); ++ return -ENODEV; ++ } ++ ++ if (pdata->is_ar91xx) ++ ret = ehci_ar71xx_probe(&ehci_ar91xx_hc_driver, &hcd, pdev); ++ else ++ ret = ehci_ar71xx_probe(&ehci_ar71xx_hc_driver, &hcd, pdev); ++ ++ return ret; ++} ++ ++static int ehci_ar71xx_driver_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ ++ ehci_ar71xx_remove(hcd, pdev); ++ return 0; ++} ++ ++MODULE_ALIAS("platform:ar71xx-ehci"); ++ ++static struct platform_driver ehci_ar71xx_driver = { ++ .probe = ehci_ar71xx_driver_probe, ++ .remove = ehci_ar71xx_driver_remove, ++ .driver = { ++ .name = "ar71xx-ehci", ++ } ++}; +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/ehci-hcd.c linux-2.6.29.1/drivers/usb/host/ehci-hcd.c +--- linux-2.6.29.1.orig/drivers/usb/host/ehci-hcd.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/usb/host/ehci-hcd.c 2009-04-13 14:27:34.859087838 +0200 +@@ -1036,6 +1036,11 @@ + #define PLATFORM_DRIVER ixp4xx_ehci_driver + #endif + ++#ifdef CONFIG_USB_EHCI_AR71XX ++#include "ehci-ar71xx.c" ++#define PLATFORM_DRIVER ehci_ar71xx_driver ++#endif ++ + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) + #error "missing bus glue for ehci-hcd" +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/ohci-ar71xx.c linux-2.6.29.1/drivers/usb/host/ohci-ar71xx.c +--- linux-2.6.29.1.orig/drivers/usb/host/ohci-ar71xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/usb/host/ohci-ar71xx.c 2009-04-13 14:27:34.887088611 +0200 +@@ -0,0 +1,165 @@ ++/* ++ * OHCI HCD (Host Controller Driver) for USB. ++ * ++ * Bus Glue for Atheros AR71xx built-in OHCI controller. ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * Parts of this file are based on Atheros' 2.6.15 BSP ++ * Copyright (C) 2007 Atheros Communications, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++ ++extern int usb_disabled(void); ++ ++static int usb_hcd_ar71xx_probe(const struct hc_driver *driver, ++ struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd; ++ struct resource *res; ++ int irq; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res) { ++ dev_dbg(&pdev->dev, "no IRQ specified for %s\n", ++ pdev->dev.bus_id); ++ return -ENODEV; ++ } ++ irq = res->start; ++ ++ hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); ++ if (!hcd) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_dbg(&pdev->dev, "no base address specified for %s\n", ++ pdev->dev.bus_id); ++ ret = -ENODEV; ++ goto err_put_hcd; ++ } ++ hcd->rsrc_start = res->start; ++ hcd->rsrc_len = res->end - res->start + 1; ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ dev_dbg(&pdev->dev, "controller already in use\n"); ++ ret = -EBUSY; ++ goto err_put_hcd; ++ } ++ ++ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ dev_dbg(&pdev->dev, "error mapping memory\n"); ++ ret = -EFAULT; ++ goto err_release_region; ++ } ++ ++ ohci_hcd_init(hcd_to_ohci(hcd)); ++ ++ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); ++ if (ret) ++ goto err_stop_hcd; ++ ++ return 0; ++ ++ err_stop_hcd: ++ iounmap(hcd->regs); ++ err_release_region: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ err_put_hcd: ++ usb_put_hcd(hcd); ++ return ret; ++} ++ ++void usb_hcd_ar71xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) ++{ ++ usb_remove_hcd(hcd); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++} ++ ++static int __devinit ohci_ar71xx_start(struct usb_hcd *hcd) ++{ ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int ret; ++ ++ ret = ohci_init(ohci); ++ if (ret < 0) ++ return ret; ++ ++ ret = ohci_run(ohci); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++ ++ err: ++ ohci_stop(hcd); ++ return ret; ++} ++ ++static const struct hc_driver ohci_ar71xx_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Atheros AR71xx built-in OHCI controller", ++ .hcd_priv_size = sizeof(struct ohci_hcd), ++ ++ .irq = ohci_irq, ++ .flags = HCD_USB11 | HCD_MEMORY, ++ ++ .start = ohci_ar71xx_start, ++ .stop = ohci_stop, ++ .shutdown = ohci_shutdown, ++ ++ .urb_enqueue = ohci_urb_enqueue, ++ .urb_dequeue = ohci_urb_dequeue, ++ .endpoint_disable = ohci_endpoint_disable, ++ ++ /* ++ * scheduling support ++ */ ++ .get_frame_number = ohci_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = ohci_hub_status_data, ++ .hub_control = ohci_hub_control, ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++static int ohci_hcd_ar71xx_drv_probe(struct platform_device *pdev) ++{ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ return usb_hcd_ar71xx_probe(&ohci_ar71xx_hc_driver, pdev); ++} ++ ++static int ohci_hcd_ar71xx_drv_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ ++ usb_hcd_ar71xx_remove(hcd, pdev); ++ return 0; ++} ++ ++MODULE_ALIAS("platform:ar71xx-ohci"); ++ ++static struct platform_driver ohci_hcd_ar71xx_driver = { ++ .probe = ohci_hcd_ar71xx_drv_probe, ++ .remove = ohci_hcd_ar71xx_drv_remove, ++ .shutdown = usb_hcd_platform_shutdown, ++ .driver = { ++ .name = "ar71xx-ohci", ++ .owner = THIS_MODULE, ++ }, ++}; +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/ohci-hcd.c linux-2.6.29.1/drivers/usb/host/ohci-hcd.c +--- linux-2.6.29.1.orig/drivers/usb/host/ohci-hcd.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/usb/host/ohci-hcd.c 2009-04-13 14:27:34.887088611 +0200 +@@ -1080,6 +1080,11 @@ + #define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver + #endif + ++#ifdef CONFIG_USB_OHCI_AR71XX ++#include "ohci-ar71xx.c" ++#define PLATFORM_DRIVER ohci_hcd_ar71xx_driver ++#endif ++ + #if !defined(PCI_DRIVER) && \ + !defined(PLATFORM_DRIVER) && \ + !defined(OF_PLATFORM_DRIVER) && \ +diff -Nur linux-2.6.29.1.orig/drivers/watchdog/Kconfig linux-2.6.29.1/drivers/watchdog/Kconfig +--- linux-2.6.29.1.orig/drivers/watchdog/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/watchdog/Kconfig 2009-04-13 14:27:34.891089680 +0200 +@@ -766,6 +766,13 @@ + help + Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. + ++config AR71XX_WDT ++ tristate "Atheros AR71xx Watchdog Timer" ++ depends on ATHEROS_AR71XX ++ help ++ Hardware driver for the built-in watchdog timer on the Atheros ++ AR71xx SoCs. ++ + # PARISC Architecture + + # POWERPC Architecture +diff -Nur linux-2.6.29.1.orig/drivers/watchdog/Makefile linux-2.6.29.1/drivers/watchdog/Makefile +--- linux-2.6.29.1.orig/drivers/watchdog/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/watchdog/Makefile 2009-04-13 14:27:34.891089680 +0200 +@@ -107,6 +107,7 @@ + obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o + obj-$(CONFIG_AR7_WDT) += ar7_wdt.o + obj-$(CONFIG_TXX9_WDT) += txx9wdt.o ++obj-$(CONFIG_AR71XX_WDT) += ar71xx_wdt.o + + # PARISC Architecture + +diff -Nur linux-2.6.29.1.orig/drivers/watchdog/ar71xx_wdt.c linux-2.6.29.1/drivers/watchdog/ar71xx_wdt.c +--- linux-2.6.29.1.orig/drivers/watchdog/ar71xx_wdt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/watchdog/ar71xx_wdt.c 2009-04-13 14:27:34.915091622 +0200 +@@ -0,0 +1,270 @@ ++/* ++ * Driver for the Atheros AR71xx SoC's built-in hardware watchdog timer. ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This driver was based on: drivers/watchdog/ixp4xx_wdt.c ++ * Author: Deepak Saxena <dsaxena@plexity.net> ++ * Copyright 2004 (c) MontaVista, Software, Inc. ++ * ++ * which again was based on sa1100 driver, ++ * Copyright (C) 2000 Oleg Drokin <green@crimea.edu> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/errno.h> ++#include <linux/fs.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/platform_device.h> ++#include <linux/types.h> ++#include <linux/watchdog.h> ++ ++#include <asm/mach-ar71xx/ar71xx.h> ++ ++#define DRV_NAME "ar71xx-wdt" ++#define DRV_DESC "Atheros AR71xx hardware watchdog driver" ++#define DRV_VERSION "0.1.0" ++ ++#define WDT_TIMEOUT 15 /* seconds */ ++ ++static int nowayout = WATCHDOG_NOWAYOUT; ++ ++#ifdef CONFIG_WATCHDOG_NOWAYOUT ++module_param(nowayout, int, 0); ++MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " ++ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++#endif ++ ++static unsigned long wdt_flags; ++ ++#define WDT_FLAGS_BUSY 0 ++#define WDT_FLAGS_EXPECT_CLOSE 1 ++ ++static int wdt_timeout = WDT_TIMEOUT; ++static int boot_status; ++static int max_timeout; ++ ++static void inline ar71xx_wdt_keepalive(void) ++{ ++ ar71xx_reset_wr(AR71XX_RESET_REG_WDOG, ar71xx_ahb_freq * wdt_timeout); ++} ++ ++static void inline ar71xx_wdt_enable(void) ++{ ++ printk(KERN_DEBUG DRV_NAME ": enabling watchdog timer\n"); ++ ar71xx_wdt_keepalive(); ++ ar71xx_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR); ++} ++ ++static void inline ar71xx_wdt_disable(void) ++{ ++ printk(KERN_DEBUG DRV_NAME ": disabling watchdog timer\n"); ++ ar71xx_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE); ++} ++ ++static int ar71xx_wdt_set_timeout(int val) ++{ ++ if (val < 1 || val > max_timeout) ++ return -EINVAL; ++ ++ wdt_timeout = val; ++ ar71xx_wdt_keepalive(); ++ ++ printk(KERN_DEBUG DRV_NAME ": timeout=%d secs\n", wdt_timeout); ++ ++ return 0; ++} ++ ++static int ar71xx_wdt_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags)) ++ return -EBUSY; ++ ++ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags); ++ ++ ar71xx_wdt_enable(); ++ ++ return nonseekable_open(inode, file); ++} ++ ++static int ar71xx_wdt_release(struct inode *inode, struct file *file) ++{ ++ if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags)) { ++ ar71xx_wdt_disable(); ++ } else { ++ printk(KERN_CRIT DRV_NAME ": device closed unexpectedly, " ++ "watchdog timer will not stop!\n"); ++ } ++ ++ clear_bit(WDT_FLAGS_BUSY, &wdt_flags); ++ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags); ++ ++ return 0; ++} ++ ++static ssize_t ar71xx_wdt_write(struct file *file, const char *data, ++ size_t len, loff_t *ppos) ++{ ++ if (len) { ++ if (!nowayout) { ++ size_t i; ++ ++ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags); ++ ++ for (i = 0; i != len; i++) { ++ char c; ++ ++ if (get_user(c, data + i)) ++ return -EFAULT; ++ ++ if (c == 'V') ++ set_bit(WDT_FLAGS_EXPECT_CLOSE, ++ &wdt_flags); ++ } ++ } ++ ++ ar71xx_wdt_keepalive(); ++ } ++ ++ return len; ++} ++ ++static struct watchdog_info ar71xx_wdt_info = { ++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | ++ WDIOF_MAGICCLOSE | WDIOF_CARDRESET, ++ .firmware_version = 0, ++ .identity = "AR71XX watchdog", ++}; ++ ++static int ar71xx_wdt_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ int t; ++ int ret; ++ ++ switch (cmd) { ++ case WDIOC_GETSUPPORT: ++ ret = copy_to_user((struct watchdog_info *)arg, ++ &ar71xx_wdt_info, ++ sizeof(&ar71xx_wdt_info)) ? -EFAULT : 0; ++ break; ++ ++ case WDIOC_GETSTATUS: ++ ret = put_user(0, (int *)arg) ? -EFAULT : 0; ++ break; ++ ++ case WDIOC_GETBOOTSTATUS: ++ ret = put_user(boot_status, (int *)arg) ? -EFAULT : 0; ++ break; ++ ++ case WDIOC_KEEPALIVE: ++ ar71xx_wdt_keepalive(); ++ ret = 0; ++ break; ++ ++ case WDIOC_SETTIMEOUT: ++ ret = get_user(t, (int *)arg) ? -EFAULT : 0; ++ if (ret) ++ break; ++ ++ ret = ar71xx_wdt_set_timeout(t); ++ if (ret) ++ break; ++ ++ /* fallthrough */ ++ case WDIOC_GETTIMEOUT: ++ ret = put_user(wdt_timeout, (int *)arg) ? -EFAULT : 0; ++ break; ++ ++ default: ++ ret = -ENOTTY; ++ break; ++ } ++ ++ return ret; ++} ++ ++static const struct file_operations ar71xx_wdt_fops = { ++ .owner = THIS_MODULE, ++ .write = ar71xx_wdt_write, ++ .ioctl = ar71xx_wdt_ioctl, ++ .open = ar71xx_wdt_open, ++ .release = ar71xx_wdt_release, ++}; ++ ++static struct miscdevice ar71xx_wdt_miscdev = { ++ .minor = WATCHDOG_MINOR, ++ .name = "watchdog", ++ .fops = &ar71xx_wdt_fops, ++}; ++ ++static int __devinit ar71xx_wdt_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ max_timeout = (0xfffffffful / ar71xx_ahb_freq); ++ wdt_timeout = (max_timeout < WDT_TIMEOUT) ? max_timeout : WDT_TIMEOUT; ++ ++ boot_status = ++ (ar71xx_reset_rr(AR71XX_RESET_REG_WDOG_CTRL) & WDOG_CTRL_LAST_RESET) ? ++ WDIOF_CARDRESET : 0; ++ ++ ret = misc_register(&ar71xx_wdt_miscdev); ++ if (ret) ++ goto err_out; ++ ++ printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n"); ++ ++ printk(KERN_DEBUG DRV_NAME ": timeout=%d secs (max=%d)\n", ++ wdt_timeout, max_timeout); ++ ++ return 0; ++ ++err_out: ++ return ret; ++} ++ ++static int __devexit ar71xx_wdt_remove(struct platform_device *pdev) ++{ ++ misc_deregister(&ar71xx_wdt_miscdev); ++ return 0; ++} ++ ++static struct platform_driver ar71xx_wdt_driver = { ++ .probe = ar71xx_wdt_probe, ++ .remove = __devexit_p(ar71xx_wdt_remove), ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ar71xx_wdt_init(void) ++{ ++ return platform_driver_register(&ar71xx_wdt_driver); ++} ++module_init(ar71xx_wdt_init); ++ ++static void __exit ar71xx_wdt_exit(void) ++{ ++ platform_driver_unregister(&ar71xx_wdt_driver); ++} ++module_exit(ar71xx_wdt_exit); ++ ++MODULE_DESCRIPTION(DRV_DESC); ++MODULE_VERSION(DRV_VERSION); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); ++MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRV_NAME); ++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +diff -Nur linux-2.6.29.1.orig/include/asm-mips/mips_machine.h linux-2.6.29.1/include/asm-mips/mips_machine.h +--- linux-2.6.29.1.orig/include/asm-mips/mips_machine.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/include/asm-mips/mips_machine.h 2009-04-13 14:27:34.915091622 +0200 +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __ASM_MIPS_MACHINE_H ++#define __ASM_MIPS_MACHINE_H ++ ++#include <linux/init.h> ++#include <linux/list.h> ++ ++#define MIPS_MACHINE_NAME_LEN 64 ++ ++struct mips_machine { ++ unsigned long mach_type; ++ void (*mach_setup)(void); ++ unsigned char mach_name[MIPS_MACHINE_NAME_LEN]; ++ struct list_head list; ++}; ++ ++void mips_machine_register(struct mips_machine *) __init; ++void mips_machine_setup(unsigned long machtype) __init; ++ ++extern unsigned char mips_machine_name[MIPS_MACHINE_NAME_LEN]; ++ ++#define MIPS_MACHINE(_type, _name, _setup) \ ++static struct mips_machine machine_##_type __initdata = \ ++{ \ ++ .mach_type = _type, \ ++ .mach_name = _name, \ ++ .mach_setup = _setup, \ ++}; \ ++ \ ++static int __init register_machine_##_type(void) \ ++{ \ ++ mips_machine_register(&machine_##_type); \ ++ return 0; \ ++} \ ++ \ ++pure_initcall(register_machine_##_type) ++ ++#endif /* __ASM_MIPS_MACHINE_H */ ++ +diff -Nur linux-2.6.29.1.orig/include/linux/ath9k_platform.h linux-2.6.29.1/include/linux/ath9k_platform.h +--- linux-2.6.29.1.orig/include/linux/ath9k_platform.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/include/linux/ath9k_platform.h 2009-04-13 14:27:34.915091622 +0200 +@@ -0,0 +1,20 @@ ++/* ++ * ath9k platform data defines ++ * ++ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#ifndef _LINUX_ATH9K_PLATFORM_H ++#define _LINUX_ATH9L_PLATFORM_H ++ ++#define ATH9K_PLAT_EEP_MAX_WORDS 2048 ++ ++struct ath9k_platform_data { ++ u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; ++}; ++ ++#endif /* _LINUX_ATH9K_PLATFORM_H */ +diff -Nur linux-2.6.29.1.orig/include/linux/gpio_buttons.h linux-2.6.29.1/include/linux/gpio_buttons.h +--- linux-2.6.29.1.orig/include/linux/gpio_buttons.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/include/linux/gpio_buttons.h 2009-04-13 14:27:34.919091574 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * Definitions for the GPIO buttons interface driver ++ * ++ * Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org> ++ * ++ * This file was based on: /include/linux/gpio_keys.h ++ * The original gpio_keys.h seems not to have a license. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef _GPIO_BUTTONS_H_ ++#define _GPIO_BUTTONS_H_ ++ ++struct gpio_button { ++ int gpio; /* GPIO line number */ ++ int active_low; ++ char *desc; /* button description */ ++ int type; /* input event type (EV_KEY, EV_SW) */ ++ int code; /* input event code (KEY_*, SW_*) */ ++ int count; ++ int threshold; /* count threshold */ ++}; ++ ++struct gpio_buttons_platform_data { ++ struct gpio_button *buttons; ++ int nbuttons; /* number of buttons */ ++ int poll_interval; /* polling interval */ ++}; ++ ++#endif /* _GPIO_BUTTONS_H_ */ ++ +diff -Nur linux-2.6.29.1.orig/include/linux/phy.h linux-2.6.29.1/include/linux/phy.h +--- linux-2.6.29.1.orig/include/linux/phy.h 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/include/linux/phy.h 2009-04-13 17:29:37.057990291 +0200 +@@ -478,6 +478,7 @@ + void phy_stop_machine(struct phy_device *phydev); + int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); + int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); ++int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr); + int phy_mii_ioctl(struct phy_device *phydev, + struct mii_ioctl_data *mii_data, int cmd); + int phy_start_interrupts(struct phy_device *phydev); +diff -Nur linux-2.6.29.1.orig/include/linux/phy.h.orig linux-2.6.29.1/include/linux/phy.h.orig +--- linux-2.6.29.1.orig/include/linux/phy.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/include/linux/phy.h.orig 2009-04-02 22:55:27.000000000 +0200 +@@ -0,0 +1,500 @@ ++/* ++ * include/linux/phy.h ++ * ++ * Framework and drivers for configuring and reading different PHYs ++ * Based on code in sungem_phy.c and gianfar_phy.c ++ * ++ * Author: Andy Fleming ++ * ++ * Copyright (c) 2004 Freescale Semiconductor, Inc. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef __PHY_H ++#define __PHY_H ++ ++#include <linux/spinlock.h> ++#include <linux/device.h> ++#include <linux/ethtool.h> ++#include <linux/mii.h> ++#include <linux/timer.h> ++#include <linux/workqueue.h> ++ ++#include <asm/atomic.h> ++ ++#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ ++ SUPPORTED_10baseT_Full | \ ++ SUPPORTED_100baseT_Half | \ ++ SUPPORTED_100baseT_Full | \ ++ SUPPORTED_Autoneg | \ ++ SUPPORTED_TP | \ ++ SUPPORTED_MII) ++ ++#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \ ++ SUPPORTED_1000baseT_Half | \ ++ SUPPORTED_1000baseT_Full) ++ ++/* ++ * Set phydev->irq to PHY_POLL if interrupts are not supported, ++ * or not desired for this PHY. Set to PHY_IGNORE_INTERRUPT if ++ * the attached driver handles the interrupt ++ */ ++#define PHY_POLL -1 ++#define PHY_IGNORE_INTERRUPT -2 ++ ++#define PHY_HAS_INTERRUPT 0x00000001 ++#define PHY_HAS_MAGICANEG 0x00000002 ++ ++/* Interface Mode definitions */ ++typedef enum { ++ PHY_INTERFACE_MODE_MII, ++ PHY_INTERFACE_MODE_GMII, ++ PHY_INTERFACE_MODE_SGMII, ++ PHY_INTERFACE_MODE_TBI, ++ PHY_INTERFACE_MODE_RMII, ++ PHY_INTERFACE_MODE_RGMII, ++ PHY_INTERFACE_MODE_RGMII_ID, ++ PHY_INTERFACE_MODE_RGMII_RXID, ++ PHY_INTERFACE_MODE_RGMII_TXID, ++ PHY_INTERFACE_MODE_RTBI ++} phy_interface_t; ++ ++ ++#define PHY_INIT_TIMEOUT 100000 ++#define PHY_STATE_TIME 1 ++#define PHY_FORCE_TIMEOUT 10 ++#define PHY_AN_TIMEOUT 10 ++ ++#define PHY_MAX_ADDR 32 ++ ++/* Used when trying to connect to a specific phy (mii bus id:phy device id) */ ++#define PHY_ID_FMT "%s:%02x" ++ ++/* ++ * Need to be a little smaller than phydev->dev.bus_id to leave room ++ * for the ":%02x" ++ */ ++#define MII_BUS_ID_SIZE (BUS_ID_SIZE - 3) ++ ++/* ++ * The Bus class for PHYs. Devices which provide access to ++ * PHYs should register using this structure ++ */ ++struct mii_bus { ++ const char *name; ++ char id[MII_BUS_ID_SIZE]; ++ void *priv; ++ int (*read)(struct mii_bus *bus, int phy_id, int regnum); ++ int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val); ++ int (*reset)(struct mii_bus *bus); ++ ++ /* ++ * A lock to ensure that only one thing can read/write ++ * the MDIO bus at a time ++ */ ++ struct mutex mdio_lock; ++ ++ struct device *parent; ++ enum { ++ MDIOBUS_ALLOCATED = 1, ++ MDIOBUS_REGISTERED, ++ MDIOBUS_UNREGISTERED, ++ MDIOBUS_RELEASED, ++ } state; ++ struct device dev; ++ ++ /* list of all PHYs on bus */ ++ struct phy_device *phy_map[PHY_MAX_ADDR]; ++ ++ /* Phy addresses to be ignored when probing */ ++ u32 phy_mask; ++ ++ /* ++ * Pointer to an array of interrupts, each PHY's ++ * interrupt at the index matching its address ++ */ ++ int *irq; ++}; ++#define to_mii_bus(d) container_of(d, struct mii_bus, dev) ++ ++struct mii_bus *mdiobus_alloc(void); ++int mdiobus_register(struct mii_bus *bus); ++void mdiobus_unregister(struct mii_bus *bus); ++void mdiobus_free(struct mii_bus *bus); ++struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); ++int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum); ++int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val); ++ ++ ++#define PHY_INTERRUPT_DISABLED 0x0 ++#define PHY_INTERRUPT_ENABLED 0x80000000 ++ ++/* PHY state machine states: ++ * ++ * DOWN: PHY device and driver are not ready for anything. probe ++ * should be called if and only if the PHY is in this state, ++ * given that the PHY device exists. ++ * - PHY driver probe function will, depending on the PHY, set ++ * the state to STARTING or READY ++ * ++ * STARTING: PHY device is coming up, and the ethernet driver is ++ * not ready. PHY drivers may set this in the probe function. ++ * If they do, they are responsible for making sure the state is ++ * eventually set to indicate whether the PHY is UP or READY, ++ * depending on the state when the PHY is done starting up. ++ * - PHY driver will set the state to READY ++ * - start will set the state to PENDING ++ * ++ * READY: PHY is ready to send and receive packets, but the ++ * controller is not. By default, PHYs which do not implement ++ * probe will be set to this state by phy_probe(). If the PHY ++ * driver knows the PHY is ready, and the PHY state is STARTING, ++ * then it sets this STATE. ++ * - start will set the state to UP ++ * ++ * PENDING: PHY device is coming up, but the ethernet driver is ++ * ready. phy_start will set this state if the PHY state is ++ * STARTING. ++ * - PHY driver will set the state to UP when the PHY is ready ++ * ++ * UP: The PHY and attached device are ready to do work. ++ * Interrupts should be started here. ++ * - timer moves to AN ++ * ++ * AN: The PHY is currently negotiating the link state. Link is ++ * therefore down for now. phy_timer will set this state when it ++ * detects the state is UP. config_aneg will set this state ++ * whenever called with phydev->autoneg set to AUTONEG_ENABLE. ++ * - If autonegotiation finishes, but there's no link, it sets ++ * the state to NOLINK. ++ * - If aneg finishes with link, it sets the state to RUNNING, ++ * and calls adjust_link ++ * - If autonegotiation did not finish after an arbitrary amount ++ * of time, autonegotiation should be tried again if the PHY ++ * supports "magic" autonegotiation (back to AN) ++ * - If it didn't finish, and no magic_aneg, move to FORCING. ++ * ++ * NOLINK: PHY is up, but not currently plugged in. ++ * - If the timer notes that the link comes back, we move to RUNNING ++ * - config_aneg moves to AN ++ * - phy_stop moves to HALTED ++ * ++ * FORCING: PHY is being configured with forced settings ++ * - if link is up, move to RUNNING ++ * - If link is down, we drop to the next highest setting, and ++ * retry (FORCING) after a timeout ++ * - phy_stop moves to HALTED ++ * ++ * RUNNING: PHY is currently up, running, and possibly sending ++ * and/or receiving packets ++ * - timer will set CHANGELINK if we're polling (this ensures the ++ * link state is polled every other cycle of this state machine, ++ * which makes it every other second) ++ * - irq will set CHANGELINK ++ * - config_aneg will set AN ++ * - phy_stop moves to HALTED ++ * ++ * CHANGELINK: PHY experienced a change in link state ++ * - timer moves to RUNNING if link ++ * - timer moves to NOLINK if the link is down ++ * - phy_stop moves to HALTED ++ * ++ * HALTED: PHY is up, but no polling or interrupts are done. Or ++ * PHY is in an error state. ++ * ++ * - phy_start moves to RESUMING ++ * ++ * RESUMING: PHY was halted, but now wants to run again. ++ * - If we are forcing, or aneg is done, timer moves to RUNNING ++ * - If aneg is not done, timer moves to AN ++ * - phy_stop moves to HALTED ++ */ ++enum phy_state { ++ PHY_DOWN=0, ++ PHY_STARTING, ++ PHY_READY, ++ PHY_PENDING, ++ PHY_UP, ++ PHY_AN, ++ PHY_RUNNING, ++ PHY_NOLINK, ++ PHY_FORCING, ++ PHY_CHANGELINK, ++ PHY_HALTED, ++ PHY_RESUMING ++}; ++ ++/* phy_device: An instance of a PHY ++ * ++ * drv: Pointer to the driver for this PHY instance ++ * bus: Pointer to the bus this PHY is on ++ * dev: driver model device structure for this PHY ++ * phy_id: UID for this device found during discovery ++ * state: state of the PHY for management purposes ++ * dev_flags: Device-specific flags used by the PHY driver. ++ * addr: Bus address of PHY ++ * link_timeout: The number of timer firings to wait before the ++ * giving up on the current attempt at acquiring a link ++ * irq: IRQ number of the PHY's interrupt (-1 if none) ++ * phy_timer: The timer for handling the state machine ++ * phy_queue: A work_queue for the interrupt ++ * attached_dev: The attached enet driver's device instance ptr ++ * adjust_link: Callback for the enet controller to respond to ++ * changes in the link state. ++ * adjust_state: Callback for the enet driver to respond to ++ * changes in the state machine. ++ * ++ * speed, duplex, pause, supported, advertising, and ++ * autoneg are used like in mii_if_info ++ * ++ * interrupts currently only supports enabled or disabled, ++ * but could be changed in the future to support enabling ++ * and disabling specific interrupts ++ * ++ * Contains some infrastructure for polling and interrupt ++ * handling, as well as handling shifts in PHY hardware state ++ */ ++struct phy_device { ++ /* Information about the PHY type */ ++ /* And management functions */ ++ struct phy_driver *drv; ++ ++ struct mii_bus *bus; ++ ++ struct device dev; ++ ++ u32 phy_id; ++ ++ enum phy_state state; ++ ++ u32 dev_flags; ++ ++ phy_interface_t interface; ++ ++ /* Bus address of the PHY (0-32) */ ++ int addr; ++ ++ /* ++ * forced speed & duplex (no autoneg) ++ * partner speed & duplex & pause (autoneg) ++ */ ++ int speed; ++ int duplex; ++ int pause; ++ int asym_pause; ++ ++ /* The most recently read link state */ ++ int link; ++ ++ /* Enabled Interrupts */ ++ u32 interrupts; ++ ++ /* Union of PHY and Attached devices' supported modes */ ++ /* See mii.h for more info */ ++ u32 supported; ++ u32 advertising; ++ ++ int autoneg; ++ ++ int link_timeout; ++ ++ /* ++ * Interrupt number for this PHY ++ * -1 means no interrupt ++ */ ++ int irq; ++ ++ /* private data pointer */ ++ /* For use by PHYs to maintain extra state */ ++ void *priv; ++ ++ /* Interrupt and Polling infrastructure */ ++ struct work_struct phy_queue; ++ struct work_struct state_queue; ++ struct timer_list phy_timer; ++ atomic_t irq_disable; ++ ++ struct mutex lock; ++ ++ struct net_device *attached_dev; ++ ++ void (*adjust_link)(struct net_device *dev); ++ ++ void (*adjust_state)(struct net_device *dev); ++}; ++#define to_phy_device(d) container_of(d, struct phy_device, dev) ++ ++/* struct phy_driver: Driver structure for a particular PHY type ++ * ++ * phy_id: The result of reading the UID registers of this PHY ++ * type, and ANDing them with the phy_id_mask. This driver ++ * only works for PHYs with IDs which match this field ++ * name: The friendly name of this PHY type ++ * phy_id_mask: Defines the important bits of the phy_id ++ * features: A list of features (speed, duplex, etc) supported ++ * by this PHY ++ * flags: A bitfield defining certain other features this PHY ++ * supports (like interrupts) ++ * ++ * The drivers must implement config_aneg and read_status. All ++ * other functions are optional. Note that none of these ++ * functions should be called from interrupt time. The goal is ++ * for the bus read/write functions to be able to block when the ++ * bus transaction is happening, and be freed up by an interrupt ++ * (The MPC85xx has this ability, though it is not currently ++ * supported in the driver). ++ */ ++struct phy_driver { ++ u32 phy_id; ++ char *name; ++ unsigned int phy_id_mask; ++ u32 features; ++ u32 flags; ++ ++ /* ++ * Called to initialize the PHY, ++ * including after a reset ++ */ ++ int (*config_init)(struct phy_device *phydev); ++ ++ /* ++ * Called during discovery. Used to set ++ * up device-specific structures, if any ++ */ ++ int (*probe)(struct phy_device *phydev); ++ ++ /* PHY Power Management */ ++ int (*suspend)(struct phy_device *phydev); ++ int (*resume)(struct phy_device *phydev); ++ ++ /* ++ * Configures the advertisement and resets ++ * autonegotiation if phydev->autoneg is on, ++ * forces the speed to the current settings in phydev ++ * if phydev->autoneg is off ++ */ ++ int (*config_aneg)(struct phy_device *phydev); ++ ++ /* Determines the negotiated speed and duplex */ ++ int (*read_status)(struct phy_device *phydev); ++ ++ /* Clears any pending interrupts */ ++ int (*ack_interrupt)(struct phy_device *phydev); ++ ++ /* Enables or disables interrupts */ ++ int (*config_intr)(struct phy_device *phydev); ++ ++ /* Clears up any memory if needed */ ++ void (*remove)(struct phy_device *phydev); ++ ++ struct device_driver driver; ++}; ++#define to_phy_driver(d) container_of(d, struct phy_driver, driver) ++ ++#define PHY_ANY_ID "MATCH ANY PHY" ++#define PHY_ANY_UID 0xffffffff ++ ++/* A Structure for boards to register fixups with the PHY Lib */ ++struct phy_fixup { ++ struct list_head list; ++ char bus_id[BUS_ID_SIZE]; ++ u32 phy_uid; ++ u32 phy_uid_mask; ++ int (*run)(struct phy_device *phydev); ++}; ++ ++/** ++ * phy_read - Convenience function for reading a given PHY register ++ * @phydev: the phy_device struct ++ * @regnum: register number to read ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++static inline int phy_read(struct phy_device *phydev, u16 regnum) ++{ ++ return mdiobus_read(phydev->bus, phydev->addr, regnum); ++} ++ ++/** ++ * phy_write - Convenience function for writing a given PHY register ++ * @phydev: the phy_device struct ++ * @regnum: register number to write ++ * @val: value to write to @regnum ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++static inline int phy_write(struct phy_device *phydev, u16 regnum, u16 val) ++{ ++ return mdiobus_write(phydev->bus, phydev->addr, regnum, val); ++} ++ ++int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id); ++struct phy_device* get_phy_device(struct mii_bus *bus, int addr); ++int phy_clear_interrupt(struct phy_device *phydev); ++int phy_config_interrupt(struct phy_device *phydev, u32 interrupts); ++struct phy_device * phy_attach(struct net_device *dev, ++ const char *bus_id, u32 flags, phy_interface_t interface); ++struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, ++ void (*handler)(struct net_device *), u32 flags, ++ phy_interface_t interface); ++void phy_disconnect(struct phy_device *phydev); ++void phy_detach(struct phy_device *phydev); ++void phy_start(struct phy_device *phydev); ++void phy_stop(struct phy_device *phydev); ++int phy_start_aneg(struct phy_device *phydev); ++ ++void phy_sanitize_settings(struct phy_device *phydev); ++int phy_stop_interrupts(struct phy_device *phydev); ++int phy_enable_interrupts(struct phy_device *phydev); ++int phy_disable_interrupts(struct phy_device *phydev); ++ ++static inline int phy_read_status(struct phy_device *phydev) { ++ return phydev->drv->read_status(phydev); ++} ++ ++int genphy_config_advert(struct phy_device *phydev); ++int genphy_setup_forced(struct phy_device *phydev); ++int genphy_restart_aneg(struct phy_device *phydev); ++int genphy_config_aneg(struct phy_device *phydev); ++int genphy_update_link(struct phy_device *phydev); ++int genphy_read_status(struct phy_device *phydev); ++int genphy_suspend(struct phy_device *phydev); ++int genphy_resume(struct phy_device *phydev); ++void phy_driver_unregister(struct phy_driver *drv); ++int phy_driver_register(struct phy_driver *new_driver); ++void phy_prepare_link(struct phy_device *phydev, ++ void (*adjust_link)(struct net_device *)); ++void phy_start_machine(struct phy_device *phydev, ++ void (*handler)(struct net_device *)); ++void phy_stop_machine(struct phy_device *phydev); ++int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); ++int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); ++int phy_mii_ioctl(struct phy_device *phydev, ++ struct mii_ioctl_data *mii_data, int cmd); ++int phy_start_interrupts(struct phy_device *phydev); ++void phy_print_status(struct phy_device *phydev); ++struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id); ++void phy_device_free(struct phy_device *phydev); ++ ++int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, ++ int (*run)(struct phy_device *)); ++int phy_register_fixup_for_id(const char *bus_id, ++ int (*run)(struct phy_device *)); ++int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, ++ int (*run)(struct phy_device *)); ++int phy_scan_fixups(struct phy_device *phydev); ++ ++int __init mdio_bus_init(void); ++void mdio_bus_exit(void); ++ ++extern struct bus_type mdio_bus_type; ++#endif /* __PHY_H */ diff --git a/target/rb433/patches/ip175-switch.patch b/target/rb433/patches/ip175-switch.patch new file mode 100644 index 000000000..b5fdb2709 --- /dev/null +++ b/target/rb433/patches/ip175-switch.patch @@ -0,0 +1,1364 @@ +diff -Nur linux-2.6.29.1.orig/drivers/net/phy/Kconfig linux-2.6.29.1/drivers/net/phy/Kconfig +--- linux-2.6.29.1.orig/drivers/net/phy/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/net/phy/Kconfig 2009-04-17 12:23:55.049703765 +0200 +@@ -61,6 +61,11 @@ + ---help--- + Currently supports the IP175C PHY. + ++config IP175_PHY ++ tristate "Drivers for ICPlus PHYs (swconfig compatible)" ++ ---help--- ++ Currently supports the IP175C PHY. ++ + config REALTEK_PHY + tristate "Drivers for Realtek PHYs" + ---help--- +diff -Nur linux-2.6.29.1.orig/drivers/net/phy/Makefile linux-2.6.29.1/drivers/net/phy/Makefile +--- linux-2.6.29.1.orig/drivers/net/phy/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/net/phy/Makefile 2009-04-17 12:24:22.879180918 +0200 +@@ -12,6 +12,7 @@ + obj-$(CONFIG_VITESSE_PHY) += vitesse.o + obj-$(CONFIG_BROADCOM_PHY) += broadcom.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o ++obj-$(CONFIG_IP175_PHY) += swconfig-ip175c.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o + obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o + obj-$(CONFIG_FIXED_PHY) += fixed.o +diff -Nur linux-2.6.29.1.orig/drivers/net/phy/swconfig-ip175c.c linux-2.6.29.1/drivers/net/phy/swconfig-ip175c.c +--- linux-2.6.29.1.orig/drivers/net/phy/swconfig-ip175c.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/net/phy/swconfig-ip175c.c 2009-04-17 13:59:08.478770561 +0200 +@@ -0,0 +1,1334 @@ ++/* ++ * swconfig-ip175c.c: Swconfig configuration for IC+ IP175C switch ++ * Version 0.3 ++ * ++ * Copyright (C) 2008 Patrick Horn <patrick.horn@gmail.com> ++ * Copyright (C) 2008 Martin Mares <mj@ucw.cz> ++ * ++ * 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. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/skbuff.h> ++#include <linux/mii.h> ++#include <linux/phy.h> ++#include <linux/delay.h> ++ ++#include <linux/switch.h> ++ ++#define MAX_VLANS 16 ++#define MAX_PORTS 9 ++ ++#define MODULE_NAME "swconfig-ip175c" ++#define DRIVER_NAME "ip175c" ++#define DRIVER_VERSION "0.3" ++ ++typedef struct ip175c_reg { ++ u16 p; // phy ++ u16 m; // mii ++} reg; ++typedef char bitnum; ++ ++#define NOTSUPPORTED {-1,-1} ++ ++#define REG_SUPP(x) (((x).m != ((u16)-1)) && ((x).p != (u16)-1)) ++ ++/*********** CONSTANTS ***********/ ++struct register_mappings { ++ char *NAME; ++ u16 MODEL_NO; // compare to bits 4-9 of MII register 0,3. ++ bitnum NUM_PORTS; ++ bitnum CPU_PORT; ++ ++/* The default VLAN for each port. ++ Default: 0x0001 for Ports 0,1,2,3 ++ 0x0002 for Ports 4,5 */ ++ reg VLAN_DEFAULT_TAG_REG[MAX_PORTS]; ++ ++/* These ports are tagged. ++ Default: 0x00 */ ++ reg ADD_TAG_REG; ++ reg REMOVE_TAG_REG; ++ bitnum ADD_TAG_BIT[MAX_PORTS]; ++/* These ports are untagged. ++ Default: 0x00 (i.e. do not alter any VLAN tags...) ++ Maybe set to 0 if user disables VLANs. */ ++ bitnum REMOVE_TAG_BIT[MAX_PORTS]; ++ ++/* Port M and Port N are on the same VLAN. ++ Default: All ports on all VLANs. */ ++// Use register {29, 19+N/2} ++ reg VLAN_LOOKUP_REG; ++// Port 5 uses register {30, 18} but same as odd bits. ++ reg VLAN_LOOKUP_REG_5; // in a different register on IP175C. ++ bitnum VLAN_LOOKUP_EVEN_BIT[MAX_PORTS]; ++ bitnum VLAN_LOOKUP_ODD_BIT[MAX_PORTS]; ++ ++/* This VLAN corresponds to which ports. ++ Default: 0x2f,0x30,0x3f,0x3f... */ ++ reg TAG_VLAN_MASK_REG; ++ bitnum TAG_VLAN_MASK_EVEN_BIT[MAX_PORTS]; ++ bitnum TAG_VLAN_MASK_ODD_BIT[MAX_PORTS]; ++ ++ int RESET_VAL; ++ reg RESET_REG; ++ ++/* General flags */ ++ reg ROUTER_CONTROL_REG; ++ reg VLAN_CONTROL_REG; ++ bitnum TAG_VLAN_BIT; ++ bitnum ROUTER_EN_BIT; ++ bitnum NUMLAN_GROUPS_MAX; ++ bitnum NUMLAN_GROUPS_BIT; ++ ++ reg MII_REGISTER_EN; ++ bitnum MII_REGISTER_EN_BIT; ++ ++ // set to 1 for 178C, 0 for 175C. ++ bitnum SIMPLE_VLAN_REGISTERS; // 175C has two vlans per register but 178C has only one. ++}; ++ ++static const struct register_mappings IP178C = { ++ .NAME = "IP178C", ++ .MODEL_NO = 0x18, ++ .VLAN_DEFAULT_TAG_REG = { ++ {30,3},{30,4},{30,5},{30,6},{30,7},{30,8}, ++ {30,9},{30,10},{30,11}, ++ }, ++ ++ .ADD_TAG_REG = {30,12}, ++ .ADD_TAG_BIT = {0,1,2,3,4,5,6,7,8}, ++ .REMOVE_TAG_REG = {30,13}, ++ .REMOVE_TAG_BIT = {4,5,6,7,8,9,10,11,12}, ++ ++ .SIMPLE_VLAN_REGISTERS = 1, ++ ++ .VLAN_LOOKUP_REG = {31,0},// +N ++ .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, // not used with SIMPLE_VLAN_REGISTERS ++ .VLAN_LOOKUP_EVEN_BIT = {0,1,2,3,4,5,6,7,8}, ++ .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,5,6,7,8}, ++ ++ .TAG_VLAN_MASK_REG = {30,14}, // +N ++ .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,6,7,8}, ++ .TAG_VLAN_MASK_ODD_BIT = {0,1,2,3,4,5,6,7,8}, ++ ++ .RESET_VAL = 0x55AA, ++ .RESET_REG = {30,0}, ++ ++ .ROUTER_CONTROL_REG = {30,30}, ++ .ROUTER_EN_BIT = 11, ++ .NUMLAN_GROUPS_MAX = 8, ++ .NUMLAN_GROUPS_BIT = 8, // {0-2} ++ ++ .VLAN_CONTROL_REG = {30,13}, ++ .TAG_VLAN_BIT = 3, ++ ++ .CPU_PORT = 8, ++ .NUM_PORTS = 9, ++ ++ .MII_REGISTER_EN = NOTSUPPORTED, ++ ++}; ++ ++static const struct register_mappings IP175C = { ++ .NAME = "IP175C", ++ .MODEL_NO = 0x18, ++ .VLAN_DEFAULT_TAG_REG = { ++ {29,24},{29,25},{29,26},{29,27},{29,28},{29,30}, ++ NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED ++ }, ++ ++ .ADD_TAG_REG = {29,23}, ++ .REMOVE_TAG_REG = {29,23}, ++ .ADD_TAG_BIT = {11,12,13,14,15,1,-1,-1,-1}, ++ .REMOVE_TAG_BIT = {6,7,8,9,10,0,-1,-1,-1}, ++ ++ .SIMPLE_VLAN_REGISTERS = 0, ++ ++ .VLAN_LOOKUP_REG = {29,19},// +N/2 ++ .VLAN_LOOKUP_REG_5 = {30,18}, ++ .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,15,-1,-1,-1}, ++ .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,7,-1,-1,-1}, ++ ++ .TAG_VLAN_MASK_REG = {30,1}, // +N/2 ++ .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,-1,-1,-1}, ++ .TAG_VLAN_MASK_ODD_BIT = {8,9,10,11,12,13,-1,-1,-1}, ++ ++ .RESET_VAL = 0x175C, ++ .RESET_REG = {30,0}, ++ ++ .ROUTER_CONTROL_REG = {30,9}, ++ .ROUTER_EN_BIT = 3, ++ .NUMLAN_GROUPS_MAX = 8, ++ .NUMLAN_GROUPS_BIT = 0, // {0-2} ++ ++ .VLAN_CONTROL_REG = {30,9}, ++ .TAG_VLAN_BIT = 7, ++ ++ .NUM_PORTS = 6, ++ .CPU_PORT = 5, ++ ++ .MII_REGISTER_EN = NOTSUPPORTED, ++ ++}; ++ ++static const struct register_mappings IP175A = { ++ .NAME = "IP175A", ++ .MODEL_NO = 0x05, ++ .VLAN_DEFAULT_TAG_REG = { ++ {0,24},{0,25},{0,26},{0,27},{0,28},NOTSUPPORTED, ++ NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED ++ }, ++ ++ .ADD_TAG_REG = {0,23}, ++ .REMOVE_TAG_REG = {0,23}, ++ .ADD_TAG_BIT = {11,12,13,14,15,1,-1,-1,-1}, ++ .REMOVE_TAG_BIT = {6,7,8,9,10,0,-1,-1,-1}, ++ ++ .SIMPLE_VLAN_REGISTERS = 1, ++ ++ // Only programmable via. EEPROM ++ .VLAN_LOOKUP_REG = NOTSUPPORTED,// +N/2 ++ .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, ++ .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,15,-1,-1,-1}, ++ .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,7,-1,-1,-1}, ++ ++ .TAG_VLAN_MASK_REG = NOTSUPPORTED, // +N/2 ++ .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,-1,-1,-1}, ++ .TAG_VLAN_MASK_ODD_BIT = {8,9,10,11,12,13,-1,-1,-1}, ++ ++ .RESET_VAL = -1, ++ .RESET_REG = NOTSUPPORTED, ++ ++ .ROUTER_CONTROL_REG = NOTSUPPORTED, ++ .VLAN_CONTROL_REG = NOTSUPPORTED, ++ .TAG_VLAN_BIT = -1, ++ .ROUTER_EN_BIT = -1, ++ .NUMLAN_GROUPS_MAX = -1, ++ .NUMLAN_GROUPS_BIT = -1, // {0-2} ++ ++ .NUM_PORTS = 6, ++ .CPU_PORT = 5, ++ ++ .MII_REGISTER_EN = {0, 12}, ++ .MII_REGISTER_EN_BIT = 7, ++}; ++ ++struct ip175c_state { ++ struct list_head list; ++ struct mii_bus *mii_bus; ++ struct switch_dev *dev; ++ ++ int router_mode; // ROUTER_EN ++ int vlan_enabled; // TAG_VLAN_EN ++ struct port_state { ++ struct phy_device *phy; ++ unsigned int shareports; ++ u16 vlan_tag; ++ } ports[MAX_PORTS]; ++ unsigned int add_tag; ++ unsigned int remove_tag; ++ int num_vlans; ++ unsigned int vlan_ports[MAX_VLANS]; ++ const struct register_mappings *regs; ++ reg proc_mii; /*!< phy/reg for the low level register access via /proc */ ++ int proc_errno; /*!< error code of the last read/write to "val" */ ++ ++ char buf[80]; ++}; ++ ++static struct list_head state_list; ++ ++static int getPhy (struct ip175c_state *state, reg mii) ++{ ++ struct mii_bus *bus = state->mii_bus; ++ int err; ++ ++ if (!REG_SUPP(mii)) ++ return -EFAULT; ++ mutex_lock(&bus->mdio_lock); ++ err = bus->read(bus, mii.p, mii.m); ++ mutex_unlock(&bus->mdio_lock); ++ if (err < 0) { ++ state->proc_errno = err; ++ pr_warning("IP175C: Unable to get MII register %d,%d: error %d\n", mii.p,mii.m,-err); ++ return err; ++ } ++ ++ pr_debug("IP175C: Read MII register %d,%d -> %04x\n", mii.p, mii.m, err); ++ return err; ++} ++ ++static int setPhy (struct ip175c_state *state, reg mii, u16 value) ++{ ++ struct mii_bus *bus = state->mii_bus; ++ int err; ++ ++ if (!REG_SUPP(mii)) ++ return -EFAULT; ++ mutex_lock(&bus->mdio_lock); ++ err = bus->write(bus, mii.p, mii.m, value); ++ mutex_unlock(&bus->mdio_lock); ++ if (err < 0) { ++ state->proc_errno = err; ++ pr_warning("IP175C: Unable to set MII register %d,%d to %d: error %d\n", mii.p,mii.m,value,-err); ++ return err; ++ } ++ mdelay(2); ++ getPhy(state, mii); ++ pr_debug("IP175C: Set MII register %d,%d to %04x\n", mii.p, mii.m, value); ++ return 0; ++} ++ ++/** ++ * These two macros are to simplify the mapping of logical bits to the bits in hardware. ++ * NOTE: these macros will return if there is an error! ++ */ ++ ++#define GET_PORT_BITS(state, bits, addr, bit_lookup) \ ++ do { \ ++ int i, val = getPhy((state), (addr)); \ ++ if (val < 0) \ ++ return val; \ ++ (bits) = 0; \ ++ for (i = 0; i < MAX_PORTS; i++) { \ ++ if ((bit_lookup)[i] == -1) continue; \ ++ if (val & (1<<(bit_lookup)[i])) \ ++ (bits) |= (1<<i); \ ++ } \ ++ } while (0) ++ ++#define SET_PORT_BITS(state, bits, addr, bit_lookup) \ ++ do { \ ++ int i, val = getPhy((state), (addr)); \ ++ if (val < 0) \ ++ return val; \ ++ for (i = 0; i < MAX_PORTS; i++) { \ ++ unsigned int newmask = ((bits)&(1<<i)); \ ++ if ((bit_lookup)[i] == -1) continue; \ ++ val &= ~(1<<(bit_lookup)[i]); \ ++ val |= ((newmask>>i)<<(bit_lookup)[i]); \ ++ } \ ++ val = setPhy((state), (addr), val); \ ++ if (val < 0) \ ++ return val; \ ++ } while (0) ++ ++static int get_model(struct ip175c_state *state) ++{ ++ reg oui_id_reg = {0, 2}; ++ int oui_id; ++ reg model_no_reg = {0, 3}; ++ int model_no, model_no_orig; ++ ++ // 175 and 178 have the same oui ID. ++ reg oui_id_reg_178c = {5, 2}; // returns error on IP175C. ++ int is_178c = 0; ++ ++ oui_id = getPhy(state, oui_id_reg); ++ if (oui_id != 0x0243) { ++ // non ++ return -ENODEV; // Not a IC+ chip. ++ } ++ oui_id = getPhy(state, oui_id_reg_178c); ++ if (oui_id == 0x0243) { ++ is_178c = 1; ++ } ++ ++ model_no_orig = getPhy(state, model_no_reg); ++ if (model_no_orig < 0) { ++ return -ENODEV; ++ } ++ model_no = model_no_orig >> 4; // shift out revision number. ++ model_no &= 0x3f; // only take the model number (low 6 bits). ++ if (model_no == IP175A.MODEL_NO) { ++ state->regs = &IP175A; ++ } else if (model_no == IP175C.MODEL_NO) { ++ if (is_178c) { ++ state->regs = &IP178C; ++ } else { ++ state->regs = &IP175C; ++ } ++ } else { ++ printk(KERN_WARNING "ip175c: Found an unknown IC+ switch with model number %02Xh.\n", model_no_orig); ++ return -EPERM; ++ } ++ return 0; ++} ++ ++/** Get only the vlan and router flags on the router **/ ++static int get_flags(struct ip175c_state *state) ++{ ++ int val; ++ ++ state->router_mode = 0; ++ state->vlan_enabled = -1; // hack ++ state->num_vlans = 0; ++ ++ if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) { ++ return 0; // not an error if it doesn't support enable vlan. ++ } ++ ++ val = getPhy(state, state->regs->ROUTER_CONTROL_REG); ++ if (val < 0) { ++ return val; ++ } ++ if (state->regs->ROUTER_EN_BIT >= 0) ++ state->router_mode = ((val>>state->regs->ROUTER_EN_BIT) & 1); ++ ++ if (state->regs->NUMLAN_GROUPS_BIT >= 0) { ++ state->num_vlans = (val >> state->regs->NUMLAN_GROUPS_BIT); ++ state->num_vlans &= (state->regs->NUMLAN_GROUPS_MAX-1); ++ state->num_vlans+=1; // does not include WAN. ++ } ++ ++ ++ val = getPhy(state, state->regs->VLAN_CONTROL_REG); ++ if (val < 0) { ++ return 0; ++ } ++ if (state->regs->TAG_VLAN_BIT >= 0) ++ state->vlan_enabled = ((val>>state->regs->TAG_VLAN_BIT) & 1); ++ ++ return 0; ++} ++/** Get all state variables for VLAN mappings and port-based tagging. **/ ++static int get_state(struct ip175c_state *state) ++{ ++ int i, j; ++ int ret; ++ ret = get_flags(state); ++ if (ret < 0) { ++ return ret; ++ } ++ GET_PORT_BITS(state, state->remove_tag, ++ state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT); ++ GET_PORT_BITS(state, state->add_tag, ++ state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT); ++ ++ if (state->vlan_enabled == -1) { ++ // not sure how to get this... ++ state->vlan_enabled = (!state->remove_tag && !state->add_tag); ++ } ++ ++ if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) { ++ for (j=0; j<MAX_PORTS; j++) { ++ state->ports[j].shareports = 0; // initialize them in case. ++ } ++ for (j=0; j<state->regs->NUM_PORTS; j++) { ++ reg addr; ++ const bitnum *bit_lookup = (j%2==0)? ++ state->regs->VLAN_LOOKUP_EVEN_BIT: ++ state->regs->VLAN_LOOKUP_ODD_BIT; ++ addr = state->regs->VLAN_LOOKUP_REG; ++ if (state->regs->SIMPLE_VLAN_REGISTERS) { ++ addr.m += j; ++ } else { ++ switch (j) { ++ case 0: ++ case 1: ++ break; ++ case 2: ++ case 3: ++ addr.m+=1; ++ break; ++ case 4: ++ addr.m+=2; ++ break; ++ case 5: ++ addr = state->regs->VLAN_LOOKUP_REG_5; ++ break; ++ } ++ } ++ ++ if (REG_SUPP(addr)) { ++ GET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup); ++ } ++ } ++ } else { ++ for (j=0; j<MAX_PORTS; j++) { ++ state->ports[j].shareports = 0xff; ++ } ++ } ++ ++ for (i=0; i<MAX_PORTS; i++) { ++ if (REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[i])) { ++ int val = getPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i]); ++ if (val < 0) { ++ return val; ++ } ++ state->ports[i].vlan_tag = val; ++ } else { ++ state->ports[i].vlan_tag = 0; ++ } ++ } ++ ++ if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) { ++ for (j=0; j<MAX_VLANS; j++) { ++ reg addr = state->regs->TAG_VLAN_MASK_REG; ++ const bitnum *bit_lookup = (j%2==0)? ++ state->regs->TAG_VLAN_MASK_EVEN_BIT: ++ state->regs->TAG_VLAN_MASK_ODD_BIT; ++ if (state->regs->SIMPLE_VLAN_REGISTERS) { ++ addr.m += j; ++ } else { ++ addr.m += j/2; ++ } ++ GET_PORT_BITS(state, state->vlan_ports[j], addr, bit_lookup); ++ } ++ } else { ++ for (j=0; j<MAX_VLANS; j++) { ++ state->vlan_ports[j] = 0; ++ for (i=0; i<state->regs->NUM_PORTS; i++) { ++ if ((state->ports[i].vlan_tag == j) || ++ (state->ports[i].vlan_tag == 0)) { ++ state->vlan_ports[j] |= (1<<i); ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/** Only update vlan and router flags in the switch **/ ++static int update_flags(struct ip175c_state *state) ++{ ++ int val; ++ ++ if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) { ++ return 0; ++ } ++ ++ val = getPhy(state, state->regs->ROUTER_CONTROL_REG); ++ if (val < 0) { ++ return val; ++ } ++ if (state->regs->ROUTER_EN_BIT >= 0) { ++ if (state->router_mode) { ++ val |= (1<<state->regs->ROUTER_EN_BIT); ++ } else { ++ val &= (~(1<<state->regs->ROUTER_EN_BIT)); ++ } ++ } ++ if (state->regs->TAG_VLAN_BIT >= 0) { ++ if (state->vlan_enabled) { ++ val |= (1<<state->regs->TAG_VLAN_BIT); ++ } else { ++ val &= (~(1<<state->regs->TAG_VLAN_BIT)); ++ } ++ } ++ if (state->regs->NUMLAN_GROUPS_BIT >= 0) { ++ val &= (~((state->regs->NUMLAN_GROUPS_MAX-1)<<state->regs->NUMLAN_GROUPS_BIT)); ++ if (state->num_vlans > state->regs->NUMLAN_GROUPS_MAX) { ++ val |= state->regs->NUMLAN_GROUPS_MAX << state->regs->NUMLAN_GROUPS_BIT; ++ } else if (state->num_vlans >= 1) { ++ val |= (state->num_vlans-1) << state->regs->NUMLAN_GROUPS_BIT; ++ } ++ } ++ return setPhy(state, state->regs->ROUTER_CONTROL_REG, val); ++} ++ ++/** Update all VLAN and port state. Usually you should call "correct_vlan_state" first. **/ ++static int update_state(struct ip175c_state *state) ++{ ++ int j; ++ int i; ++ SET_PORT_BITS(state, state->add_tag, ++ state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT); ++ SET_PORT_BITS(state, state->remove_tag, ++ state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT); ++ ++ if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) { ++ for (j=0; j<state->regs->NUM_PORTS; j++) { ++ reg addr; ++ const bitnum *bit_lookup = (j%2==0)? ++ state->regs->VLAN_LOOKUP_EVEN_BIT: ++ state->regs->VLAN_LOOKUP_ODD_BIT; ++ ++ // duplicate code -- sorry ++ addr = state->regs->VLAN_LOOKUP_REG; ++ if (state->regs->SIMPLE_VLAN_REGISTERS) { ++ addr.m += j; ++ } else { ++ switch (j) { ++ case 0: ++ case 1: ++ break; ++ case 2: ++ case 3: ++ addr.m+=1; ++ break; ++ case 4: ++ addr.m+=2; ++ break; ++ case 5: ++ addr = state->regs->VLAN_LOOKUP_REG_5; ++ break; ++ default: ++ addr.m = -1; // shouldn't get here, but... ++ break; ++ } ++ } ++ //printf("shareports for %d is %02X\n",j,state->ports[j].shareports); ++ if (REG_SUPP(addr)) { ++ SET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup); ++ } ++ } ++ } ++ if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) { ++ for (j=0; j<MAX_VLANS; j++) { ++ reg addr = state->regs->TAG_VLAN_MASK_REG; ++ const bitnum *bit_lookup = (j%2==0)? ++ state->regs->TAG_VLAN_MASK_EVEN_BIT: ++ state->regs->TAG_VLAN_MASK_ODD_BIT; ++ unsigned int vlan_mask; ++ if (state->regs->SIMPLE_VLAN_REGISTERS) { ++ addr.m += j; ++ } else { ++ addr.m += j/2; ++ } ++ vlan_mask = state->vlan_ports[j]; ++ SET_PORT_BITS(state, vlan_mask, addr, bit_lookup); ++ } ++ } ++ ++ for (i=0; i<MAX_PORTS; i++) { ++ if (REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[i])) { ++ int err = setPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i], ++ state->ports[i].vlan_tag); ++ if (err < 0) { ++ return err; ++ } ++ } ++ } ++ ++ return update_flags(state); ++ ++ // software reset: 30.0 = 0x175C ++ // wait 2ms ++ // reset ports 0,1,2,3,4 ++} ++ ++/* ++ Uses only the VLAN port mask and the add tag mask to generate the other fields: ++ which ports are part of the same VLAN, removing vlan tags, and VLAN tag ids. ++ */ ++static void correct_vlan_state(struct ip175c_state *state) ++{ ++ int i, j; ++ state->num_vlans = 0; ++ for (i=0; i<MAX_VLANS; i++) { ++ if (state->vlan_ports[i] != 0) { ++ state->num_vlans = i+1; //hack -- we need to store the "set" vlans somewhere... ++ } ++ } ++ ++ for (i=0; i<state->regs->NUM_PORTS; i++) { ++ int oldtag = state->ports[i].vlan_tag; ++ if (oldtag >= 0 && oldtag < MAX_VLANS) { ++ if (state->vlan_ports[oldtag] & (1<<i)) { ++ continue; // primary vlan is valid. ++ } ++ } ++ state->ports[i].vlan_tag = 0; ++ } ++ ++ for (i=0; i<state->regs->NUM_PORTS; i++) { ++ unsigned int portmask = (1<<i); ++ state->ports[i].shareports = portmask; ++ for (j=0; j<MAX_VLANS; j++) { ++ if (state->vlan_ports[j] & portmask) { ++ state->ports[i].shareports |= state->vlan_ports[j]; ++ if (state->ports[i].vlan_tag == 0) { ++ state->ports[i].vlan_tag = j; ++ } ++ } ++ } ++ if (!state->vlan_enabled) { ++ // share with everybody! ++ state->ports[i].shareports = (1<<state->regs->NUM_PORTS)-1; ++ } ++ } ++ state->remove_tag = ((~state->add_tag) & ((1<<state->regs->NUM_PORTS)-1)); ++} ++ ++static int ip175c_get_enable_vlan(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ ++ err = get_state(state); // may be set in get_state. ++ if (err < 0) ++ return err; ++ val->value.i = state->vlan_enabled; ++ return 0; ++} ++ ++static int ip175c_set_enable_vlan(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ int enable; ++ int i; ++ ++ err = get_state(state); ++ if (err < 0) ++ return err; ++ enable = val->value.i; ++ //printk(KERN_WARNING "set enable vlan to %d\n",enable); ++ ++ if (state->vlan_enabled == enable) { ++ // do not change any state. ++ return 0; ++ } ++ state->vlan_enabled = enable; ++ ++ // Otherwise, if we are switching state, set fields to a known default. ++ state->remove_tag = 0x0000; ++ state->add_tag = 0x0000; ++ for (i = 0; i < MAX_PORTS; i++) { ++ state->ports[i].vlan_tag = 0; ++ state->ports[i].shareports = 0xffff; ++ } ++ for (i = 0; i < MAX_VLANS; i++) { ++ state->vlan_ports[i] = 0x0; ++ } ++ ++ if (state->vlan_enabled) { ++ // updates other fields only based off vlan_ports and add_tag fields. ++ // Note that by default, no ports are in any vlans. ++ correct_vlan_state(state); ++ } ++ // ensure sane defaults? ++ return update_state(state); ++} ++ ++static int ip175c_get_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ int b; ++ int ind; ++ unsigned int ports; ++ ++ if (val->port_vlan >= dev->vlans || val->port_vlan < 0) ++ return -EINVAL; ++ ++ err = get_state(state); ++ if (err<0) ++ return err; ++ ++ ports = state->vlan_ports[val->port_vlan]; ++ b = 0; ++ ind = 0; ++ while (b < MAX_PORTS) { ++ if (ports&1) { ++ int istagged = ((state->add_tag >> b) & 1); ++ val->value.ports[ind].id = b; ++ val->value.ports[ind].flags = (istagged << SWITCH_PORT_FLAG_TAGGED); ++ ind++; ++ } ++ b++; ++ ports >>= 1; ++ } ++ val->len = ind; ++ ++ return 0; ++} ++ ++static int ip175c_set_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int i; ++ int err; ++ ++ if (val->port_vlan >= dev->vlans || val->port_vlan < 0) ++ return -EINVAL; ++ ++ err = get_state(state); ++ if (err < 0) ++ return err; ++ ++ state->vlan_ports[val->port_vlan] = 0; ++ for (i = 0; i < val->len; i++) { ++ int bitmask = (1<<val->value.ports[i].id); ++ state->vlan_ports[val->port_vlan] |= bitmask; ++ if (val->value.ports[i].flags & (1<<SWITCH_PORT_FLAG_TAGGED)) { ++ state->add_tag |= bitmask; ++ } else { ++ state->add_tag &= (~bitmask); ++ } ++ } ++ /* ++ // no primary vlan id support in swconfig? ++ // primary vlan will be set to the first non-zero vlan a port is a member of. ++ for (i = 0; i< state->regs->NUM_PORTS; i++) { ++ if (vlan_config->pvid & (1<<i)) { ++ state->ports[i].vlan_tag = nr; ++ } ++ } ++ */ ++ ++ correct_vlan_state(state); ++ err = update_state(state); ++ ++ return err; ++} ++ ++static int ip175c_apply(struct switch_dev *dev) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ ++ err = get_flags(state); ++ if (err < 0) ++ return err; ++ ++ if (REG_SUPP(state->regs->MII_REGISTER_EN)){ ++ int val = getPhy(state, state->regs->MII_REGISTER_EN); ++ if (val < 0) { ++ return val; ++ } ++ val |= (1<<state->regs->MII_REGISTER_EN_BIT); ++ return setPhy(state, state->regs->MII_REGISTER_EN, val); ++ } ++ return 0; ++} ++ ++static int ip175c_reset(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ ++ err = get_flags(state); ++ if (err < 0) ++ return err; ++ ++ if (REG_SUPP(state->regs->RESET_REG)) { ++ err = setPhy(state, state->regs->RESET_REG, state->regs->RESET_VAL); ++ if (err < 0) ++ return err; ++ err = getPhy(state, state->regs->RESET_REG); ++ ++ /* data sheet specifies reset period is 2 msec ++ (don't see any mention of the 2ms delay in the IP178C spec, only ++ in IP175C, but it can't hurt.) */ ++ mdelay(2); ++ } ++ return 0; ++} ++ ++/*! get the current register number */ ++static int ip175c_get_tagged(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ ++ err = get_state(state); ++ if (err < 0) ++ return err; ++ ++ if (state->add_tag & (1<<val->port_vlan)) { ++ if (state->remove_tag & (1<<val->port_vlan)) ++ val->value.i = 3; // shouldn't ever happen. ++ else ++ val->value.i = 1; ++ } else { ++ if (state->remove_tag & (1<<val->port_vlan)) ++ val->value.i = 0; ++ else ++ val->value.i = 2; ++ } ++ return 0; ++} ++ ++/*! set a new register address for low level access to registers */ ++static int ip175c_set_tagged(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int err; ++ ++ err = get_state(state); ++ if (err < 0) ++ return err; ++ ++ state->add_tag &= ~(1<<val->port_vlan); ++ state->remove_tag &= ~(1<<val->port_vlan); ++ ++ if (val->value.i == 0) ++ state->remove_tag |= (1<<val->port_vlan); ++ if (val->value.i == 1) ++ state->add_tag |= (1<<val->port_vlan); ++ ++ SET_PORT_BITS(state, state->add_tag, ++ state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT); ++ SET_PORT_BITS(state, state->remove_tag, ++ state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT); ++ ++ return err; ++} ++ ++ ++/* low level /proc procedures */ ++ ++/*! get the current phy address */ ++static int ip175c_get_phy(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ ++ val->value.i = state->proc_mii.p; ++ return 0; ++} ++ ++/*! set a new phy address for low level access to registers */ ++static int ip175c_set_phy(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int new_reg = val->value.i; ++ ++ if (new_reg < 0 || new_reg > 31) ++ state->proc_mii.p = (u16)-1; ++ else ++ state->proc_mii.p = (u16)new_reg; ++ return 0; ++} ++ ++/*! get the current register number */ ++static int ip175c_get_reg(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ ++ val->value.i = state->proc_mii.m; ++ return 0; ++} ++ ++/*! set a new register address for low level access to registers */ ++static int ip175c_set_reg(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int new_reg = val->value.i; ++ ++ if (new_reg < 0 || new_reg > 31) ++ state->proc_mii.m = (u16)-1; ++ else ++ state->proc_mii.m = (u16)new_reg; ++ return 0; ++} ++ ++/*! get the register content of state->proc_mii */ ++static int ip175c_get_val(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int retval = -EINVAL; ++ if (REG_SUPP(state->proc_mii)) ++ retval = getPhy(state, state->proc_mii); ++ ++ if (retval < 0) { ++ state->proc_errno = retval; ++ return retval; ++ } else { ++ state->proc_errno = 0; ++ val->value.i = retval; ++ return 0; ++ } ++} ++ ++/*! write a value to the register defined by phy/reg above */ ++static int ip175c_set_val(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ int myval; ++ ++ myval = val->value.i; ++ if (myval <= 0xffff && myval >= 0 && REG_SUPP(state->proc_mii)) { ++ state->proc_errno = setPhy(state, state->proc_mii, (u16)myval); ++ } else { ++ state->proc_errno = -EINVAL; ++ } ++ return state->proc_errno; ++} ++ ++/*! get the errno of the last read/write of "val" */ ++static int ip175c_get_errno(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ val->value.i = state->proc_errno; ++ return 0; ++} ++ ++static int ip175c_read_name(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ struct ip175c_state *state = dev->priv; ++ val->value.s = state->regs->NAME; // just a const pointer, won't be freed by swconfig. ++ return 0; ++} ++ ++ ++static int ip175c_set_port_speed(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ int nr = val->port_vlan; ++ struct ip175c_state *state = dev->priv; ++ struct phy_device *phy; ++ int ctrl; ++ int autoneg; ++ int speed; ++ if (val->value.i == 100) { ++ speed = 1; ++ autoneg = 0; ++ } else if (val->value.i == 10) { ++ speed = 0; ++ autoneg = 0; ++ } else { ++ autoneg = 1; ++ speed = 1; ++ } ++ ++ if (nr == state->regs->CPU_PORT) { ++ return -EINVAL; // can't set speed for cpu port! ++ } ++ ++ if (nr >= dev->ports || nr < 0) ++ return -EINVAL; ++ phy = state->ports[nr].phy; ++ if (!phy) ++ return -EINVAL; ++ ++ ctrl = phy_read(phy, 0); ++ if (ctrl < 0) ++ return -EIO; ++ ++ ctrl &= (~(1<<12)); ++ ctrl &= (~(1<<13)); ++ ctrl |= (autoneg<<12); ++ ctrl |= (speed<<13); ++ ++ return phy_write(phy, 0, ctrl); ++} ++ ++static int ip175c_get_port_speed(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ int nr = val->port_vlan; ++ struct ip175c_state *state = dev->priv; ++ struct phy_device *phy; ++ int speed, status; ++ ++ if (nr == state->regs->CPU_PORT) { ++ val->value.i = 100; ++ return 0; ++ } ++ ++ if (nr >= dev->ports || nr < 0) ++ return -EINVAL; ++ phy = state->ports[nr].phy; ++ if (!phy) ++ return -EINVAL; ++ ++ status = phy_read(phy, 1); ++ speed = phy_read(phy, 18); ++ if (status < 0 || speed < 0) ++ return -EIO; ++ ++ if (status & 4) ++ val->value.i = ((speed & (1<<11)) ? 100 : 10); ++ else ++ val->value.i = 0; ++ ++ return 0; ++} ++ ++ ++static int ip175c_get_port_status(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) ++{ ++ int nr = val->port_vlan; ++ struct ip175c_state *state = dev->priv; ++ struct phy_device *phy; ++ int ctrl, speed, status; ++ int len; ++ char *buf = state->buf; // fixed-length at 80. ++ ++ if (nr == state->regs->CPU_PORT) { ++ sprintf(buf, "up, 100 Mbps, cpu port"); ++ val->value.s = buf; ++ return 0; ++ } ++ ++ if (nr >= dev->ports || nr < 0) ++ return -EINVAL; ++ phy = state->ports[nr].phy; ++ if (!phy) ++ return -EINVAL; ++ ++ ctrl = phy_read(phy, 0); ++ status = phy_read(phy, 1); ++ speed = phy_read(phy, 18); ++ if (ctrl < 0 || status < 0 || speed < 0) ++ return -EIO; ++ ++ if (status & 4) ++ len = sprintf(buf, "up, %d Mbps, %s duplex", ++ ((speed & (1<<11)) ? 100 : 10), ++ ((speed & (1<<10)) ? "full" : "half")); ++ else ++ len = sprintf(buf, "down"); ++ ++ if (ctrl & (1<<12)) { ++ len += sprintf(buf+len, ", auto-negotiate"); ++ if (!(status & (1<<5))) ++ len += sprintf(buf+len, " (in progress)"); ++ } else { ++ len += sprintf(buf+len, ", fixed speed (%d)", ++ ((ctrl & (1<<13)) ? 100 : 10)); ++ } ++ ++ buf[len] = '\0'; ++ val->value.s = buf; ++ return 0; ++} ++ ++enum Ports { ++ IP175C_PORT_STATUS, ++ IP175C_PORT_LINK, ++ IP175C_PORT_TAGGED, ++}; ++ ++enum Globals { ++ IP175C_RESET, ++ IP175C_ENABLE_VLAN, ++ IP175C_GET_NAME, ++ IP175C_REGISTER_PHY, ++ IP175C_REGISTER_MII, ++ IP175C_REGISTER_VALUE, ++ IP175C_REGISTER_ERRNO, ++}; ++ ++static const struct switch_attr ip175c_global[] = { ++ [IP175C_RESET] = { ++ .id = IP175C_RESET, ++ .type = SWITCH_TYPE_NOVAL, ++ .name = "reset", ++ .get = NULL, ++ .description = "Resets the switch but does not clear vlan configuration", ++ .set = ip175c_reset, ++ }, ++ [IP175C_ENABLE_VLAN] = { ++ .id = IP175C_ENABLE_VLAN, ++ .type = SWITCH_TYPE_INT, ++ .name = "enable_vlan", ++ .description = "Flag to enable or disable VLANs and tagging", ++ .get = ip175c_get_enable_vlan, ++ .set = ip175c_set_enable_vlan, ++ }, ++ [IP175C_GET_NAME] = { ++ .id = IP175C_GET_NAME, ++ .type = SWITCH_TYPE_STRING, ++ .description = "Returns the type of IC+ chip.", ++ .name = "name", ++ .get = ip175c_read_name, ++ .set = NULL, ++ }, ++ /* jal: added for low level debugging etc. */ ++ [IP175C_REGISTER_PHY] = { ++ .id = IP175C_REGISTER_PHY, ++ .type = SWITCH_TYPE_INT, ++ .description = "Direct register access: set phy (0-4, or 29,30,31)", ++ .name = "phy", ++ .get = ip175c_get_phy, ++ .set = ip175c_set_phy, ++ }, ++ [IP175C_REGISTER_MII] = { ++ .id = IP175C_REGISTER_MII, ++ .type = SWITCH_TYPE_INT, ++ .description = "Direct register access: set mii number (0-31)", ++ .name = "reg", ++ .get = ip175c_get_reg, ++ .set = ip175c_set_reg, ++ }, ++ [IP175C_REGISTER_VALUE] = { ++ .id = IP175C_REGISTER_VALUE, ++ .type = SWITCH_TYPE_INT, ++ .description = "Direct register access: read/write to register (0-65535)", ++ .name = "val", ++ .get = ip175c_get_val, ++ .set = ip175c_set_val, ++ }, ++ [IP175C_REGISTER_ERRNO] = { ++ .id = IP175C_REGISTER_ERRNO, ++ .type = SWITCH_TYPE_INT, ++ .description = "Direct register access: returns last read or write error", ++ .name = "errno", ++ .get = ip175c_get_errno, ++ .set = NULL, ++ }, ++ ++}; ++ ++static const struct switch_attr ip175c_vlan[] = { ++}; ++ ++static const struct switch_attr ip175c_port[] = { ++ [IP175C_PORT_STATUS] = { ++ .id = IP175C_PORT_STATUS, ++ .type = SWITCH_TYPE_STRING, ++ .description = "Returns Detailed port status", ++ .name = "status", ++ .get = ip175c_get_port_status, ++ .set = NULL, ++ }, ++ [IP175C_PORT_LINK] = { ++ .id = IP175C_PORT_LINK, ++ .type = SWITCH_TYPE_INT, ++ .description = "Link speed. Can write 0 for auto-negotiate, or 10 or 100", ++ .name = "link", ++ .get = ip175c_get_port_speed, ++ .set = ip175c_set_port_speed, ++ }, ++ [IP175C_PORT_TAGGED] = { ++ .id = IP175C_PORT_LINK, ++ .type = SWITCH_TYPE_INT, ++ .description = "0 = untag, 1 = add tags, 2 = do not alter (This value is reset if vlans are altered)", ++ .name = "tagged", ++ .get = ip175c_get_tagged, ++ .set = ip175c_set_tagged, ++ }, ++}; ++ ++static void ip175c_cleanup(struct ip175c_state *state) ++{ ++ int i; ++ ++ for (i=0; i<MAX_PORTS; i++) ++ if (state->ports[i].phy) ++ put_device(&state->ports[i].phy->dev); ++ list_del(&state->list); ++ kfree(state); ++} ++ ++static struct ip175c_state *lookup_state(struct mii_bus *bus) ++{ ++ struct list_head *pos; ++ struct ip175c_state *state; ++ struct switch_dev *dev; ++ int err; ++ ++ list_for_each(pos, &state_list) { ++ state = list_entry(pos, struct ip175c_state, list); ++ if (state->mii_bus == bus) ++ return state; ++ } ++ ++ dev = kcalloc(1, sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return NULL; ++ dev->name = DRIVER_NAME; ++ ++ // FIXME: passing const pointers gives warning with swconfig ++ dev->attr_global.attr = &ip175c_global; ++ dev->attr_global.n_attr = ARRAY_SIZE(ip175c_global); ++ dev->attr_port.attr = &ip175c_port; ++ dev->attr_port.n_attr = ARRAY_SIZE(ip175c_port); ++ dev->attr_vlan.attr = &ip175c_vlan; ++ dev->attr_vlan.n_attr = ARRAY_SIZE(ip175c_vlan); ++ ++ dev->get_vlan_ports = ip175c_get_ports; ++ dev->set_vlan_ports = ip175c_set_ports; ++ dev->apply_config = ip175c_apply; ++ ++ state = kcalloc(1, sizeof(*state), GFP_KERNEL); ++ if (!state) ++ goto bad2; ++ list_add_tail(&state->list, &state_list); ++ state->mii_bus = bus; ++ state->dev = dev; ++ dev->priv = state; ++ dev->devname = bus->id; ++ ++ if ((err = get_model(state))) ++ goto bad; ++ ++ dev->vlans = MAX_VLANS; ++ dev->cpu_port = state->regs->CPU_PORT; ++ dev->ports = state->regs->NUM_PORTS; ++ ++ printk(KERN_INFO MODULE_NAME ": Found a %s chip at %s, id %s\n", state->regs->NAME, bus->name, bus->id); ++ ++ if (register_switch(dev, NULL) < 0) ++ goto bad; ++ ++ return state; ++ ++bad: ++ ip175c_cleanup(state); ++bad2: ++ kfree(dev); ++ return NULL; ++} ++ ++#define PRINT_FOR_EACH_PHY // slightly increases loading times but could be useful. ++static int __init ip175c_init_dev(struct device *kdev, void *data) ++{ ++ struct ip175c_state *state; ++ struct phy_device *phy; ++ ++ pr_debug("IP175C: Found PHY: bus_id=%s\n", kdev->bus_id); ++ phy = to_phy_device(kdev); ++ if (phy == NULL) ++ return 0; // bad ip175c device. ++ state = lookup_state(phy->bus); ++ if (state) { ++#ifdef PRINT_FOR_EACH_PHY ++ printk(KERN_DEBUG MODULE_NAME ": Found %s switch at bus id %s (PHY %d)\n", state->regs->NAME, kdev->bus_id, phy->addr); ++#endif ++ if (phy->addr < state->regs->NUM_PORTS) { ++ state->ports[phy->addr].phy = phy; ++ get_device(kdev); ++ } ++ } ++ return 0; ++} ++ ++int __init ip175c_init(void) ++{ ++ struct device_driver *drv = driver_find("ICPlus IP175C", &mdio_bus_type); ++ if (!drv) { ++ pr_info("IP175C: No ICPlus driver found.\n"); ++ return -ENOENT; ++ } ++ INIT_LIST_HEAD(&state_list); ++ driver_for_each_device(drv, NULL, NULL, ip175c_init_dev); ++ put_driver(drv); ++ return 0; ++} ++ ++void __exit ip175c_exit(void) ++{ ++ struct list_head *pos; ++ struct list_head *temp; ++ struct ip175c_state *state; ++ ++ list_for_each_safe(pos, temp, &state_list) { ++ state = list_entry(pos, struct ip175c_state, list); ++ unregister_switch(state->dev); ++ state->dev = NULL; ++ ip175c_cleanup(state); // removes from list and frees. ++ } ++} ++ ++MODULE_AUTHOR("Patrick Horn <patrick.horn@gmail.com>"); ++MODULE_LICENSE("GPL"); ++ ++module_init(ip175c_init); ++module_exit(ip175c_exit); ++ |