diff options
Diffstat (limited to 'kernel/arch/mips/ath79')
32 files changed, 3646 insertions, 0 deletions
diff --git a/kernel/arch/mips/ath79/Kconfig b/kernel/arch/mips/ath79/Kconfig new file mode 100644 index 000000000..dfc60209d --- /dev/null +++ b/kernel/arch/mips/ath79/Kconfig @@ -0,0 +1,120 @@ +if ATH79 + +menu "Atheros AR71XX/AR724X/AR913X machine selection" + +config ATH79_MACH_AP121 + bool "Atheros AP121 reference board" + select SOC_AR933X + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + select ATH79_DEV_USB + select ATH79_DEV_WMAC + help + Say 'Y' here if you want your kernel to support the + Atheros AP121 reference board. + +config ATH79_MACH_AP136 + bool "Atheros AP136 reference board" + select SOC_QCA955X + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + select ATH79_DEV_USB + select ATH79_DEV_WMAC + help + Say 'Y' here if you want your kernel to support the + Atheros AP136 reference board. + +config ATH79_MACH_AP81 + bool "Atheros AP81 reference board" + select SOC_AR913X + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + select ATH79_DEV_USB + select ATH79_DEV_WMAC + help + Say 'Y' here if you want your kernel to support the + Atheros AP81 reference board. + +config ATH79_MACH_DB120 + bool "Atheros DB120 reference board" + select SOC_AR934X + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + select ATH79_DEV_USB + select ATH79_DEV_WMAC + help + Say 'Y' here if you want your kernel to support the + Atheros DB120 reference board. + +config ATH79_MACH_PB44 + bool "Atheros PB44 reference board" + select SOC_AR71XX + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + select ATH79_DEV_USB + help + Say 'Y' here if you want your kernel to support the + Atheros PB44 reference board. + +config ATH79_MACH_UBNT_XM + bool "Ubiquiti Networks XM (rev 1.0) board" + select SOC_AR724X + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + help + Say 'Y' here if you want your kernel to support the + Ubiquiti Networks XM (rev 1.0) board. + +endmenu + +config SOC_AR71XX + select HW_HAS_PCI + def_bool n + +config SOC_AR724X + select HW_HAS_PCI + select PCI_AR724X if PCI + def_bool n + +config SOC_AR913X + def_bool n + +config SOC_AR933X + def_bool n + +config SOC_AR934X + select HW_HAS_PCI + select PCI_AR724X if PCI + def_bool n + +config SOC_QCA955X + select HW_HAS_PCI + select PCI_AR724X if PCI + def_bool n + +config PCI_AR724X + def_bool n + +config ATH79_DEV_GPIO_BUTTONS + def_bool n + +config ATH79_DEV_LEDS_GPIO + def_bool n + +config ATH79_DEV_SPI + def_bool n + +config ATH79_DEV_USB + def_bool n + +config ATH79_DEV_WMAC + depends on (SOC_AR913X || SOC_AR933X || SOC_AR934X || SOC_QCA955X) + def_bool n + +endif diff --git a/kernel/arch/mips/ath79/Makefile b/kernel/arch/mips/ath79/Makefile new file mode 100644 index 000000000..5c9ff692f --- /dev/null +++ b/kernel/arch/mips/ath79/Makefile @@ -0,0 +1,34 @@ +# +# Makefile for the Atheros AR71XX/AR724X/AR913X specific parts of the kernel +# +# Copyright (C) 2008-2011 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 setup.o irq.o common.o clock.o gpio.o + +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_PCI) += pci.o + +# +# Devices +# +obj-y += dev-common.o +obj-$(CONFIG_ATH79_DEV_GPIO_BUTTONS) += dev-gpio-buttons.o +obj-$(CONFIG_ATH79_DEV_LEDS_GPIO) += dev-leds-gpio.o +obj-$(CONFIG_ATH79_DEV_SPI) += dev-spi.o +obj-$(CONFIG_ATH79_DEV_USB) += dev-usb.o +obj-$(CONFIG_ATH79_DEV_WMAC) += dev-wmac.o + +# +# Machines +# +obj-$(CONFIG_ATH79_MACH_AP121) += mach-ap121.o +obj-$(CONFIG_ATH79_MACH_AP136) += mach-ap136.o +obj-$(CONFIG_ATH79_MACH_AP81) += mach-ap81.o +obj-$(CONFIG_ATH79_MACH_DB120) += mach-db120.o +obj-$(CONFIG_ATH79_MACH_PB44) += mach-pb44.o +obj-$(CONFIG_ATH79_MACH_UBNT_XM) += mach-ubnt-xm.o diff --git a/kernel/arch/mips/ath79/Platform b/kernel/arch/mips/ath79/Platform new file mode 100644 index 000000000..2bd663647 --- /dev/null +++ b/kernel/arch/mips/ath79/Platform @@ -0,0 +1,7 @@ +# +# Atheros AR71xx/AR724x/AR913x +# + +platform-$(CONFIG_ATH79) += ath79/ +cflags-$(CONFIG_ATH79) += -I$(srctree)/arch/mips/include/asm/mach-ath79 +load-$(CONFIG_ATH79) = 0xffffffff80060000 diff --git a/kernel/arch/mips/ath79/clock.c b/kernel/arch/mips/ath79/clock.c new file mode 100644 index 000000000..26479f437 --- /dev/null +++ b/kernel/arch/mips/ath79/clock.c @@ -0,0 +1,490 @@ +/* + * Atheros AR71XX/AR724X/AR913X common routines + * + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 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/err.h> +#include <linux/clk.h> +#include <linux/clkdev.h> + +#include <asm/div64.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" + +#define AR71XX_BASE_FREQ 40000000 +#define AR724X_BASE_FREQ 5000000 +#define AR913X_BASE_FREQ 5000000 + +struct clk { + unsigned long rate; +}; + +static void __init ath79_add_sys_clkdev(const char *id, unsigned long rate) +{ + struct clk *clk; + int err; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + panic("failed to allocate %s clock structure", id); + + clk->rate = rate; + + err = clk_register_clkdev(clk, id, NULL); + if (err) + panic("unable to register %s clock device", id); +} + +static void __init ar71xx_clocks_init(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + u32 pll; + u32 freq; + u32 div; + + ref_rate = AR71XX_BASE_FREQ; + + pll = ath79_pll_rr(AR71XX_PLL_REG_CPU_CONFIG); + + div = ((pll >> AR71XX_PLL_DIV_SHIFT) & AR71XX_PLL_DIV_MASK) + 1; + freq = div * ref_rate; + + div = ((pll >> AR71XX_CPU_DIV_SHIFT) & AR71XX_CPU_DIV_MASK) + 1; + cpu_rate = freq / div; + + div = ((pll >> AR71XX_DDR_DIV_SHIFT) & AR71XX_DDR_DIV_MASK) + 1; + ddr_rate = freq / div; + + div = (((pll >> AR71XX_AHB_DIV_SHIFT) & AR71XX_AHB_DIV_MASK) + 1) * 2; + ahb_rate = cpu_rate / div; + + ath79_add_sys_clkdev("ref", ref_rate); + ath79_add_sys_clkdev("cpu", cpu_rate); + ath79_add_sys_clkdev("ddr", ddr_rate); + ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ahb", NULL); + clk_add_alias("uart", NULL, "ahb", NULL); +} + +static void __init ar724x_clocks_init(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + u32 pll; + u32 freq; + u32 div; + + ref_rate = AR724X_BASE_FREQ; + pll = ath79_pll_rr(AR724X_PLL_REG_CPU_CONFIG); + + div = ((pll >> AR724X_PLL_DIV_SHIFT) & AR724X_PLL_DIV_MASK); + freq = div * ref_rate; + + div = ((pll >> AR724X_PLL_REF_DIV_SHIFT) & AR724X_PLL_REF_DIV_MASK); + freq *= div; + + cpu_rate = freq; + + div = ((pll >> AR724X_DDR_DIV_SHIFT) & AR724X_DDR_DIV_MASK) + 1; + ddr_rate = freq / div; + + div = (((pll >> AR724X_AHB_DIV_SHIFT) & AR724X_AHB_DIV_MASK) + 1) * 2; + ahb_rate = cpu_rate / div; + + ath79_add_sys_clkdev("ref", ref_rate); + ath79_add_sys_clkdev("cpu", cpu_rate); + ath79_add_sys_clkdev("ddr", ddr_rate); + ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ahb", NULL); + clk_add_alias("uart", NULL, "ahb", NULL); +} + +static void __init ar913x_clocks_init(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + u32 pll; + u32 freq; + u32 div; + + ref_rate = AR913X_BASE_FREQ; + pll = ath79_pll_rr(AR913X_PLL_REG_CPU_CONFIG); + + div = ((pll >> AR913X_PLL_DIV_SHIFT) & AR913X_PLL_DIV_MASK); + freq = div * ref_rate; + + cpu_rate = freq; + + div = ((pll >> AR913X_DDR_DIV_SHIFT) & AR913X_DDR_DIV_MASK) + 1; + ddr_rate = freq / div; + + div = (((pll >> AR913X_AHB_DIV_SHIFT) & AR913X_AHB_DIV_MASK) + 1) * 2; + ahb_rate = cpu_rate / div; + + ath79_add_sys_clkdev("ref", ref_rate); + ath79_add_sys_clkdev("cpu", cpu_rate); + ath79_add_sys_clkdev("ddr", ddr_rate); + ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ahb", NULL); + clk_add_alias("uart", NULL, "ahb", NULL); +} + +static void __init ar933x_clocks_init(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + u32 clock_ctrl; + u32 cpu_config; + u32 freq; + u32 t; + + t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP); + if (t & AR933X_BOOTSTRAP_REF_CLK_40) + ref_rate = (40 * 1000 * 1000); + else + ref_rate = (25 * 1000 * 1000); + + clock_ctrl = ath79_pll_rr(AR933X_PLL_CLOCK_CTRL_REG); + if (clock_ctrl & AR933X_PLL_CLOCK_CTRL_BYPASS) { + cpu_rate = ref_rate; + ahb_rate = ref_rate; + ddr_rate = ref_rate; + } else { + cpu_config = ath79_pll_rr(AR933X_PLL_CPU_CONFIG_REG); + + t = (cpu_config >> AR933X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + AR933X_PLL_CPU_CONFIG_REFDIV_MASK; + freq = ref_rate / t; + + t = (cpu_config >> AR933X_PLL_CPU_CONFIG_NINT_SHIFT) & + AR933X_PLL_CPU_CONFIG_NINT_MASK; + freq *= t; + + t = (cpu_config >> AR933X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + AR933X_PLL_CPU_CONFIG_OUTDIV_MASK; + if (t == 0) + t = 1; + + freq >>= t; + + t = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_CPU_DIV_SHIFT) & + AR933X_PLL_CLOCK_CTRL_CPU_DIV_MASK) + 1; + cpu_rate = freq / t; + + t = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_DDR_DIV_SHIFT) & + AR933X_PLL_CLOCK_CTRL_DDR_DIV_MASK) + 1; + ddr_rate = freq / t; + + t = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_AHB_DIV_SHIFT) & + AR933X_PLL_CLOCK_CTRL_AHB_DIV_MASK) + 1; + ahb_rate = freq / t; + } + + ath79_add_sys_clkdev("ref", ref_rate); + ath79_add_sys_clkdev("cpu", cpu_rate); + ath79_add_sys_clkdev("ddr", ddr_rate); + ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ahb", NULL); + clk_add_alias("uart", NULL, "ref", NULL); +} + +static u32 __init ar934x_get_pll_freq(u32 ref, u32 ref_div, u32 nint, u32 nfrac, + u32 frac, u32 out_div) +{ + u64 t; + u32 ret; + + t = ref; + t *= nint; + do_div(t, ref_div); + ret = t; + + t = ref; + t *= nfrac; + do_div(t, ref_div * frac); + ret += t; + + ret /= (1 << out_div); + return ret; +} + +static void __init ar934x_clocks_init(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + u32 pll, out_div, ref_div, nint, nfrac, frac, clk_ctrl, postdiv; + u32 cpu_pll, ddr_pll; + u32 bootstrap; + void __iomem *dpll_base; + + dpll_base = ioremap(AR934X_SRIF_BASE, AR934X_SRIF_SIZE); + + bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); + if (bootstrap & AR934X_BOOTSTRAP_REF_CLK_40) + ref_rate = 40 * 1000 * 1000; + else + ref_rate = 25 * 1000 * 1000; + + pll = __raw_readl(dpll_base + AR934X_SRIF_CPU_DPLL2_REG); + if (pll & AR934X_SRIF_DPLL2_LOCAL_PLL) { + out_div = (pll >> AR934X_SRIF_DPLL2_OUTDIV_SHIFT) & + AR934X_SRIF_DPLL2_OUTDIV_MASK; + pll = __raw_readl(dpll_base + AR934X_SRIF_CPU_DPLL1_REG); + nint = (pll >> AR934X_SRIF_DPLL1_NINT_SHIFT) & + AR934X_SRIF_DPLL1_NINT_MASK; + nfrac = pll & AR934X_SRIF_DPLL1_NFRAC_MASK; + ref_div = (pll >> AR934X_SRIF_DPLL1_REFDIV_SHIFT) & + AR934X_SRIF_DPLL1_REFDIV_MASK; + frac = 1 << 18; + } else { + pll = ath79_pll_rr(AR934X_PLL_CPU_CONFIG_REG); + out_div = (pll >> AR934X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + AR934X_PLL_CPU_CONFIG_OUTDIV_MASK; + ref_div = (pll >> AR934X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + AR934X_PLL_CPU_CONFIG_REFDIV_MASK; + nint = (pll >> AR934X_PLL_CPU_CONFIG_NINT_SHIFT) & + AR934X_PLL_CPU_CONFIG_NINT_MASK; + nfrac = (pll >> AR934X_PLL_CPU_CONFIG_NFRAC_SHIFT) & + AR934X_PLL_CPU_CONFIG_NFRAC_MASK; + frac = 1 << 6; + } + + cpu_pll = ar934x_get_pll_freq(ref_rate, ref_div, nint, + nfrac, frac, out_div); + + pll = __raw_readl(dpll_base + AR934X_SRIF_DDR_DPLL2_REG); + if (pll & AR934X_SRIF_DPLL2_LOCAL_PLL) { + out_div = (pll >> AR934X_SRIF_DPLL2_OUTDIV_SHIFT) & + AR934X_SRIF_DPLL2_OUTDIV_MASK; + pll = __raw_readl(dpll_base + AR934X_SRIF_DDR_DPLL1_REG); + nint = (pll >> AR934X_SRIF_DPLL1_NINT_SHIFT) & + AR934X_SRIF_DPLL1_NINT_MASK; + nfrac = pll & AR934X_SRIF_DPLL1_NFRAC_MASK; + ref_div = (pll >> AR934X_SRIF_DPLL1_REFDIV_SHIFT) & + AR934X_SRIF_DPLL1_REFDIV_MASK; + frac = 1 << 18; + } else { + pll = ath79_pll_rr(AR934X_PLL_DDR_CONFIG_REG); + out_div = (pll >> AR934X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & + AR934X_PLL_DDR_CONFIG_OUTDIV_MASK; + ref_div = (pll >> AR934X_PLL_DDR_CONFIG_REFDIV_SHIFT) & + AR934X_PLL_DDR_CONFIG_REFDIV_MASK; + nint = (pll >> AR934X_PLL_DDR_CONFIG_NINT_SHIFT) & + AR934X_PLL_DDR_CONFIG_NINT_MASK; + nfrac = (pll >> AR934X_PLL_DDR_CONFIG_NFRAC_SHIFT) & + AR934X_PLL_DDR_CONFIG_NFRAC_MASK; + frac = 1 << 10; + } + + ddr_pll = ar934x_get_pll_freq(ref_rate, ref_div, nint, + nfrac, frac, out_div); + + clk_ctrl = ath79_pll_rr(AR934X_PLL_CPU_DDR_CLK_CTRL_REG); + + postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_SHIFT) & + AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_MASK; + + if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_PLL_BYPASS) + cpu_rate = ref_rate; + else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_CPUCLK_FROM_CPUPLL) + cpu_rate = cpu_pll / (postdiv + 1); + else + cpu_rate = ddr_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_SHIFT) & + AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_MASK; + + if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_PLL_BYPASS) + ddr_rate = ref_rate; + else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL) + ddr_rate = ddr_pll / (postdiv + 1); + else + ddr_rate = cpu_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_SHIFT) & + AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_MASK; + + if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_PLL_BYPASS) + ahb_rate = ref_rate; + else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL) + ahb_rate = ddr_pll / (postdiv + 1); + else + ahb_rate = cpu_pll / (postdiv + 1); + + ath79_add_sys_clkdev("ref", ref_rate); + ath79_add_sys_clkdev("cpu", cpu_rate); + ath79_add_sys_clkdev("ddr", ddr_rate); + ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ref", NULL); + clk_add_alias("uart", NULL, "ref", NULL); + + iounmap(dpll_base); +} + +static void __init qca955x_clocks_init(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + u32 pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv; + u32 cpu_pll, ddr_pll; + u32 bootstrap; + + bootstrap = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP); + if (bootstrap & QCA955X_BOOTSTRAP_REF_CLK_40) + ref_rate = 40 * 1000 * 1000; + else + ref_rate = 25 * 1000 * 1000; + + pll = ath79_pll_rr(QCA955X_PLL_CPU_CONFIG_REG); + out_div = (pll >> QCA955X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + QCA955X_PLL_CPU_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA955X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + QCA955X_PLL_CPU_CONFIG_REFDIV_MASK; + nint = (pll >> QCA955X_PLL_CPU_CONFIG_NINT_SHIFT) & + QCA955X_PLL_CPU_CONFIG_NINT_MASK; + frac = (pll >> QCA955X_PLL_CPU_CONFIG_NFRAC_SHIFT) & + QCA955X_PLL_CPU_CONFIG_NFRAC_MASK; + + cpu_pll = nint * ref_rate / ref_div; + cpu_pll += frac * ref_rate / (ref_div * (1 << 6)); + cpu_pll /= (1 << out_div); + + pll = ath79_pll_rr(QCA955X_PLL_DDR_CONFIG_REG); + out_div = (pll >> QCA955X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & + QCA955X_PLL_DDR_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA955X_PLL_DDR_CONFIG_REFDIV_SHIFT) & + QCA955X_PLL_DDR_CONFIG_REFDIV_MASK; + nint = (pll >> QCA955X_PLL_DDR_CONFIG_NINT_SHIFT) & + QCA955X_PLL_DDR_CONFIG_NINT_MASK; + frac = (pll >> QCA955X_PLL_DDR_CONFIG_NFRAC_SHIFT) & + QCA955X_PLL_DDR_CONFIG_NFRAC_MASK; + + ddr_pll = nint * ref_rate / ref_div; + ddr_pll += frac * ref_rate / (ref_div * (1 << 10)); + ddr_pll /= (1 << out_div); + + clk_ctrl = ath79_pll_rr(QCA955X_PLL_CLK_CTRL_REG); + + postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) & + QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_MASK; + + if (clk_ctrl & QCA955X_PLL_CLK_CTRL_CPU_PLL_BYPASS) + cpu_rate = ref_rate; + else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL) + cpu_rate = ddr_pll / (postdiv + 1); + else + cpu_rate = cpu_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) & + QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_MASK; + + if (clk_ctrl & QCA955X_PLL_CLK_CTRL_DDR_PLL_BYPASS) + ddr_rate = ref_rate; + else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL) + ddr_rate = cpu_pll / (postdiv + 1); + else + ddr_rate = ddr_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) & + QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_MASK; + + if (clk_ctrl & QCA955X_PLL_CLK_CTRL_AHB_PLL_BYPASS) + ahb_rate = ref_rate; + else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL) + ahb_rate = ddr_pll / (postdiv + 1); + else + ahb_rate = cpu_pll / (postdiv + 1); + + ath79_add_sys_clkdev("ref", ref_rate); + ath79_add_sys_clkdev("cpu", cpu_rate); + ath79_add_sys_clkdev("ddr", ddr_rate); + ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ref", NULL); + clk_add_alias("uart", NULL, "ref", NULL); +} + +void __init ath79_clocks_init(void) +{ + if (soc_is_ar71xx()) + ar71xx_clocks_init(); + else if (soc_is_ar724x()) + ar724x_clocks_init(); + else if (soc_is_ar913x()) + ar913x_clocks_init(); + else if (soc_is_ar933x()) + ar933x_clocks_init(); + else if (soc_is_ar934x()) + ar934x_clocks_init(); + else if (soc_is_qca955x()) + qca955x_clocks_init(); + else + BUG(); +} + +unsigned long __init +ath79_get_sys_clk_rate(const char *id) +{ + struct clk *clk; + unsigned long rate; + + clk = clk_get(NULL, id); + if (IS_ERR(clk)) + panic("unable to get %s clock, err=%d", id, (int) PTR_ERR(clk)); + + rate = clk_get_rate(clk); + clk_put(clk); + + return rate; +} + +/* + * Linux clock API + */ +int clk_enable(struct clk *clk) +{ + return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); diff --git a/kernel/arch/mips/ath79/common.c b/kernel/arch/mips/ath79/common.c new file mode 100644 index 000000000..eb3966cd8 --- /dev/null +++ b/kernel/arch/mips/ath79/common.c @@ -0,0 +1,113 @@ +/* + * Atheros AR71XX/AR724X/AR913X common routines + * + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 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/types.h> +#include <linux/spinlock.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" + +static DEFINE_SPINLOCK(ath79_device_reset_lock); + +u32 ath79_cpu_freq; +EXPORT_SYMBOL_GPL(ath79_cpu_freq); + +u32 ath79_ahb_freq; +EXPORT_SYMBOL_GPL(ath79_ahb_freq); + +u32 ath79_ddr_freq; +EXPORT_SYMBOL_GPL(ath79_ddr_freq); + +enum ath79_soc_type ath79_soc; +unsigned int ath79_soc_rev; + +void __iomem *ath79_pll_base; +void __iomem *ath79_reset_base; +EXPORT_SYMBOL_GPL(ath79_reset_base); +void __iomem *ath79_ddr_base; + +void ath79_ddr_wb_flush(u32 reg) +{ + void __iomem *flush_reg = ath79_ddr_base + reg; + + /* Flush the DDR write buffer. */ + __raw_writel(0x1, flush_reg); + while (__raw_readl(flush_reg) & 0x1) + ; + + /* It must be run twice. */ + __raw_writel(0x1, flush_reg); + while (__raw_readl(flush_reg) & 0x1) + ; +} +EXPORT_SYMBOL_GPL(ath79_ddr_wb_flush); + +void ath79_device_reset_set(u32 mask) +{ + unsigned long flags; + u32 reg; + u32 t; + + if (soc_is_ar71xx()) + reg = AR71XX_RESET_REG_RESET_MODULE; + else if (soc_is_ar724x()) + reg = AR724X_RESET_REG_RESET_MODULE; + else if (soc_is_ar913x()) + reg = AR913X_RESET_REG_RESET_MODULE; + else if (soc_is_ar933x()) + reg = AR933X_RESET_REG_RESET_MODULE; + else if (soc_is_ar934x()) + reg = AR934X_RESET_REG_RESET_MODULE; + else if (soc_is_qca955x()) + reg = QCA955X_RESET_REG_RESET_MODULE; + else + BUG(); + + spin_lock_irqsave(&ath79_device_reset_lock, flags); + t = ath79_reset_rr(reg); + ath79_reset_wr(reg, t | mask); + spin_unlock_irqrestore(&ath79_device_reset_lock, flags); +} +EXPORT_SYMBOL_GPL(ath79_device_reset_set); + +void ath79_device_reset_clear(u32 mask) +{ + unsigned long flags; + u32 reg; + u32 t; + + if (soc_is_ar71xx()) + reg = AR71XX_RESET_REG_RESET_MODULE; + else if (soc_is_ar724x()) + reg = AR724X_RESET_REG_RESET_MODULE; + else if (soc_is_ar913x()) + reg = AR913X_RESET_REG_RESET_MODULE; + else if (soc_is_ar933x()) + reg = AR933X_RESET_REG_RESET_MODULE; + else if (soc_is_ar934x()) + reg = AR934X_RESET_REG_RESET_MODULE; + else if (soc_is_qca955x()) + reg = QCA955X_RESET_REG_RESET_MODULE; + else + BUG(); + + spin_lock_irqsave(&ath79_device_reset_lock, flags); + t = ath79_reset_rr(reg); + ath79_reset_wr(reg, t & ~mask); + spin_unlock_irqrestore(&ath79_device_reset_lock, flags); +} +EXPORT_SYMBOL_GPL(ath79_device_reset_clear); diff --git a/kernel/arch/mips/ath79/common.h b/kernel/arch/mips/ath79/common.h new file mode 100644 index 000000000..c39de61f9 --- /dev/null +++ b/kernel/arch/mips/ath79/common.h @@ -0,0 +1,32 @@ +/* + * Atheros AR71XX/AR724X/AR913X common definitions + * + * Copyright (C) 2008-2011 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 __ATH79_COMMON_H +#define __ATH79_COMMON_H + +#include <linux/types.h> + +#define ATH79_MEM_SIZE_MIN (2 * 1024 * 1024) +#define ATH79_MEM_SIZE_MAX (256 * 1024 * 1024) + +void ath79_clocks_init(void); +unsigned long ath79_get_sys_clk_rate(const char *id); + +void ath79_ddr_wb_flush(unsigned int reg); + +void ath79_gpio_function_enable(u32 mask); +void ath79_gpio_function_disable(u32 mask); +void ath79_gpio_function_setup(u32 set, u32 clear); +void ath79_gpio_init(void); + +#endif /* __ATH79_COMMON_H */ diff --git a/kernel/arch/mips/ath79/dev-common.c b/kernel/arch/mips/ath79/dev-common.c new file mode 100644 index 000000000..516225d20 --- /dev/null +++ b/kernel/arch/mips/ath79/dev-common.c @@ -0,0 +1,108 @@ +/* + * Atheros AR71XX/AR724X/AR913X common devices + * + * Copyright (C) 2008-2011 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/platform_device.h> +#include <linux/serial_8250.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" +#include "dev-common.h" + +static struct resource ath79_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 ath79_uart_data[] = { + { + .mapbase = AR71XX_UART_BASE, + .irq = ATH79_MISC_IRQ(3), + .flags = AR71XX_UART_FLAGS, + .iotype = UPIO_MEM32, + .regshift = 2, + }, { + /* terminating entry */ + } +}; + +static struct platform_device ath79_uart_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .resource = ath79_uart_resources, + .num_resources = ARRAY_SIZE(ath79_uart_resources), + .dev = { + .platform_data = ath79_uart_data + }, +}; + +static struct resource ar933x_uart_resources[] = { + { + .start = AR933X_UART_BASE, + .end = AR933X_UART_BASE + AR71XX_UART_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = ATH79_MISC_IRQ(3), + .end = ATH79_MISC_IRQ(3), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ar933x_uart_device = { + .name = "ar933x-uart", + .id = -1, + .resource = ar933x_uart_resources, + .num_resources = ARRAY_SIZE(ar933x_uart_resources), +}; + +void __init ath79_register_uart(void) +{ + unsigned long uart_clk_rate; + + uart_clk_rate = ath79_get_sys_clk_rate("uart"); + + if (soc_is_ar71xx() || + soc_is_ar724x() || + soc_is_ar913x() || + soc_is_ar934x() || + soc_is_qca955x()) { + ath79_uart_data[0].uartclk = uart_clk_rate; + platform_device_register(&ath79_uart_device); + } else if (soc_is_ar933x()) { + platform_device_register(&ar933x_uart_device); + } else { + BUG(); + } +} + +void __init ath79_register_wdt(void) +{ + struct resource res; + + memset(&res, 0, sizeof(res)); + + res.flags = IORESOURCE_MEM; + res.start = AR71XX_RESET_BASE + AR71XX_RESET_REG_WDOG_CTRL; + res.end = res.start + 0x8 - 1; + + platform_device_register_simple("ath79-wdt", -1, &res, 1); +} diff --git a/kernel/arch/mips/ath79/dev-common.h b/kernel/arch/mips/ath79/dev-common.h new file mode 100644 index 000000000..0f514e1af --- /dev/null +++ b/kernel/arch/mips/ath79/dev-common.h @@ -0,0 +1,18 @@ +/* + * Atheros AR71XX/AR724X/AR913X common devices + * + * Copyright (C) 2008-2010 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 _ATH79_DEV_COMMON_H +#define _ATH79_DEV_COMMON_H + +void ath79_register_uart(void); +void ath79_register_wdt(void); + +#endif /* _ATH79_DEV_COMMON_H */ diff --git a/kernel/arch/mips/ath79/dev-gpio-buttons.c b/kernel/arch/mips/ath79/dev-gpio-buttons.c new file mode 100644 index 000000000..366b35fb1 --- /dev/null +++ b/kernel/arch/mips/ath79/dev-gpio-buttons.c @@ -0,0 +1,56 @@ +/* + * Atheros AR71XX/AR724X/AR913X GPIO button support + * + * Copyright (C) 2008-2010 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/slab.h" +#include <linux/platform_device.h> + +#include "dev-gpio-buttons.h" + +void __init ath79_register_gpio_keys_polled(int id, + unsigned poll_interval, + unsigned nbuttons, + struct gpio_keys_button *buttons) +{ + struct platform_device *pdev; + struct gpio_keys_platform_data pdata; + struct gpio_keys_button *p; + int err; + + p = kmemdup(buttons, nbuttons * sizeof(*p), GFP_KERNEL); + if (!p) + return; + + pdev = platform_device_alloc("gpio-keys-polled", 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); +} diff --git a/kernel/arch/mips/ath79/dev-gpio-buttons.h b/kernel/arch/mips/ath79/dev-gpio-buttons.h new file mode 100644 index 000000000..481847ac1 --- /dev/null +++ b/kernel/arch/mips/ath79/dev-gpio-buttons.h @@ -0,0 +1,23 @@ +/* + * Atheros AR71XX/AR724X/AR913X GPIO button support + * + * Copyright (C) 2008-2010 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 _ATH79_DEV_GPIO_BUTTONS_H +#define _ATH79_DEV_GPIO_BUTTONS_H + +#include <linux/input.h> +#include <linux/gpio_keys.h> + +void ath79_register_gpio_keys_polled(int id, + unsigned poll_interval, + unsigned nbuttons, + struct gpio_keys_button *buttons); + +#endif /* _ATH79_DEV_GPIO_BUTTONS_H */ diff --git a/kernel/arch/mips/ath79/dev-leds-gpio.c b/kernel/arch/mips/ath79/dev-leds-gpio.c new file mode 100644 index 000000000..dcb1debce --- /dev/null +++ b/kernel/arch/mips/ath79/dev-leds-gpio.c @@ -0,0 +1,54 @@ +/* + * Atheros AR71XX/AR724X/AR913X common GPIO LEDs support + * + * Copyright (C) 2008-2010 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/slab.h> +#include <linux/platform_device.h> + +#include "dev-leds-gpio.h" + +void __init ath79_register_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 = kmemdup(leds, num_leds * sizeof(*p), GFP_KERNEL); + if (!p) + return; + + 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); +} diff --git a/kernel/arch/mips/ath79/dev-leds-gpio.h b/kernel/arch/mips/ath79/dev-leds-gpio.h new file mode 100644 index 000000000..6e5d8851e --- /dev/null +++ b/kernel/arch/mips/ath79/dev-leds-gpio.h @@ -0,0 +1,21 @@ +/* + * Atheros AR71XX/AR724X/AR913X common GPIO LEDs support + * + * Copyright (C) 2008-2010 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 _ATH79_DEV_LEDS_GPIO_H +#define _ATH79_DEV_LEDS_GPIO_H + +#include <linux/leds.h> + +void ath79_register_leds_gpio(int id, + unsigned num_leds, + struct gpio_led *leds); + +#endif /* _ATH79_DEV_LEDS_GPIO_H */ diff --git a/kernel/arch/mips/ath79/dev-spi.c b/kernel/arch/mips/ath79/dev-spi.c new file mode 100644 index 000000000..aa30163ef --- /dev/null +++ b/kernel/arch/mips/ath79/dev-spi.c @@ -0,0 +1,38 @@ +/* + * Atheros AR71XX/AR724X/AR913X SPI controller device + * + * Copyright (C) 2008-2010 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 <asm/mach-ath79/ar71xx_regs.h> +#include "dev-spi.h" + +static struct resource ath79_spi_resources[] = { + { + .start = AR71XX_SPI_BASE, + .end = AR71XX_SPI_BASE + AR71XX_SPI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ath79_spi_device = { + .name = "ath79-spi", + .id = -1, + .resource = ath79_spi_resources, + .num_resources = ARRAY_SIZE(ath79_spi_resources), +}; + +void __init ath79_register_spi(struct ath79_spi_platform_data *pdata, + struct spi_board_info const *info, + unsigned n) +{ + spi_register_board_info(info, n); + ath79_spi_device.dev.platform_data = pdata; + platform_device_register(&ath79_spi_device); +} diff --git a/kernel/arch/mips/ath79/dev-spi.h b/kernel/arch/mips/ath79/dev-spi.h new file mode 100644 index 000000000..d732565ca --- /dev/null +++ b/kernel/arch/mips/ath79/dev-spi.h @@ -0,0 +1,22 @@ +/* + * Atheros AR71XX/AR724X/AR913X SPI controller device + * + * Copyright (C) 2008-2010 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 _ATH79_DEV_SPI_H +#define _ATH79_DEV_SPI_H + +#include <linux/spi/spi.h> +#include <asm/mach-ath79/ath79_spi_platform.h> + +void ath79_register_spi(struct ath79_spi_platform_data *pdata, + struct spi_board_info const *info, + unsigned n); + +#endif /* _ATH79_DEV_SPI_H */ diff --git a/kernel/arch/mips/ath79/dev-usb.c b/kernel/arch/mips/ath79/dev-usb.c new file mode 100644 index 000000000..8227265bc --- /dev/null +++ b/kernel/arch/mips/ath79/dev-usb.c @@ -0,0 +1,242 @@ +/* + * Atheros AR7XXX/AR9XXX USB Host Controller device + * + * Copyright (C) 2008-2011 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/irq.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/usb/ehci_pdriver.h> +#include <linux/usb/ohci_pdriver.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" +#include "dev-usb.h" + +static u64 ath79_usb_dmamask = DMA_BIT_MASK(32); + +static struct usb_ohci_pdata ath79_ohci_pdata = { +}; + +static struct usb_ehci_pdata ath79_ehci_pdata_v1 = { + .has_synopsys_hc_bug = 1, +}; + +static struct usb_ehci_pdata ath79_ehci_pdata_v2 = { + .caps_offset = 0x100, + .has_tt = 1, +}; + +static void __init ath79_usb_register(const char *name, int id, + unsigned long base, unsigned long size, + int irq, const void *data, + size_t data_size) +{ + struct resource res[2]; + struct platform_device *pdev; + + memset(res, 0, sizeof(res)); + + res[0].flags = IORESOURCE_MEM; + res[0].start = base; + res[0].end = base + size - 1; + + res[1].flags = IORESOURCE_IRQ; + res[1].start = irq; + res[1].end = irq; + + pdev = platform_device_register_resndata(NULL, name, id, + res, ARRAY_SIZE(res), + data, data_size); + + if (IS_ERR(pdev)) { + pr_err("ath79: unable to register USB at %08lx, err=%d\n", + base, (int) PTR_ERR(pdev)); + return; + } + + pdev->dev.dma_mask = &ath79_usb_dmamask; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); +} + +#define AR71XX_USB_RESET_MASK (AR71XX_RESET_USB_HOST | \ + AR71XX_RESET_USB_PHY | \ + AR71XX_RESET_USB_OHCI_DLL) + +static void __init ath79_usb_setup(void) +{ + void __iomem *usb_ctrl_base; + + ath79_device_reset_set(AR71XX_USB_RESET_MASK); + mdelay(1000); + ath79_device_reset_clear(AR71XX_USB_RESET_MASK); + + usb_ctrl_base = ioremap(AR71XX_USB_CTRL_BASE, AR71XX_USB_CTRL_SIZE); + + /* Turning on the Buff and Desc swap bits */ + __raw_writel(0xf0000, usb_ctrl_base + AR71XX_USB_CTRL_REG_CONFIG); + + /* WAR for HW bug. Here it adjusts the duration between two SOFS */ + __raw_writel(0x20c00, usb_ctrl_base + AR71XX_USB_CTRL_REG_FLADJ); + + iounmap(usb_ctrl_base); + + mdelay(900); + + ath79_usb_register("ohci-platform", -1, + AR71XX_OHCI_BASE, AR71XX_OHCI_SIZE, + ATH79_MISC_IRQ(6), + &ath79_ohci_pdata, sizeof(ath79_ohci_pdata)); + + ath79_usb_register("ehci-platform", -1, + AR71XX_EHCI_BASE, AR71XX_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v1, sizeof(ath79_ehci_pdata_v1)); +} + +static void __init ar7240_usb_setup(void) +{ + void __iomem *usb_ctrl_base; + + ath79_device_reset_clear(AR7240_RESET_OHCI_DLL); + ath79_device_reset_set(AR7240_RESET_USB_HOST); + + mdelay(1000); + + ath79_device_reset_set(AR7240_RESET_OHCI_DLL); + ath79_device_reset_clear(AR7240_RESET_USB_HOST); + + usb_ctrl_base = ioremap(AR7240_USB_CTRL_BASE, AR7240_USB_CTRL_SIZE); + + /* WAR for HW bug. Here it adjusts the duration between two SOFS */ + __raw_writel(0x3, usb_ctrl_base + AR71XX_USB_CTRL_REG_FLADJ); + + iounmap(usb_ctrl_base); + + ath79_usb_register("ohci-platform", -1, + AR7240_OHCI_BASE, AR7240_OHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ohci_pdata, sizeof(ath79_ohci_pdata)); +} + +static void __init ar724x_usb_setup(void) +{ + ath79_device_reset_set(AR724X_RESET_USBSUS_OVERRIDE); + mdelay(10); + + ath79_device_reset_clear(AR724X_RESET_USB_HOST); + mdelay(10); + + ath79_device_reset_clear(AR724X_RESET_USB_PHY); + mdelay(10); + + ath79_usb_register("ehci-platform", -1, + AR724X_EHCI_BASE, AR724X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); +} + +static void __init ar913x_usb_setup(void) +{ + ath79_device_reset_set(AR913X_RESET_USBSUS_OVERRIDE); + mdelay(10); + + ath79_device_reset_clear(AR913X_RESET_USB_HOST); + mdelay(10); + + ath79_device_reset_clear(AR913X_RESET_USB_PHY); + mdelay(10); + + ath79_usb_register("ehci-platform", -1, + AR913X_EHCI_BASE, AR913X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); +} + +static void __init ar933x_usb_setup(void) +{ + ath79_device_reset_set(AR933X_RESET_USBSUS_OVERRIDE); + mdelay(10); + + ath79_device_reset_clear(AR933X_RESET_USB_HOST); + mdelay(10); + + ath79_device_reset_clear(AR933X_RESET_USB_PHY); + mdelay(10); + + ath79_usb_register("ehci-platform", -1, + AR933X_EHCI_BASE, AR933X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); +} + +static void __init ar934x_usb_setup(void) +{ + u32 bootstrap; + + bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); + if (bootstrap & AR934X_BOOTSTRAP_USB_MODE_DEVICE) + return; + + ath79_device_reset_set(AR934X_RESET_USBSUS_OVERRIDE); + udelay(1000); + + ath79_device_reset_clear(AR934X_RESET_USB_PHY); + udelay(1000); + + ath79_device_reset_clear(AR934X_RESET_USB_PHY_ANALOG); + udelay(1000); + + ath79_device_reset_clear(AR934X_RESET_USB_HOST); + udelay(1000); + + ath79_usb_register("ehci-platform", -1, + AR934X_EHCI_BASE, AR934X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); +} + +static void __init qca955x_usb_setup(void) +{ + ath79_usb_register("ehci-platform", 0, + QCA955X_EHCI0_BASE, QCA955X_EHCI_SIZE, + ATH79_IP3_IRQ(0), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); + + ath79_usb_register("ehci-platform", 1, + QCA955X_EHCI1_BASE, QCA955X_EHCI_SIZE, + ATH79_IP3_IRQ(1), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); +} + +void __init ath79_register_usb(void) +{ + if (soc_is_ar71xx()) + ath79_usb_setup(); + else if (soc_is_ar7240()) + ar7240_usb_setup(); + else if (soc_is_ar7241() || soc_is_ar7242()) + ar724x_usb_setup(); + else if (soc_is_ar913x()) + ar913x_usb_setup(); + else if (soc_is_ar933x()) + ar933x_usb_setup(); + else if (soc_is_ar934x()) + ar934x_usb_setup(); + else if (soc_is_qca955x()) + qca955x_usb_setup(); + else + BUG(); +} diff --git a/kernel/arch/mips/ath79/dev-usb.h b/kernel/arch/mips/ath79/dev-usb.h new file mode 100644 index 000000000..4b86a69ca --- /dev/null +++ b/kernel/arch/mips/ath79/dev-usb.h @@ -0,0 +1,17 @@ +/* + * Atheros AR71XX/AR724X/AR913X USB Host Controller support + * + * Copyright (C) 2008-2010 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 _ATH79_DEV_USB_H +#define _ATH79_DEV_USB_H + +void ath79_register_usb(void); + +#endif /* _ATH79_DEV_USB_H */ diff --git a/kernel/arch/mips/ath79/dev-wmac.c b/kernel/arch/mips/ath79/dev-wmac.c new file mode 100644 index 000000000..da190b1b8 --- /dev/null +++ b/kernel/arch/mips/ath79/dev-wmac.c @@ -0,0 +1,155 @@ +/* + * Atheros AR913X/AR933X SoC built-in WMAC device support + * + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros 2.6.15/2.6.31 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/init.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/ath9k_platform.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "dev-wmac.h" + +static struct ath9k_platform_data ath79_wmac_data; + +static struct resource ath79_wmac_resources[] = { + { + /* .start and .end fields are filled dynamically */ + .flags = IORESOURCE_MEM, + }, { + /* .start and .end fields are filled dynamically */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ath79_wmac_device = { + .name = "ath9k", + .id = -1, + .resource = ath79_wmac_resources, + .num_resources = ARRAY_SIZE(ath79_wmac_resources), + .dev = { + .platform_data = &ath79_wmac_data, + }, +}; + +static void __init ar913x_wmac_setup(void) +{ + /* reset the WMAC */ + ath79_device_reset_set(AR913X_RESET_AMBA2WMAC); + mdelay(10); + + ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC); + mdelay(10); + + ath79_wmac_resources[0].start = AR913X_WMAC_BASE; + ath79_wmac_resources[0].end = AR913X_WMAC_BASE + AR913X_WMAC_SIZE - 1; + ath79_wmac_resources[1].start = ATH79_CPU_IRQ(2); + ath79_wmac_resources[1].end = ATH79_CPU_IRQ(2); +} + + +static int ar933x_wmac_reset(void) +{ + ath79_device_reset_set(AR933X_RESET_WMAC); + ath79_device_reset_clear(AR933X_RESET_WMAC); + + return 0; +} + +static int ar933x_r1_get_wmac_revision(void) +{ + return ath79_soc_rev; +} + +static void __init ar933x_wmac_setup(void) +{ + u32 t; + + ar933x_wmac_reset(); + + ath79_wmac_device.name = "ar933x_wmac"; + + ath79_wmac_resources[0].start = AR933X_WMAC_BASE; + ath79_wmac_resources[0].end = AR933X_WMAC_BASE + AR933X_WMAC_SIZE - 1; + ath79_wmac_resources[1].start = ATH79_CPU_IRQ(2); + ath79_wmac_resources[1].end = ATH79_CPU_IRQ(2); + + t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP); + if (t & AR933X_BOOTSTRAP_REF_CLK_40) + ath79_wmac_data.is_clk_25mhz = false; + else + ath79_wmac_data.is_clk_25mhz = true; + + if (ath79_soc_rev == 1) + ath79_wmac_data.get_mac_revision = ar933x_r1_get_wmac_revision; + + ath79_wmac_data.external_reset = ar933x_wmac_reset; +} + +static void ar934x_wmac_setup(void) +{ + u32 t; + + ath79_wmac_device.name = "ar934x_wmac"; + + ath79_wmac_resources[0].start = AR934X_WMAC_BASE; + ath79_wmac_resources[0].end = AR934X_WMAC_BASE + AR934X_WMAC_SIZE - 1; + ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1); + ath79_wmac_resources[1].end = ATH79_IP2_IRQ(1); + + t = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); + if (t & AR934X_BOOTSTRAP_REF_CLK_40) + ath79_wmac_data.is_clk_25mhz = false; + else + ath79_wmac_data.is_clk_25mhz = true; +} + +static void qca955x_wmac_setup(void) +{ + u32 t; + + ath79_wmac_device.name = "qca955x_wmac"; + + ath79_wmac_resources[0].start = QCA955X_WMAC_BASE; + ath79_wmac_resources[0].end = QCA955X_WMAC_BASE + QCA955X_WMAC_SIZE - 1; + ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1); + ath79_wmac_resources[1].end = ATH79_IP2_IRQ(1); + + t = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP); + if (t & QCA955X_BOOTSTRAP_REF_CLK_40) + ath79_wmac_data.is_clk_25mhz = false; + else + ath79_wmac_data.is_clk_25mhz = true; +} + +void __init ath79_register_wmac(u8 *cal_data) +{ + if (soc_is_ar913x()) + ar913x_wmac_setup(); + else if (soc_is_ar933x()) + ar933x_wmac_setup(); + else if (soc_is_ar934x()) + ar934x_wmac_setup(); + else if (soc_is_qca955x()) + qca955x_wmac_setup(); + else + BUG(); + + if (cal_data) + memcpy(ath79_wmac_data.eeprom_data, cal_data, + sizeof(ath79_wmac_data.eeprom_data)); + + platform_device_register(&ath79_wmac_device); +} diff --git a/kernel/arch/mips/ath79/dev-wmac.h b/kernel/arch/mips/ath79/dev-wmac.h new file mode 100644 index 000000000..c9cd8709f --- /dev/null +++ b/kernel/arch/mips/ath79/dev-wmac.h @@ -0,0 +1,17 @@ +/* + * Atheros AR913X/AR933X SoC built-in WMAC device support + * + * Copyright (C) 2008-2011 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 _ATH79_DEV_WMAC_H +#define _ATH79_DEV_WMAC_H + +void ath79_register_wmac(u8 *cal_data); + +#endif /* _ATH79_DEV_WMAC_H */ diff --git a/kernel/arch/mips/ath79/early_printk.c b/kernel/arch/mips/ath79/early_printk.c new file mode 100644 index 000000000..b955fafc5 --- /dev/null +++ b/kernel/arch/mips/ath79/early_printk.c @@ -0,0 +1,99 @@ +/* + * Atheros AR7XXX/AR9XXX SoC early printk support + * + * Copyright (C) 2008-2011 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/io.h> +#include <linux/errno.h> +#include <linux/serial_reg.h> +#include <asm/addrspace.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include <asm/mach-ath79/ar933x_uart.h> + +static void (*_prom_putchar) (unsigned char); + +static inline void prom_putchar_wait(void __iomem *reg, u32 mask, u32 val) +{ + u32 t; + + do { + t = __raw_readl(reg); + if ((t & mask) == val) + break; + } while (1); +} + +static void prom_putchar_ar71xx(unsigned char ch) +{ + void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE)); + + prom_putchar_wait(base + UART_LSR * 4, UART_LSR_THRE, UART_LSR_THRE); + __raw_writel(ch, base + UART_TX * 4); + prom_putchar_wait(base + UART_LSR * 4, UART_LSR_THRE, UART_LSR_THRE); +} + +static void prom_putchar_ar933x(unsigned char ch) +{ + void __iomem *base = (void __iomem *)(KSEG1ADDR(AR933X_UART_BASE)); + + prom_putchar_wait(base + AR933X_UART_DATA_REG, AR933X_UART_DATA_TX_CSR, + AR933X_UART_DATA_TX_CSR); + __raw_writel(AR933X_UART_DATA_TX_CSR | ch, base + AR933X_UART_DATA_REG); + prom_putchar_wait(base + AR933X_UART_DATA_REG, AR933X_UART_DATA_TX_CSR, + AR933X_UART_DATA_TX_CSR); +} + +static void prom_putchar_dummy(unsigned char ch) +{ + /* nothing to do */ +} + +static void prom_putchar_init(void) +{ + void __iomem *base; + u32 id; + + base = (void __iomem *)(KSEG1ADDR(AR71XX_RESET_BASE)); + id = __raw_readl(base + AR71XX_RESET_REG_REV_ID); + id &= REV_ID_MAJOR_MASK; + + switch (id) { + case REV_ID_MAJOR_AR71XX: + case REV_ID_MAJOR_AR7240: + case REV_ID_MAJOR_AR7241: + case REV_ID_MAJOR_AR7242: + case REV_ID_MAJOR_AR913X: + case REV_ID_MAJOR_AR9341: + case REV_ID_MAJOR_AR9342: + case REV_ID_MAJOR_AR9344: + case REV_ID_MAJOR_QCA9556: + case REV_ID_MAJOR_QCA9558: + _prom_putchar = prom_putchar_ar71xx; + break; + + case REV_ID_MAJOR_AR9330: + case REV_ID_MAJOR_AR9331: + _prom_putchar = prom_putchar_ar933x; + break; + + default: + _prom_putchar = prom_putchar_dummy; + break; + } +} + +void prom_putchar(unsigned char ch) +{ + if (!_prom_putchar) + prom_putchar_init(); + + _prom_putchar(ch); +} diff --git a/kernel/arch/mips/ath79/gpio.c b/kernel/arch/mips/ath79/gpio.c new file mode 100644 index 000000000..8d025b028 --- /dev/null +++ b/kernel/arch/mips/ath79/gpio.c @@ -0,0 +1,244 @@ +/* + * Atheros AR71XX/AR724X/AR913X GPIO API support + * + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 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/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-ath79/ar71xx_regs.h> +#include <asm/mach-ath79/ath79.h> +#include "common.h" + +static void __iomem *ath79_gpio_base; +static unsigned long ath79_gpio_count; +static DEFINE_SPINLOCK(ath79_gpio_lock); + +static void __ath79_gpio_set_value(unsigned gpio, int value) +{ + void __iomem *base = ath79_gpio_base; + + if (value) + __raw_writel(1 << gpio, base + AR71XX_GPIO_REG_SET); + else + __raw_writel(1 << gpio, base + AR71XX_GPIO_REG_CLEAR); +} + +static int __ath79_gpio_get_value(unsigned gpio) +{ + return (__raw_readl(ath79_gpio_base + AR71XX_GPIO_REG_IN) >> gpio) & 1; +} + +static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + return __ath79_gpio_get_value(offset); +} + +static void ath79_gpio_set_value(struct gpio_chip *chip, + unsigned offset, int value) +{ + __ath79_gpio_set_value(offset, value); +} + +static int ath79_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + void __iomem *base = ath79_gpio_base; + unsigned long flags; + + spin_lock_irqsave(&ath79_gpio_lock, flags); + + __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset), + base + AR71XX_GPIO_REG_OE); + + spin_unlock_irqrestore(&ath79_gpio_lock, flags); + + return 0; +} + +static int ath79_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + void __iomem *base = ath79_gpio_base; + unsigned long flags; + + spin_lock_irqsave(&ath79_gpio_lock, flags); + + if (value) + __raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET); + else + __raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR); + + __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset), + base + AR71XX_GPIO_REG_OE); + + spin_unlock_irqrestore(&ath79_gpio_lock, flags); + + return 0; +} + +static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *base = ath79_gpio_base; + unsigned long flags; + + spin_lock_irqsave(&ath79_gpio_lock, flags); + + __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset), + base + AR71XX_GPIO_REG_OE); + + spin_unlock_irqrestore(&ath79_gpio_lock, flags); + + return 0; +} + +static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + void __iomem *base = ath79_gpio_base; + unsigned long flags; + + spin_lock_irqsave(&ath79_gpio_lock, flags); + + if (value) + __raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET); + else + __raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR); + + __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset), + base + AR71XX_GPIO_REG_OE); + + spin_unlock_irqrestore(&ath79_gpio_lock, flags); + + return 0; +} + +static struct gpio_chip ath79_gpio_chip = { + .label = "ath79", + .get = ath79_gpio_get_value, + .set = ath79_gpio_set_value, + .direction_input = ath79_gpio_direction_input, + .direction_output = ath79_gpio_direction_output, + .base = 0, +}; + +static void __iomem *ath79_gpio_get_function_reg(void) +{ + u32 reg = 0; + + if (soc_is_ar71xx() || + soc_is_ar724x() || + soc_is_ar913x() || + soc_is_ar933x()) + reg = AR71XX_GPIO_REG_FUNC; + else if (soc_is_ar934x()) + reg = AR934X_GPIO_REG_FUNC; + else + BUG(); + + return ath79_gpio_base + reg; +} + +void ath79_gpio_function_setup(u32 set, u32 clear) +{ + void __iomem *reg = ath79_gpio_get_function_reg(); + unsigned long flags; + + spin_lock_irqsave(&ath79_gpio_lock, flags); + + __raw_writel((__raw_readl(reg) & ~clear) | set, reg); + /* flush write */ + __raw_readl(reg); + + spin_unlock_irqrestore(&ath79_gpio_lock, flags); +} + +void ath79_gpio_function_enable(u32 mask) +{ + ath79_gpio_function_setup(mask, 0); +} + +void ath79_gpio_function_disable(u32 mask) +{ + ath79_gpio_function_setup(0, mask); +} + +void __init ath79_gpio_init(void) +{ + int err; + + if (soc_is_ar71xx()) + ath79_gpio_count = AR71XX_GPIO_COUNT; + else if (soc_is_ar7240()) + ath79_gpio_count = AR7240_GPIO_COUNT; + else if (soc_is_ar7241() || soc_is_ar7242()) + ath79_gpio_count = AR7241_GPIO_COUNT; + else if (soc_is_ar913x()) + ath79_gpio_count = AR913X_GPIO_COUNT; + else if (soc_is_ar933x()) + ath79_gpio_count = AR933X_GPIO_COUNT; + else if (soc_is_ar934x()) + ath79_gpio_count = AR934X_GPIO_COUNT; + else if (soc_is_qca955x()) + ath79_gpio_count = QCA955X_GPIO_COUNT; + else + BUG(); + + ath79_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE); + ath79_gpio_chip.ngpio = ath79_gpio_count; + if (soc_is_ar934x() || soc_is_qca955x()) { + ath79_gpio_chip.direction_input = ar934x_gpio_direction_input; + ath79_gpio_chip.direction_output = ar934x_gpio_direction_output; + } + + err = gpiochip_add(&ath79_gpio_chip); + if (err) + panic("cannot add AR71xx GPIO chip, error=%d", err); +} + +int gpio_get_value(unsigned gpio) +{ + if (gpio < ath79_gpio_count) + return __ath79_gpio_get_value(gpio); + + return __gpio_get_value(gpio); +} +EXPORT_SYMBOL(gpio_get_value); + +void gpio_set_value(unsigned gpio, int value) +{ + if (gpio < ath79_gpio_count) + __ath79_gpio_set_value(gpio, value); + else + __gpio_set_value(gpio, value); +} +EXPORT_SYMBOL(gpio_set_value); + +int gpio_to_irq(unsigned gpio) +{ + /* FIXME */ + return -EINVAL; +} +EXPORT_SYMBOL(gpio_to_irq); + +int irq_to_gpio(unsigned irq) +{ + /* FIXME */ + return -EINVAL; +} +EXPORT_SYMBOL(irq_to_gpio); diff --git a/kernel/arch/mips/ath79/irq.c b/kernel/arch/mips/ath79/irq.c new file mode 100644 index 000000000..6adae366f --- /dev/null +++ b/kernel/arch/mips/ath79/irq.c @@ -0,0 +1,369 @@ +/* + * Atheros AR71xx/AR724x/AR913x specific interrupt handling + * + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 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-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" + +static void (*ath79_ip2_handler)(void); +static void (*ath79_ip3_handler)(void); + +static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *base = ath79_reset_base; + u32 pending; + + pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); + + if (!pending) { + spurious_interrupt(); + return; + } + + while (pending) { + int bit = __ffs(pending); + + generic_handle_irq(ATH79_MISC_IRQ(bit)); + pending &= ~BIT(bit); + } +} + +static void ar71xx_misc_irq_unmask(struct irq_data *d) +{ + unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; + void __iomem *base = ath79_reset_base; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); + __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); +} + +static void ar71xx_misc_irq_mask(struct irq_data *d) +{ + unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; + void __iomem *base = ath79_reset_base; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); + __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); +} + +static void ar724x_misc_irq_ack(struct irq_data *d) +{ + unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; + void __iomem *base = ath79_reset_base; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); + __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); +} + +static struct irq_chip ath79_misc_irq_chip = { + .name = "MISC", + .irq_unmask = ar71xx_misc_irq_unmask, + .irq_mask = ar71xx_misc_irq_mask, +}; + +static void __init ath79_misc_irq_init(void) +{ + void __iomem *base = ath79_reset_base; + int i; + + __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); + __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); + + if (soc_is_ar71xx() || soc_is_ar913x()) + ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; + else if (soc_is_ar724x() || + soc_is_ar933x() || + soc_is_ar934x() || + soc_is_qca955x()) + ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; + else + BUG(); + + for (i = ATH79_MISC_IRQ_BASE; + i < ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT; i++) { + irq_set_chip_and_handler(i, &ath79_misc_irq_chip, + handle_level_irq); + } + + irq_set_chained_handler(ATH79_CPU_IRQ(6), ath79_misc_irq_handler); +} + +static void ar934x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc) +{ + u32 status; + + disable_irq_nosync(irq); + + status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS); + + if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) { + ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_PCIE); + generic_handle_irq(ATH79_IP2_IRQ(0)); + } else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) { + ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_WMAC); + generic_handle_irq(ATH79_IP2_IRQ(1)); + } else { + spurious_interrupt(); + } + + enable_irq(irq); +} + +static void ar934x_ip2_irq_init(void) +{ + int i; + + for (i = ATH79_IP2_IRQ_BASE; + i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &dummy_irq_chip, + handle_level_irq); + + irq_set_chained_handler(ATH79_CPU_IRQ(2), ar934x_ip2_irq_dispatch); +} + +static void qca955x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc) +{ + u32 status; + + disable_irq_nosync(irq); + + status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS); + status &= QCA955X_EXT_INT_PCIE_RC1_ALL | QCA955X_EXT_INT_WMAC_ALL; + + if (status == 0) { + spurious_interrupt(); + goto enable; + } + + if (status & QCA955X_EXT_INT_PCIE_RC1_ALL) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP2_IRQ(0)); + } + + if (status & QCA955X_EXT_INT_WMAC_ALL) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP2_IRQ(1)); + } + +enable: + enable_irq(irq); +} + +static void qca955x_ip3_irq_dispatch(unsigned int irq, struct irq_desc *desc) +{ + u32 status; + + disable_irq_nosync(irq); + + status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS); + status &= QCA955X_EXT_INT_PCIE_RC2_ALL | + QCA955X_EXT_INT_USB1 | + QCA955X_EXT_INT_USB2; + + if (status == 0) { + spurious_interrupt(); + goto enable; + } + + if (status & QCA955X_EXT_INT_USB1) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP3_IRQ(0)); + } + + if (status & QCA955X_EXT_INT_USB2) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP3_IRQ(1)); + } + + if (status & QCA955X_EXT_INT_PCIE_RC2_ALL) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP3_IRQ(2)); + } + +enable: + enable_irq(irq); +} + +static void qca955x_irq_init(void) +{ + int i; + + for (i = ATH79_IP2_IRQ_BASE; + i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &dummy_irq_chip, + handle_level_irq); + + irq_set_chained_handler(ATH79_CPU_IRQ(2), qca955x_ip2_irq_dispatch); + + for (i = ATH79_IP3_IRQ_BASE; + i < ATH79_IP3_IRQ_BASE + ATH79_IP3_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &dummy_irq_chip, + handle_level_irq); + + irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch); +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending; + + pending = read_c0_status() & read_c0_cause() & ST0_IM; + + if (pending & STATUSF_IP7) + do_IRQ(ATH79_CPU_IRQ(7)); + + else if (pending & STATUSF_IP2) + ath79_ip2_handler(); + + else if (pending & STATUSF_IP4) + do_IRQ(ATH79_CPU_IRQ(4)); + + else if (pending & STATUSF_IP5) + do_IRQ(ATH79_CPU_IRQ(5)); + + else if (pending & STATUSF_IP3) + ath79_ip3_handler(); + + else if (pending & STATUSF_IP6) + do_IRQ(ATH79_CPU_IRQ(6)); + + else + spurious_interrupt(); +} + +/* + * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for + * these devices typically allocate coherent DMA memory, however the + * DMA controller may still have some unsynchronized data in the FIFO. + * Issue a flush in the handlers to ensure that the driver sees + * the update. + */ + +static void ath79_default_ip2_handler(void) +{ + do_IRQ(ATH79_CPU_IRQ(2)); +} + +static void ath79_default_ip3_handler(void) +{ + do_IRQ(ATH79_CPU_IRQ(3)); +} + +static void ar71xx_ip2_handler(void) +{ + ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_PCI); + do_IRQ(ATH79_CPU_IRQ(2)); +} + +static void ar724x_ip2_handler(void) +{ + ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_PCIE); + do_IRQ(ATH79_CPU_IRQ(2)); +} + +static void ar913x_ip2_handler(void) +{ + ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_WMAC); + do_IRQ(ATH79_CPU_IRQ(2)); +} + +static void ar933x_ip2_handler(void) +{ + ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_WMAC); + do_IRQ(ATH79_CPU_IRQ(2)); +} + +static void ar71xx_ip3_handler(void) +{ + ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_USB); + do_IRQ(ATH79_CPU_IRQ(3)); +} + +static void ar724x_ip3_handler(void) +{ + ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_USB); + do_IRQ(ATH79_CPU_IRQ(3)); +} + +static void ar913x_ip3_handler(void) +{ + ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_USB); + do_IRQ(ATH79_CPU_IRQ(3)); +} + +static void ar933x_ip3_handler(void) +{ + ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_USB); + do_IRQ(ATH79_CPU_IRQ(3)); +} + +static void ar934x_ip3_handler(void) +{ + ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_USB); + do_IRQ(ATH79_CPU_IRQ(3)); +} + +void __init arch_init_irq(void) +{ + if (soc_is_ar71xx()) { + ath79_ip2_handler = ar71xx_ip2_handler; + ath79_ip3_handler = ar71xx_ip3_handler; + } else if (soc_is_ar724x()) { + ath79_ip2_handler = ar724x_ip2_handler; + ath79_ip3_handler = ar724x_ip3_handler; + } else if (soc_is_ar913x()) { + ath79_ip2_handler = ar913x_ip2_handler; + ath79_ip3_handler = ar913x_ip3_handler; + } else if (soc_is_ar933x()) { + ath79_ip2_handler = ar933x_ip2_handler; + ath79_ip3_handler = ar933x_ip3_handler; + } else if (soc_is_ar934x()) { + ath79_ip2_handler = ath79_default_ip2_handler; + ath79_ip3_handler = ar934x_ip3_handler; + } else if (soc_is_qca955x()) { + ath79_ip2_handler = ath79_default_ip2_handler; + ath79_ip3_handler = ath79_default_ip3_handler; + } else { + BUG(); + } + + mips_cpu_irq_init(); + ath79_misc_irq_init(); + + if (soc_is_ar934x()) + ar934x_ip2_irq_init(); + else if (soc_is_qca955x()) + qca955x_irq_init(); +} diff --git a/kernel/arch/mips/ath79/mach-ap121.c b/kernel/arch/mips/ath79/mach-ap121.c new file mode 100644 index 000000000..1bf73f2a0 --- /dev/null +++ b/kernel/arch/mips/ath79/mach-ap121.c @@ -0,0 +1,92 @@ +/* + * Atheros AP121 board support + * + * Copyright (C) 2011 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 "machtypes.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "dev-usb.h" +#include "dev-wmac.h" + +#define AP121_GPIO_LED_WLAN 0 +#define AP121_GPIO_LED_USB 1 + +#define AP121_GPIO_BTN_JUMPSTART 11 +#define AP121_GPIO_BTN_RESET 12 + +#define AP121_KEYS_POLL_INTERVAL 20 /* msecs */ +#define AP121_KEYS_DEBOUNCE_INTERVAL (3 * AP121_KEYS_POLL_INTERVAL) + +#define AP121_CAL_DATA_ADDR 0x1fff1000 + +static struct gpio_led ap121_leds_gpio[] __initdata = { + { + .name = "ap121:green:usb", + .gpio = AP121_GPIO_LED_USB, + .active_low = 0, + }, + { + .name = "ap121:green:wlan", + .gpio = AP121_GPIO_LED_WLAN, + .active_low = 0, + }, +}; + +static struct gpio_keys_button ap121_gpio_keys[] __initdata = { + { + .desc = "jumpstart button", + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = AP121_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP121_GPIO_BTN_JUMPSTART, + .active_low = 1, + }, + { + .desc = "reset button", + .type = EV_KEY, + .code = KEY_RESTART, + .debounce_interval = AP121_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP121_GPIO_BTN_RESET, + .active_low = 1, + } +}; + +static struct spi_board_info ap121_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "mx25l1606e", + } +}; + +static struct ath79_spi_platform_data ap121_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +static void __init ap121_setup(void) +{ + u8 *cal_data = (u8 *) KSEG1ADDR(AP121_CAL_DATA_ADDR); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(ap121_leds_gpio), + ap121_leds_gpio); + ath79_register_gpio_keys_polled(-1, AP121_KEYS_POLL_INTERVAL, + ARRAY_SIZE(ap121_gpio_keys), + ap121_gpio_keys); + + ath79_register_spi(&ap121_spi_data, ap121_spi_info, + ARRAY_SIZE(ap121_spi_info)); + ath79_register_usb(); + ath79_register_wmac(cal_data); +} + +MIPS_MACHINE(ATH79_MACH_AP121, "AP121", "Atheros AP121 reference board", + ap121_setup); diff --git a/kernel/arch/mips/ath79/mach-ap136.c b/kernel/arch/mips/ath79/mach-ap136.c new file mode 100644 index 000000000..07eac58c3 --- /dev/null +++ b/kernel/arch/mips/ath79/mach-ap136.c @@ -0,0 +1,156 @@ +/* + * Qualcomm Atheros AP136 reference board support + * + * Copyright (c) 2012 Qualcomm Atheros + * Copyright (c) 2012-2013 Gabor Juhos <juhosg@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <linux/pci.h> +#include <linux/ath9k_platform.h> + +#include "machtypes.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "dev-usb.h" +#include "dev-wmac.h" +#include "pci.h" + +#define AP136_GPIO_LED_STATUS_RED 14 +#define AP136_GPIO_LED_STATUS_GREEN 19 +#define AP136_GPIO_LED_USB 4 +#define AP136_GPIO_LED_WLAN_2G 13 +#define AP136_GPIO_LED_WLAN_5G 12 +#define AP136_GPIO_LED_WPS_RED 15 +#define AP136_GPIO_LED_WPS_GREEN 20 + +#define AP136_GPIO_BTN_WPS 16 +#define AP136_GPIO_BTN_RFKILL 21 + +#define AP136_KEYS_POLL_INTERVAL 20 /* msecs */ +#define AP136_KEYS_DEBOUNCE_INTERVAL (3 * AP136_KEYS_POLL_INTERVAL) + +#define AP136_WMAC_CALDATA_OFFSET 0x1000 +#define AP136_PCIE_CALDATA_OFFSET 0x5000 + +static struct gpio_led ap136_leds_gpio[] __initdata = { + { + .name = "qca:green:status", + .gpio = AP136_GPIO_LED_STATUS_GREEN, + .active_low = 1, + }, + { + .name = "qca:red:status", + .gpio = AP136_GPIO_LED_STATUS_RED, + .active_low = 1, + }, + { + .name = "qca:green:wps", + .gpio = AP136_GPIO_LED_WPS_GREEN, + .active_low = 1, + }, + { + .name = "qca:red:wps", + .gpio = AP136_GPIO_LED_WPS_RED, + .active_low = 1, + }, + { + .name = "qca:red:wlan-2g", + .gpio = AP136_GPIO_LED_WLAN_2G, + .active_low = 1, + }, + { + .name = "qca:red:usb", + .gpio = AP136_GPIO_LED_USB, + .active_low = 1, + } +}; + +static struct gpio_keys_button ap136_gpio_keys[] __initdata = { + { + .desc = "WPS button", + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = AP136_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP136_GPIO_BTN_WPS, + .active_low = 1, + }, + { + .desc = "RFKILL button", + .type = EV_KEY, + .code = KEY_RFKILL, + .debounce_interval = AP136_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP136_GPIO_BTN_RFKILL, + .active_low = 1, + }, +}; + +static struct spi_board_info ap136_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "mx25l6405d", + } +}; + +static struct ath79_spi_platform_data ap136_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +#ifdef CONFIG_PCI +static struct ath9k_platform_data ap136_ath9k_data; + +static int ap136_pci_plat_dev_init(struct pci_dev *dev) +{ + if (dev->bus->number == 1 && (PCI_SLOT(dev->devfn)) == 0) + dev->dev.platform_data = &ap136_ath9k_data; + + return 0; +} + +static void __init ap136_pci_init(u8 *eeprom) +{ + memcpy(ap136_ath9k_data.eeprom_data, eeprom, + sizeof(ap136_ath9k_data.eeprom_data)); + + ath79_pci_set_plat_dev_init(ap136_pci_plat_dev_init); + ath79_register_pci(); +} +#else +static inline void ap136_pci_init(u8 *eeprom) {} +#endif /* CONFIG_PCI */ + +static void __init ap136_setup(void) +{ + u8 *art = (u8 *) KSEG1ADDR(0x1fff0000); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(ap136_leds_gpio), + ap136_leds_gpio); + ath79_register_gpio_keys_polled(-1, AP136_KEYS_POLL_INTERVAL, + ARRAY_SIZE(ap136_gpio_keys), + ap136_gpio_keys); + ath79_register_spi(&ap136_spi_data, ap136_spi_info, + ARRAY_SIZE(ap136_spi_info)); + ath79_register_usb(); + ath79_register_wmac(art + AP136_WMAC_CALDATA_OFFSET); + ap136_pci_init(art + AP136_PCIE_CALDATA_OFFSET); +} + +MIPS_MACHINE(ATH79_MACH_AP136_010, "AP136-010", + "Atheros AP136-010 reference board", + ap136_setup); diff --git a/kernel/arch/mips/ath79/mach-ap81.c b/kernel/arch/mips/ath79/mach-ap81.c new file mode 100644 index 000000000..1c78d497f --- /dev/null +++ b/kernel/arch/mips/ath79/mach-ap81.c @@ -0,0 +1,100 @@ +/* + * Atheros AP81 board support + * + * Copyright (C) 2009-2010 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 "machtypes.h" +#include "dev-wmac.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "dev-usb.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_KEYS_POLL_INTERVAL 20 /* msecs */ +#define AP81_KEYS_DEBOUNCE_INTERVAL (3 * AP81_KEYS_POLL_INTERVAL) + +#define AP81_CAL_DATA_ADDR 0x1fff1000 + +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_keys_button ap81_gpio_keys[] __initdata = { + { + .desc = "sw1", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = AP81_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP81_GPIO_BTN_SW1, + .active_low = 1, + } , { + .desc = "sw4", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = AP81_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP81_GPIO_BTN_SW4, + .active_low = 1, + } +}; + +static struct spi_board_info ap81_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "m25p64", + } +}; + +static struct ath79_spi_platform_data ap81_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +static void __init ap81_setup(void) +{ + u8 *cal_data = (u8 *) KSEG1ADDR(AP81_CAL_DATA_ADDR); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(ap81_leds_gpio), + ap81_leds_gpio); + ath79_register_gpio_keys_polled(-1, AP81_KEYS_POLL_INTERVAL, + ARRAY_SIZE(ap81_gpio_keys), + ap81_gpio_keys); + ath79_register_spi(&ap81_spi_data, ap81_spi_info, + ARRAY_SIZE(ap81_spi_info)); + ath79_register_wmac(cal_data); + ath79_register_usb(); +} + +MIPS_MACHINE(ATH79_MACH_AP81, "AP81", "Atheros AP81 reference board", + ap81_setup); diff --git a/kernel/arch/mips/ath79/mach-db120.c b/kernel/arch/mips/ath79/mach-db120.c new file mode 100644 index 000000000..9423f5aed --- /dev/null +++ b/kernel/arch/mips/ath79/mach-db120.c @@ -0,0 +1,136 @@ +/* + * Atheros DB120 reference board support + * + * Copyright (c) 2011 Qualcomm Atheros + * Copyright (c) 2011 Gabor Juhos <juhosg@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <linux/pci.h> +#include <linux/ath9k_platform.h> + +#include "machtypes.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "dev-usb.h" +#include "dev-wmac.h" +#include "pci.h" + +#define DB120_GPIO_LED_WLAN_5G 12 +#define DB120_GPIO_LED_WLAN_2G 13 +#define DB120_GPIO_LED_STATUS 14 +#define DB120_GPIO_LED_WPS 15 + +#define DB120_GPIO_BTN_WPS 16 + +#define DB120_KEYS_POLL_INTERVAL 20 /* msecs */ +#define DB120_KEYS_DEBOUNCE_INTERVAL (3 * DB120_KEYS_POLL_INTERVAL) + +#define DB120_WMAC_CALDATA_OFFSET 0x1000 +#define DB120_PCIE_CALDATA_OFFSET 0x5000 + +static struct gpio_led db120_leds_gpio[] __initdata = { + { + .name = "db120:green:status", + .gpio = DB120_GPIO_LED_STATUS, + .active_low = 1, + }, + { + .name = "db120:green:wps", + .gpio = DB120_GPIO_LED_WPS, + .active_low = 1, + }, + { + .name = "db120:green:wlan-5g", + .gpio = DB120_GPIO_LED_WLAN_5G, + .active_low = 1, + }, + { + .name = "db120:green:wlan-2g", + .gpio = DB120_GPIO_LED_WLAN_2G, + .active_low = 1, + }, +}; + +static struct gpio_keys_button db120_gpio_keys[] __initdata = { + { + .desc = "WPS button", + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = DB120_KEYS_DEBOUNCE_INTERVAL, + .gpio = DB120_GPIO_BTN_WPS, + .active_low = 1, + }, +}; + +static struct spi_board_info db120_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "s25sl064a", + } +}; + +static struct ath79_spi_platform_data db120_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +#ifdef CONFIG_PCI +static struct ath9k_platform_data db120_ath9k_data; + +static int db120_pci_plat_dev_init(struct pci_dev *dev) +{ + switch (PCI_SLOT(dev->devfn)) { + case 0: + dev->dev.platform_data = &db120_ath9k_data; + break; + } + + return 0; +} + +static void __init db120_pci_init(u8 *eeprom) +{ + memcpy(db120_ath9k_data.eeprom_data, eeprom, + sizeof(db120_ath9k_data.eeprom_data)); + + ath79_pci_set_plat_dev_init(db120_pci_plat_dev_init); + ath79_register_pci(); +} +#else +static inline void db120_pci_init(u8 *eeprom) {} +#endif /* CONFIG_PCI */ + +static void __init db120_setup(void) +{ + u8 *art = (u8 *) KSEG1ADDR(0x1fff0000); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(db120_leds_gpio), + db120_leds_gpio); + ath79_register_gpio_keys_polled(-1, DB120_KEYS_POLL_INTERVAL, + ARRAY_SIZE(db120_gpio_keys), + db120_gpio_keys); + ath79_register_spi(&db120_spi_data, db120_spi_info, + ARRAY_SIZE(db120_spi_info)); + ath79_register_usb(); + ath79_register_wmac(art + DB120_WMAC_CALDATA_OFFSET); + db120_pci_init(art + DB120_PCIE_CALDATA_OFFSET); +} + +MIPS_MACHINE(ATH79_MACH_DB120, "DB120", "Atheros DB120 reference board", + db120_setup); diff --git a/kernel/arch/mips/ath79/mach-pb44.c b/kernel/arch/mips/ath79/mach-pb44.c new file mode 100644 index 000000000..67b980d94 --- /dev/null +++ b/kernel/arch/mips/ath79/mach-pb44.c @@ -0,0 +1,122 @@ +/* + * Atheros PB44 reference board support + * + * Copyright (C) 2009-2010 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/init.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/i2c-gpio.h> +#include <linux/i2c/pcf857x.h> + +#include "machtypes.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "dev-usb.h" +#include "pci.h" + +#define PB44_GPIO_I2C_SCL 0 +#define PB44_GPIO_I2C_SDA 1 + +#define PB44_GPIO_EXP_BASE 16 +#define PB44_GPIO_SW_RESET (PB44_GPIO_EXP_BASE + 6) +#define PB44_GPIO_SW_JUMP (PB44_GPIO_EXP_BASE + 8) +#define PB44_GPIO_LED_JUMP1 (PB44_GPIO_EXP_BASE + 9) +#define PB44_GPIO_LED_JUMP2 (PB44_GPIO_EXP_BASE + 10) + +#define PB44_KEYS_POLL_INTERVAL 20 /* msecs */ +#define PB44_KEYS_DEBOUNCE_INTERVAL (3 * PB44_KEYS_POLL_INTERVAL) + +static struct i2c_gpio_platform_data pb44_i2c_gpio_data = { + .sda_pin = PB44_GPIO_I2C_SDA, + .scl_pin = PB44_GPIO_I2C_SCL, +}; + +static struct platform_device pb44_i2c_gpio_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { + .platform_data = &pb44_i2c_gpio_data, + } +}; + +static struct pcf857x_platform_data pb44_pcf857x_data = { + .gpio_base = PB44_GPIO_EXP_BASE, +}; + +static struct i2c_board_info pb44_i2c_board_info[] __initdata = { + { + I2C_BOARD_INFO("pcf8575", 0x20), + .platform_data = &pb44_pcf857x_data, + }, +}; + +static struct gpio_led pb44_leds_gpio[] __initdata = { + { + .name = "pb44:amber:jump1", + .gpio = PB44_GPIO_LED_JUMP1, + .active_low = 1, + }, { + .name = "pb44:green:jump2", + .gpio = PB44_GPIO_LED_JUMP2, + .active_low = 1, + }, +}; + +static struct gpio_keys_button pb44_gpio_keys[] __initdata = { + { + .desc = "soft_reset", + .type = EV_KEY, + .code = KEY_RESTART, + .debounce_interval = PB44_KEYS_DEBOUNCE_INTERVAL, + .gpio = PB44_GPIO_SW_RESET, + .active_low = 1, + } , { + .desc = "jumpstart", + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = PB44_KEYS_DEBOUNCE_INTERVAL, + .gpio = PB44_GPIO_SW_JUMP, + .active_low = 1, + } +}; + +static struct spi_board_info pb44_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "m25p64", + }, +}; + +static struct ath79_spi_platform_data pb44_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +static void __init pb44_init(void) +{ + i2c_register_board_info(0, pb44_i2c_board_info, + ARRAY_SIZE(pb44_i2c_board_info)); + platform_device_register(&pb44_i2c_gpio_device); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(pb44_leds_gpio), + pb44_leds_gpio); + ath79_register_gpio_keys_polled(-1, PB44_KEYS_POLL_INTERVAL, + ARRAY_SIZE(pb44_gpio_keys), + pb44_gpio_keys); + ath79_register_spi(&pb44_spi_data, pb44_spi_info, + ARRAY_SIZE(pb44_spi_info)); + ath79_register_usb(); + ath79_register_pci(); +} + +MIPS_MACHINE(ATH79_MACH_PB44, "PB44", "Atheros PB44 reference board", + pb44_init); diff --git a/kernel/arch/mips/ath79/mach-ubnt-xm.c b/kernel/arch/mips/ath79/mach-ubnt-xm.c new file mode 100644 index 000000000..4a3c60694 --- /dev/null +++ b/kernel/arch/mips/ath79/mach-ubnt-xm.c @@ -0,0 +1,126 @@ +/* + * Ubiquiti Networks XM (rev 1.0) board support + * + * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> + * + * Derived from: mach-pb44.c + * + * 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/pci.h> +#include <linux/ath9k_platform.h> + +#include <asm/mach-ath79/irq.h> + +#include "machtypes.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "pci.h" + +#define UBNT_XM_GPIO_LED_L1 0 +#define UBNT_XM_GPIO_LED_L2 1 +#define UBNT_XM_GPIO_LED_L3 11 +#define UBNT_XM_GPIO_LED_L4 7 + +#define UBNT_XM_GPIO_BTN_RESET 12 + +#define UBNT_XM_KEYS_POLL_INTERVAL 20 +#define UBNT_XM_KEYS_DEBOUNCE_INTERVAL (3 * UBNT_XM_KEYS_POLL_INTERVAL) + +#define UBNT_XM_EEPROM_ADDR (u8 *) KSEG1ADDR(0x1fff1000) + +static struct gpio_led ubnt_xm_leds_gpio[] __initdata = { + { + .name = "ubnt-xm:red:link1", + .gpio = UBNT_XM_GPIO_LED_L1, + .active_low = 0, + }, { + .name = "ubnt-xm:orange:link2", + .gpio = UBNT_XM_GPIO_LED_L2, + .active_low = 0, + }, { + .name = "ubnt-xm:green:link3", + .gpio = UBNT_XM_GPIO_LED_L3, + .active_low = 0, + }, { + .name = "ubnt-xm:green:link4", + .gpio = UBNT_XM_GPIO_LED_L4, + .active_low = 0, + }, +}; + +static struct gpio_keys_button ubnt_xm_gpio_keys[] __initdata = { + { + .desc = "reset", + .type = EV_KEY, + .code = KEY_RESTART, + .debounce_interval = UBNT_XM_KEYS_DEBOUNCE_INTERVAL, + .gpio = UBNT_XM_GPIO_BTN_RESET, + .active_low = 1, + } +}; + +static struct spi_board_info ubnt_xm_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "mx25l6405d", + } +}; + +static struct ath79_spi_platform_data ubnt_xm_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +#ifdef CONFIG_PCI +static struct ath9k_platform_data ubnt_xm_eeprom_data; + +static int ubnt_xm_pci_plat_dev_init(struct pci_dev *dev) +{ + switch (PCI_SLOT(dev->devfn)) { + case 0: + dev->dev.platform_data = &ubnt_xm_eeprom_data; + break; + } + + return 0; +} + +static void __init ubnt_xm_pci_init(void) +{ + memcpy(ubnt_xm_eeprom_data.eeprom_data, UBNT_XM_EEPROM_ADDR, + sizeof(ubnt_xm_eeprom_data.eeprom_data)); + + ath79_pci_set_plat_dev_init(ubnt_xm_pci_plat_dev_init); + ath79_register_pci(); +} +#else +static inline void ubnt_xm_pci_init(void) {} +#endif /* CONFIG_PCI */ + +static void __init ubnt_xm_init(void) +{ + ath79_register_leds_gpio(-1, ARRAY_SIZE(ubnt_xm_leds_gpio), + ubnt_xm_leds_gpio); + + ath79_register_gpio_keys_polled(-1, UBNT_XM_KEYS_POLL_INTERVAL, + ARRAY_SIZE(ubnt_xm_gpio_keys), + ubnt_xm_gpio_keys); + + ath79_register_spi(&ubnt_xm_spi_data, ubnt_xm_spi_info, + ARRAY_SIZE(ubnt_xm_spi_info)); + + ubnt_xm_pci_init(); +} + +MIPS_MACHINE(ATH79_MACH_UBNT_XM, + "UBNT-XM", + "Ubiquiti Networks XM (rev 1.0) board", + ubnt_xm_init); diff --git a/kernel/arch/mips/ath79/machtypes.h b/kernel/arch/mips/ath79/machtypes.h new file mode 100644 index 000000000..26254058c --- /dev/null +++ b/kernel/arch/mips/ath79/machtypes.h @@ -0,0 +1,27 @@ +/* + * Atheros AR71XX/AR724X/AR913X machine type definitions + * + * Copyright (C) 2008-2010 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 _ATH79_MACHTYPE_H +#define _ATH79_MACHTYPE_H + +#include <asm/mips_machine.h> + +enum ath79_mach_type { + ATH79_MACH_GENERIC = 0, + ATH79_MACH_AP121, /* Atheros AP121 reference board */ + ATH79_MACH_AP136_010, /* Atheros AP136-010 reference board */ + ATH79_MACH_AP81, /* Atheros AP81 reference board */ + ATH79_MACH_DB120, /* Atheros DB120 reference board */ + ATH79_MACH_PB44, /* Atheros PB44 reference board */ + ATH79_MACH_UBNT_XM, /* Ubiquiti Networks XM board rev 1.0 */ +}; + +#endif /* _ATH79_MACHTYPE_H */ diff --git a/kernel/arch/mips/ath79/pci.c b/kernel/arch/mips/ath79/pci.c new file mode 100644 index 000000000..730c0b030 --- /dev/null +++ b/kernel/arch/mips/ath79/pci.c @@ -0,0 +1,273 @@ +/* + * Atheros AR71XX/AR724X specific PCI setup code + * + * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> + * Copyright (C) 2008-2011 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/init.h> +#include <linux/pci.h> +#include <linux/resource.h> +#include <linux/platform_device.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/irq.h> +#include "pci.h" + +static int (*ath79_pci_plat_dev_init)(struct pci_dev *dev); +static const struct ath79_pci_irq *ath79_pci_irq_map __initdata; +static unsigned ath79_pci_nr_irqs __initdata; + +static const struct ath79_pci_irq ar71xx_pci_irq_map[] __initconst = { + { + .slot = 17, + .pin = 1, + .irq = ATH79_PCI_IRQ(0), + }, { + .slot = 18, + .pin = 1, + .irq = ATH79_PCI_IRQ(1), + }, { + .slot = 19, + .pin = 1, + .irq = ATH79_PCI_IRQ(2), + } +}; + +static const struct ath79_pci_irq ar724x_pci_irq_map[] __initconst = { + { + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(0), + } +}; + +static const struct ath79_pci_irq qca955x_pci_irq_map[] __initconst = { + { + .bus = 0, + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(0), + }, + { + .bus = 1, + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(1), + }, +}; + +int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) +{ + int irq = -1; + int i; + + if (ath79_pci_nr_irqs == 0 || + ath79_pci_irq_map == NULL) { + if (soc_is_ar71xx()) { + ath79_pci_irq_map = ar71xx_pci_irq_map; + ath79_pci_nr_irqs = ARRAY_SIZE(ar71xx_pci_irq_map); + } else if (soc_is_ar724x() || + soc_is_ar9342() || + soc_is_ar9344()) { + ath79_pci_irq_map = ar724x_pci_irq_map; + ath79_pci_nr_irqs = ARRAY_SIZE(ar724x_pci_irq_map); + } else if (soc_is_qca955x()) { + ath79_pci_irq_map = qca955x_pci_irq_map; + ath79_pci_nr_irqs = ARRAY_SIZE(qca955x_pci_irq_map); + } else { + pr_crit("pci %s: invalid irq map\n", + pci_name((struct pci_dev *) dev)); + return irq; + } + } + + for (i = 0; i < ath79_pci_nr_irqs; i++) { + const struct ath79_pci_irq *entry; + + entry = &ath79_pci_irq_map[i]; + if (entry->bus == dev->bus->number && + entry->slot == slot && + entry->pin == pin) { + irq = entry->irq; + break; + } + } + + if (irq < 0) + pr_crit("pci %s: no irq found for pin %u\n", + pci_name((struct pci_dev *) dev), pin); + else + pr_info("pci %s: using irq %d for pin %u\n", + pci_name((struct pci_dev *) dev), irq, pin); + + return irq; +} + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + if (ath79_pci_plat_dev_init) + return ath79_pci_plat_dev_init(dev); + + return 0; +} + +void __init ath79_pci_set_irq_map(unsigned nr_irqs, + const struct ath79_pci_irq *map) +{ + ath79_pci_nr_irqs = nr_irqs; + ath79_pci_irq_map = map; +} + +void __init ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *dev)) +{ + ath79_pci_plat_dev_init = func; +} + +static struct platform_device * +ath79_register_pci_ar71xx(void) +{ + struct platform_device *pdev; + struct resource res[4]; + + memset(res, 0, sizeof(res)); + + res[0].name = "cfg_base"; + res[0].flags = IORESOURCE_MEM; + res[0].start = AR71XX_PCI_CFG_BASE; + res[0].end = AR71XX_PCI_CFG_BASE + AR71XX_PCI_CFG_SIZE - 1; + + res[1].flags = IORESOURCE_IRQ; + res[1].start = ATH79_CPU_IRQ(2); + res[1].end = ATH79_CPU_IRQ(2); + + res[2].name = "io_base"; + res[2].flags = IORESOURCE_IO; + res[2].start = 0; + res[2].end = 0; + + res[3].name = "mem_base"; + res[3].flags = IORESOURCE_MEM; + res[3].start = AR71XX_PCI_MEM_BASE; + res[3].end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1; + + pdev = platform_device_register_simple("ar71xx-pci", -1, + res, ARRAY_SIZE(res)); + return pdev; +} + +static struct platform_device * +ath79_register_pci_ar724x(int id, + unsigned long cfg_base, + unsigned long ctrl_base, + unsigned long crp_base, + unsigned long mem_base, + unsigned long mem_size, + unsigned long io_base, + int irq) +{ + struct platform_device *pdev; + struct resource res[6]; + + memset(res, 0, sizeof(res)); + + res[0].name = "cfg_base"; + res[0].flags = IORESOURCE_MEM; + res[0].start = cfg_base; + res[0].end = cfg_base + AR724X_PCI_CFG_SIZE - 1; + + res[1].name = "ctrl_base"; + res[1].flags = IORESOURCE_MEM; + res[1].start = ctrl_base; + res[1].end = ctrl_base + AR724X_PCI_CTRL_SIZE - 1; + + res[2].flags = IORESOURCE_IRQ; + res[2].start = irq; + res[2].end = irq; + + res[3].name = "mem_base"; + res[3].flags = IORESOURCE_MEM; + res[3].start = mem_base; + res[3].end = mem_base + mem_size - 1; + + res[4].name = "io_base"; + res[4].flags = IORESOURCE_IO; + res[4].start = io_base; + res[4].end = io_base; + + res[5].name = "crp_base"; + res[5].flags = IORESOURCE_MEM; + res[5].start = crp_base; + res[5].end = crp_base + AR724X_PCI_CRP_SIZE - 1; + + pdev = platform_device_register_simple("ar724x-pci", id, + res, ARRAY_SIZE(res)); + return pdev; +} + +int __init ath79_register_pci(void) +{ + struct platform_device *pdev = NULL; + + if (soc_is_ar71xx()) { + pdev = ath79_register_pci_ar71xx(); + } else if (soc_is_ar724x()) { + pdev = ath79_register_pci_ar724x(-1, + AR724X_PCI_CFG_BASE, + AR724X_PCI_CTRL_BASE, + AR724X_PCI_CRP_BASE, + AR724X_PCI_MEM_BASE, + AR724X_PCI_MEM_SIZE, + 0, + ATH79_CPU_IRQ(2)); + } else if (soc_is_ar9342() || + soc_is_ar9344()) { + u32 bootstrap; + + bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); + if ((bootstrap & AR934X_BOOTSTRAP_PCIE_RC) == 0) + return -ENODEV; + + pdev = ath79_register_pci_ar724x(-1, + AR724X_PCI_CFG_BASE, + AR724X_PCI_CTRL_BASE, + AR724X_PCI_CRP_BASE, + AR724X_PCI_MEM_BASE, + AR724X_PCI_MEM_SIZE, + 0, + ATH79_IP2_IRQ(0)); + } else if (soc_is_qca9558()) { + pdev = ath79_register_pci_ar724x(0, + QCA955X_PCI_CFG_BASE0, + QCA955X_PCI_CTRL_BASE0, + QCA955X_PCI_CRP_BASE0, + QCA955X_PCI_MEM_BASE0, + QCA955X_PCI_MEM_SIZE, + 0, + ATH79_IP2_IRQ(0)); + + pdev = ath79_register_pci_ar724x(1, + QCA955X_PCI_CFG_BASE1, + QCA955X_PCI_CTRL_BASE1, + QCA955X_PCI_CRP_BASE1, + QCA955X_PCI_MEM_BASE1, + QCA955X_PCI_MEM_SIZE, + 1, + ATH79_IP3_IRQ(2)); + } else { + /* No PCI support */ + return -ENODEV; + } + + if (!pdev) + pr_err("unable to register PCI controller device\n"); + + return pdev ? 0 : -ENODEV; +} diff --git a/kernel/arch/mips/ath79/pci.h b/kernel/arch/mips/ath79/pci.h new file mode 100644 index 000000000..1d00a3803 --- /dev/null +++ b/kernel/arch/mips/ath79/pci.h @@ -0,0 +1,35 @@ +/* + * Atheros AR71XX/AR724X PCI support + * + * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> + * Copyright (C) 2008-2011 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 _ATH79_PCI_H +#define _ATH79_PCI_H + +struct ath79_pci_irq { + int bus; + u8 slot; + u8 pin; + int irq; +}; + +#ifdef CONFIG_PCI +void ath79_pci_set_irq_map(unsigned nr_irqs, const struct ath79_pci_irq *map); +void ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *dev)); +int ath79_register_pci(void); +#else +static inline void +ath79_pci_set_irq_map(unsigned nr_irqs, const struct ath79_pci_irq *map) {} +static inline void +ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *)) {} +static inline int ath79_register_pci(void) { return 0; } +#endif + +#endif /* _ATH79_PCI_H */ diff --git a/kernel/arch/mips/ath79/prom.c b/kernel/arch/mips/ath79/prom.c new file mode 100644 index 000000000..597899ad5 --- /dev/null +++ b/kernel/arch/mips/ath79/prom.c @@ -0,0 +1,42 @@ +/* + * Atheros AR71XX/AR724X/AR913X specific prom routines + * + * Copyright (C) 2015 Laurent Fasnacht <l@libres.ch> + * Copyright (C) 2008-2010 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/string.h> +#include <linux/initrd.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> +#include <asm/fw/fw.h> + +#include "common.h" + +void __init prom_init(void) +{ + fw_init_cmdline(); + +#ifdef CONFIG_BLK_DEV_INITRD + /* Read the initrd address from the firmware environment */ + initrd_start = fw_getenvl("initrd_start"); + if (initrd_start) { + initrd_start = KSEG0ADDR(initrd_start); + initrd_end = initrd_start + fw_getenvl("initrd_size"); + } +#endif +} + +void __init prom_free_prom_memory(void) +{ + /* We do not have to prom memory to free */ +} diff --git a/kernel/arch/mips/ath79/setup.c b/kernel/arch/mips/ath79/setup.c new file mode 100644 index 000000000..7fc8397d1 --- /dev/null +++ b/kernel/arch/mips/ath79/setup.c @@ -0,0 +1,258 @@ +/* + * Atheros AR71XX/AR724X/AR913X specific setup + * + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 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/bootmem.h> +#include <linux/err.h> +#include <linux/clk.h> + +#include <asm/bootinfo.h> +#include <asm/idle.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-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" +#include "dev-common.h" +#include "machtypes.h" + +#define ATH79_SYS_TYPE_LEN 64 + +#define AR71XX_BASE_FREQ 40000000 +#define AR724X_BASE_FREQ 5000000 +#define AR913X_BASE_FREQ 5000000 + +static char ath79_sys_type[ATH79_SYS_TYPE_LEN]; + +static void ath79_restart(char *command) +{ + ath79_device_reset_set(AR71XX_RESET_FULL_CHIP); + for (;;) + if (cpu_wait) + cpu_wait(); +} + +static void ath79_halt(void) +{ + while (1) + cpu_wait(); +} + +static void __init ath79_detect_sys_type(void) +{ + char *chip = "????"; + u32 id; + u32 major; + u32 minor; + u32 rev = 0; + + id = ath79_reset_rr(AR71XX_RESET_REG_REV_ID); + major = id & REV_ID_MAJOR_MASK; + + switch (major) { + case REV_ID_MAJOR_AR71XX: + minor = id & AR71XX_REV_ID_MINOR_MASK; + rev = id >> AR71XX_REV_ID_REVISION_SHIFT; + rev &= AR71XX_REV_ID_REVISION_MASK; + switch (minor) { + case AR71XX_REV_ID_MINOR_AR7130: + ath79_soc = ATH79_SOC_AR7130; + chip = "7130"; + break; + + case AR71XX_REV_ID_MINOR_AR7141: + ath79_soc = ATH79_SOC_AR7141; + chip = "7141"; + break; + + case AR71XX_REV_ID_MINOR_AR7161: + ath79_soc = ATH79_SOC_AR7161; + chip = "7161"; + break; + } + break; + + case REV_ID_MAJOR_AR7240: + ath79_soc = ATH79_SOC_AR7240; + chip = "7240"; + rev = id & AR724X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR7241: + ath79_soc = ATH79_SOC_AR7241; + chip = "7241"; + rev = id & AR724X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR7242: + ath79_soc = ATH79_SOC_AR7242; + chip = "7242"; + rev = id & AR724X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR913X: + minor = id & AR913X_REV_ID_MINOR_MASK; + rev = id >> AR913X_REV_ID_REVISION_SHIFT; + rev &= AR913X_REV_ID_REVISION_MASK; + switch (minor) { + case AR913X_REV_ID_MINOR_AR9130: + ath79_soc = ATH79_SOC_AR9130; + chip = "9130"; + break; + + case AR913X_REV_ID_MINOR_AR9132: + ath79_soc = ATH79_SOC_AR9132; + chip = "9132"; + break; + } + break; + + case REV_ID_MAJOR_AR9330: + ath79_soc = ATH79_SOC_AR9330; + chip = "9330"; + rev = id & AR933X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR9331: + ath79_soc = ATH79_SOC_AR9331; + chip = "9331"; + rev = id & AR933X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR9341: + ath79_soc = ATH79_SOC_AR9341; + chip = "9341"; + rev = id & AR934X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR9342: + ath79_soc = ATH79_SOC_AR9342; + chip = "9342"; + rev = id & AR934X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_AR9344: + ath79_soc = ATH79_SOC_AR9344; + chip = "9344"; + rev = id & AR934X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_QCA9556: + ath79_soc = ATH79_SOC_QCA9556; + chip = "9556"; + rev = id & QCA955X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_QCA9558: + ath79_soc = ATH79_SOC_QCA9558; + chip = "9558"; + rev = id & QCA955X_REV_ID_REVISION_MASK; + break; + + default: + panic("ath79: unknown SoC, id:0x%08x", id); + } + + ath79_soc_rev = rev; + + if (soc_is_qca955x()) + sprintf(ath79_sys_type, "Qualcomm Atheros QCA%s rev %u", + chip, rev); + else + sprintf(ath79_sys_type, "Atheros AR%s rev %u", chip, rev); + pr_info("SoC: %s\n", ath79_sys_type); +} + +const char *get_system_type(void) +{ + return ath79_sys_type; +} + +int get_c0_perfcount_int(void) +{ + return ATH79_MISC_IRQ(5); +} + +unsigned int get_c0_compare_int(void) +{ + return CP0_LEGACY_COMPARE_IRQ; +} + +void __init plat_mem_setup(void) +{ + set_io_port_base(KSEG1); + + ath79_reset_base = ioremap_nocache(AR71XX_RESET_BASE, + AR71XX_RESET_SIZE); + ath79_pll_base = ioremap_nocache(AR71XX_PLL_BASE, + AR71XX_PLL_SIZE); + ath79_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE, + AR71XX_DDR_CTRL_SIZE); + + ath79_detect_sys_type(); + detect_memory_region(0, ATH79_MEM_SIZE_MIN, ATH79_MEM_SIZE_MAX); + + _machine_restart = ath79_restart; + _machine_halt = ath79_halt; + pm_power_off = ath79_halt; +} + +void __init plat_time_init(void) +{ + unsigned long cpu_clk_rate; + unsigned long ahb_clk_rate; + unsigned long ddr_clk_rate; + unsigned long ref_clk_rate; + + ath79_clocks_init(); + + cpu_clk_rate = ath79_get_sys_clk_rate("cpu"); + ahb_clk_rate = ath79_get_sys_clk_rate("ahb"); + ddr_clk_rate = ath79_get_sys_clk_rate("ddr"); + ref_clk_rate = ath79_get_sys_clk_rate("ref"); + + pr_info("Clocks: CPU:%lu.%03luMHz, DDR:%lu.%03luMHz, AHB:%lu.%03luMHz, Ref:%lu.%03luMHz\n", + cpu_clk_rate / 1000000, (cpu_clk_rate / 1000) % 1000, + ddr_clk_rate / 1000000, (ddr_clk_rate / 1000) % 1000, + ahb_clk_rate / 1000000, (ahb_clk_rate / 1000) % 1000, + ref_clk_rate / 1000000, (ref_clk_rate / 1000) % 1000); + + mips_hpt_frequency = cpu_clk_rate / 2; +} + +static int __init ath79_setup(void) +{ + ath79_gpio_init(); + ath79_register_uart(); + ath79_register_wdt(); + + mips_machine_setup(); + + return 0; +} + +arch_initcall(ath79_setup); + +static void __init ath79_generic_init(void) +{ + /* Nothing to do */ +} + +MIPS_MACHINE(ATH79_MACH_GENERIC, + "Generic", + "Generic AR71XX/AR724X/AR913X based board", + ath79_generic_init); |