diff options
Diffstat (limited to '')
31 files changed, 3665 insertions, 0 deletions
diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig new file mode 100644 index 000000000..9547cf1ea --- /dev/null +++ b/arch/mips/ath79/Kconfig @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: GPL-2.0 +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/arch/mips/ath79/Makefile b/arch/mips/ath79/Makefile new file mode 100644 index 000000000..fcc382cfc --- /dev/null +++ b/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 + +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/arch/mips/ath79/Platform b/arch/mips/ath79/Platform new file mode 100644 index 000000000..2bd663647 --- /dev/null +++ b/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/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c new file mode 100644 index 000000000..cf9158e3c --- /dev/null +++ b/arch/mips/ath79/clock.c @@ -0,0 +1,723 @@ +/* + * 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/init.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <dt-bindings/clock/ath79-clk.h> + +#include <asm/div64.h> + +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include "common.h" +#include "machtypes.h" + +#define AR71XX_BASE_FREQ 40000000 +#define AR724X_BASE_FREQ 40000000 + +static struct clk *clks[ATH79_CLK_END]; +static struct clk_onecell_data clk_data = { + .clks = clks, + .clk_num = ARRAY_SIZE(clks), +}; + +static struct clk *__init ath79_add_sys_clkdev( + const char *id, unsigned long rate) +{ + struct clk *clk; + int err; + + clk = clk_register_fixed_rate(NULL, id, NULL, 0, rate); + if (IS_ERR(clk)) + panic("failed to allocate %s clock structure", id); + + err = clk_register_clkdev(clk, id, NULL); + if (err) + panic("unable to register %s clock device", id); + + return clk; +} + +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_FB_SHIFT) & AR71XX_PLL_FB_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); + clks[ATH79_CLK_CPU] = ath79_add_sys_clkdev("cpu", cpu_rate); + clks[ATH79_CLK_DDR] = ath79_add_sys_clkdev("ddr", ddr_rate); + clks[ATH79_CLK_AHB] = ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ahb", NULL); + clk_add_alias("uart", NULL, "ahb", NULL); +} + +static struct clk * __init ath79_reg_ffclk(const char *name, + const char *parent_name, unsigned int mult, unsigned int div) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mult, div); + if (IS_ERR(clk)) + panic("failed to allocate %s clock structure", name); + + return clk; +} + +static void __init ar724x_clk_init(struct clk *ref_clk, void __iomem *pll_base) +{ + u32 pll; + u32 mult, div, ddr_div, ahb_div; + + pll = __raw_readl(pll_base + AR724X_PLL_REG_CPU_CONFIG); + + mult = ((pll >> AR724X_PLL_FB_SHIFT) & AR724X_PLL_FB_MASK); + div = ((pll >> AR724X_PLL_REF_DIV_SHIFT) & AR724X_PLL_REF_DIV_MASK) * 2; + + ddr_div = ((pll >> AR724X_DDR_DIV_SHIFT) & AR724X_DDR_DIV_MASK) + 1; + ahb_div = (((pll >> AR724X_AHB_DIV_SHIFT) & AR724X_AHB_DIV_MASK) + 1) * 2; + + clks[ATH79_CLK_CPU] = ath79_reg_ffclk("cpu", "ref", mult, div); + clks[ATH79_CLK_DDR] = ath79_reg_ffclk("ddr", "ref", mult, div * ddr_div); + clks[ATH79_CLK_AHB] = ath79_reg_ffclk("ahb", "ref", mult, div * ahb_div); +} + +static void __init ar724x_clocks_init(void) +{ + struct clk *ref_clk; + + ref_clk = ath79_add_sys_clkdev("ref", AR724X_BASE_FREQ); + + ar724x_clk_init(ref_clk, ath79_pll_base); + + /* just make happy plat_time_init() from arch/mips/ath79/setup.c */ + clk_register_clkdev(clks[ATH79_CLK_CPU], "cpu", NULL); + clk_register_clkdev(clks[ATH79_CLK_DDR], "ddr", NULL); + clk_register_clkdev(clks[ATH79_CLK_AHB], "ahb", NULL); + + clk_add_alias("wdt", NULL, "ahb", NULL); + clk_add_alias("uart", NULL, "ahb", NULL); +} + +static void __init ar9330_clk_init(struct clk *ref_clk, void __iomem *pll_base) +{ + u32 clock_ctrl; + u32 ref_div; + u32 ninit_mul; + u32 out_div; + + u32 cpu_div; + u32 ddr_div; + u32 ahb_div; + + clock_ctrl = __raw_readl(pll_base + AR933X_PLL_CLOCK_CTRL_REG); + if (clock_ctrl & AR933X_PLL_CLOCK_CTRL_BYPASS) { + ref_div = 1; + ninit_mul = 1; + out_div = 1; + + cpu_div = 1; + ddr_div = 1; + ahb_div = 1; + } else { + u32 cpu_config; + u32 t; + + cpu_config = __raw_readl(pll_base + AR933X_PLL_CPU_CONFIG_REG); + + t = (cpu_config >> AR933X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + AR933X_PLL_CPU_CONFIG_REFDIV_MASK; + ref_div = t; + + ninit_mul = (cpu_config >> AR933X_PLL_CPU_CONFIG_NINT_SHIFT) & + AR933X_PLL_CPU_CONFIG_NINT_MASK; + + t = (cpu_config >> AR933X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + AR933X_PLL_CPU_CONFIG_OUTDIV_MASK; + if (t == 0) + t = 1; + + out_div = (1 << t); + + cpu_div = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_CPU_DIV_SHIFT) & + AR933X_PLL_CLOCK_CTRL_CPU_DIV_MASK) + 1; + + ddr_div = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_DDR_DIV_SHIFT) & + AR933X_PLL_CLOCK_CTRL_DDR_DIV_MASK) + 1; + + ahb_div = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_AHB_DIV_SHIFT) & + AR933X_PLL_CLOCK_CTRL_AHB_DIV_MASK) + 1; + } + + clks[ATH79_CLK_CPU] = ath79_reg_ffclk("cpu", "ref", + ninit_mul, ref_div * out_div * cpu_div); + clks[ATH79_CLK_DDR] = ath79_reg_ffclk("ddr", "ref", + ninit_mul, ref_div * out_div * ddr_div); + clks[ATH79_CLK_AHB] = ath79_reg_ffclk("ahb", "ref", + ninit_mul, ref_div * out_div * ahb_div); +} + +static void __init ar933x_clocks_init(void) +{ + struct clk *ref_clk; + unsigned long ref_rate; + 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); + + ref_clk = ath79_add_sys_clkdev("ref", ref_rate); + + ar9330_clk_init(ref_clk, ath79_pll_base); + + /* just make happy plat_time_init() from arch/mips/ath79/setup.c */ + clk_register_clkdev(clks[ATH79_CLK_CPU], "cpu", NULL); + clk_register_clkdev(clks[ATH79_CLK_DDR], "ddr", NULL); + clk_register_clkdev(clks[ATH79_CLK_AHB], "ahb", NULL); + + 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); + clks[ATH79_CLK_CPU] = ath79_add_sys_clkdev("cpu", cpu_rate); + clks[ATH79_CLK_DDR] = ath79_add_sys_clkdev("ddr", ddr_rate); + clks[ATH79_CLK_AHB] = 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 qca953x_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(QCA953X_RESET_REG_BOOTSTRAP); + if (bootstrap & QCA953X_BOOTSTRAP_REF_CLK_40) + ref_rate = 40 * 1000 * 1000; + else + ref_rate = 25 * 1000 * 1000; + + pll = ath79_pll_rr(QCA953X_PLL_CPU_CONFIG_REG); + out_div = (pll >> QCA953X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + QCA953X_PLL_CPU_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA953X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + QCA953X_PLL_CPU_CONFIG_REFDIV_MASK; + nint = (pll >> QCA953X_PLL_CPU_CONFIG_NINT_SHIFT) & + QCA953X_PLL_CPU_CONFIG_NINT_MASK; + frac = (pll >> QCA953X_PLL_CPU_CONFIG_NFRAC_SHIFT) & + QCA953X_PLL_CPU_CONFIG_NFRAC_MASK; + + cpu_pll = nint * ref_rate / ref_div; + cpu_pll += frac * (ref_rate >> 6) / ref_div; + cpu_pll /= (1 << out_div); + + pll = ath79_pll_rr(QCA953X_PLL_DDR_CONFIG_REG); + out_div = (pll >> QCA953X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & + QCA953X_PLL_DDR_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA953X_PLL_DDR_CONFIG_REFDIV_SHIFT) & + QCA953X_PLL_DDR_CONFIG_REFDIV_MASK; + nint = (pll >> QCA953X_PLL_DDR_CONFIG_NINT_SHIFT) & + QCA953X_PLL_DDR_CONFIG_NINT_MASK; + frac = (pll >> QCA953X_PLL_DDR_CONFIG_NFRAC_SHIFT) & + QCA953X_PLL_DDR_CONFIG_NFRAC_MASK; + + ddr_pll = nint * ref_rate / ref_div; + ddr_pll += frac * (ref_rate >> 6) / (ref_div << 4); + ddr_pll /= (1 << out_div); + + clk_ctrl = ath79_pll_rr(QCA953X_PLL_CLK_CTRL_REG); + + postdiv = (clk_ctrl >> QCA953X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) & + QCA953X_PLL_CLK_CTRL_CPU_POST_DIV_MASK; + + if (clk_ctrl & QCA953X_PLL_CLK_CTRL_CPU_PLL_BYPASS) + cpu_rate = ref_rate; + else if (clk_ctrl & QCA953X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL) + cpu_rate = cpu_pll / (postdiv + 1); + else + cpu_rate = ddr_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA953X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) & + QCA953X_PLL_CLK_CTRL_DDR_POST_DIV_MASK; + + if (clk_ctrl & QCA953X_PLL_CLK_CTRL_DDR_PLL_BYPASS) + ddr_rate = ref_rate; + else if (clk_ctrl & QCA953X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL) + ddr_rate = ddr_pll / (postdiv + 1); + else + ddr_rate = cpu_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA953X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) & + QCA953X_PLL_CLK_CTRL_AHB_POST_DIV_MASK; + + if (clk_ctrl & QCA953X_PLL_CLK_CTRL_AHB_PLL_BYPASS) + ahb_rate = ref_rate; + else if (clk_ctrl & QCA953X_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); +} + +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); + clks[ATH79_CLK_CPU] = ath79_add_sys_clkdev("cpu", cpu_rate); + clks[ATH79_CLK_DDR] = ath79_add_sys_clkdev("ddr", ddr_rate); + clks[ATH79_CLK_AHB] = ath79_add_sys_clkdev("ahb", ahb_rate); + + clk_add_alias("wdt", NULL, "ref", NULL); + clk_add_alias("uart", NULL, "ref", NULL); +} + +static void __init qca956x_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, hfrac, lfrac, clk_ctrl, postdiv; + u32 cpu_pll, ddr_pll; + u32 bootstrap; + + /* + * QCA956x timer init workaround has to be applied right before setting + * up the clock. Else, there will be no jiffies + */ + u32 misc; + + misc = ath79_reset_rr(AR71XX_RESET_REG_MISC_INT_ENABLE); + misc |= MISC_INT_MIPS_SI_TIMERINT_MASK; + ath79_reset_wr(AR71XX_RESET_REG_MISC_INT_ENABLE, misc); + + bootstrap = ath79_reset_rr(QCA956X_RESET_REG_BOOTSTRAP); + if (bootstrap & QCA956X_BOOTSTRAP_REF_CLK_40) + ref_rate = 40 * 1000 * 1000; + else + ref_rate = 25 * 1000 * 1000; + + pll = ath79_pll_rr(QCA956X_PLL_CPU_CONFIG_REG); + out_div = (pll >> QCA956X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + QCA956X_PLL_CPU_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA956X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + QCA956X_PLL_CPU_CONFIG_REFDIV_MASK; + + pll = ath79_pll_rr(QCA956X_PLL_CPU_CONFIG1_REG); + nint = (pll >> QCA956X_PLL_CPU_CONFIG1_NINT_SHIFT) & + QCA956X_PLL_CPU_CONFIG1_NINT_MASK; + hfrac = (pll >> QCA956X_PLL_CPU_CONFIG1_NFRAC_H_SHIFT) & + QCA956X_PLL_CPU_CONFIG1_NFRAC_H_MASK; + lfrac = (pll >> QCA956X_PLL_CPU_CONFIG1_NFRAC_L_SHIFT) & + QCA956X_PLL_CPU_CONFIG1_NFRAC_L_MASK; + + cpu_pll = nint * ref_rate / ref_div; + cpu_pll += (lfrac * ref_rate) / ((ref_div * 25) << 13); + cpu_pll += (hfrac >> 13) * ref_rate / ref_div; + cpu_pll /= (1 << out_div); + + pll = ath79_pll_rr(QCA956X_PLL_DDR_CONFIG_REG); + out_div = (pll >> QCA956X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & + QCA956X_PLL_DDR_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA956X_PLL_DDR_CONFIG_REFDIV_SHIFT) & + QCA956X_PLL_DDR_CONFIG_REFDIV_MASK; + pll = ath79_pll_rr(QCA956X_PLL_DDR_CONFIG1_REG); + nint = (pll >> QCA956X_PLL_DDR_CONFIG1_NINT_SHIFT) & + QCA956X_PLL_DDR_CONFIG1_NINT_MASK; + hfrac = (pll >> QCA956X_PLL_DDR_CONFIG1_NFRAC_H_SHIFT) & + QCA956X_PLL_DDR_CONFIG1_NFRAC_H_MASK; + lfrac = (pll >> QCA956X_PLL_DDR_CONFIG1_NFRAC_L_SHIFT) & + QCA956X_PLL_DDR_CONFIG1_NFRAC_L_MASK; + + ddr_pll = nint * ref_rate / ref_div; + ddr_pll += (lfrac * ref_rate) / ((ref_div * 25) << 13); + ddr_pll += (hfrac >> 13) * ref_rate / ref_div; + ddr_pll /= (1 << out_div); + + clk_ctrl = ath79_pll_rr(QCA956X_PLL_CLK_CTRL_REG); + + postdiv = (clk_ctrl >> QCA956X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) & + QCA956X_PLL_CLK_CTRL_CPU_POST_DIV_MASK; + + if (clk_ctrl & QCA956X_PLL_CLK_CTRL_CPU_PLL_BYPASS) + cpu_rate = ref_rate; + else if (clk_ctrl & QCA956X_PLL_CLK_CTRL_CPU_DDRCLK_FROM_CPUPLL) + cpu_rate = ddr_pll / (postdiv + 1); + else + cpu_rate = cpu_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA956X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) & + QCA956X_PLL_CLK_CTRL_DDR_POST_DIV_MASK; + + if (clk_ctrl & QCA956X_PLL_CLK_CTRL_DDR_PLL_BYPASS) + ddr_rate = ref_rate; + else if (clk_ctrl & QCA956X_PLL_CLK_CTRL_CPU_DDRCLK_FROM_DDRPLL) + ddr_rate = cpu_pll / (postdiv + 1); + else + ddr_rate = ddr_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA956X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) & + QCA956X_PLL_CLK_CTRL_AHB_POST_DIV_MASK; + + if (clk_ctrl & QCA956X_PLL_CLK_CTRL_AHB_PLL_BYPASS) + ahb_rate = ref_rate; + else if (clk_ctrl & QCA956X_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() || soc_is_ar913x()) + ar724x_clocks_init(); + else if (soc_is_ar933x()) + ar933x_clocks_init(); + else if (soc_is_ar934x()) + ar934x_clocks_init(); + else if (soc_is_qca953x()) + qca953x_clocks_init(); + else if (soc_is_qca955x()) + qca955x_clocks_init(); + else if (soc_is_qca956x() || soc_is_tp9343()) + qca956x_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; +} + +#ifdef CONFIG_OF +static void __init ath79_clocks_init_dt(struct device_node *np) +{ + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +CLK_OF_DECLARE(ar7100, "qca,ar7100-pll", ath79_clocks_init_dt); +CLK_OF_DECLARE(ar7240, "qca,ar7240-pll", ath79_clocks_init_dt); +CLK_OF_DECLARE(ar9340, "qca,ar9340-pll", ath79_clocks_init_dt); +CLK_OF_DECLARE(ar9550, "qca,qca9550-pll", ath79_clocks_init_dt); + +static void __init ath79_clocks_init_dt_ng(struct device_node *np) +{ + struct clk *ref_clk; + void __iomem *pll_base; + + ref_clk = of_clk_get(np, 0); + if (IS_ERR(ref_clk)) { + pr_err("%pOF: of_clk_get failed\n", np); + goto err; + } + + pll_base = of_iomap(np, 0); + if (!pll_base) { + pr_err("%pOF: can't map pll registers\n", np); + goto err_clk; + } + + if (of_device_is_compatible(np, "qca,ar9130-pll")) + ar724x_clk_init(ref_clk, pll_base); + else if (of_device_is_compatible(np, "qca,ar9330-pll")) + ar9330_clk_init(ref_clk, pll_base); + else { + pr_err("%pOF: could not find any appropriate clk_init()\n", np); + goto err_iounmap; + } + + if (of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data)) { + pr_err("%pOF: could not register clk provider\n", np); + goto err_iounmap; + } + + return; + +err_iounmap: + iounmap(pll_base); + +err_clk: + clk_put(ref_clk); + +err: + return; +} +CLK_OF_DECLARE(ar9130_clk, "qca,ar9130-pll", ath79_clocks_init_dt_ng); +CLK_OF_DECLARE(ar9330_clk, "qca,ar9330-pll", ath79_clocks_init_dt_ng); +#endif diff --git a/arch/mips/ath79/common.c b/arch/mips/ath79/common.c new file mode 100644 index 000000000..cd6055f9e --- /dev/null +++ b/arch/mips/ath79/common.c @@ -0,0 +1,152 @@ +/* + * 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/export.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); +static void __iomem *ath79_ddr_base; +static void __iomem *ath79_ddr_wb_flush_base; +static void __iomem *ath79_ddr_pci_win_base; + +void ath79_ddr_ctrl_init(void) +{ + ath79_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE, + AR71XX_DDR_CTRL_SIZE); + if (soc_is_ar913x() || soc_is_ar724x() || soc_is_ar933x()) { + ath79_ddr_wb_flush_base = ath79_ddr_base + 0x7c; + ath79_ddr_pci_win_base = 0; + } else { + ath79_ddr_wb_flush_base = ath79_ddr_base + 0x9c; + ath79_ddr_pci_win_base = ath79_ddr_base + 0x7c; + } +} +EXPORT_SYMBOL_GPL(ath79_ddr_ctrl_init); + +void ath79_ddr_wb_flush(u32 reg) +{ + void __iomem *flush_reg = ath79_ddr_wb_flush_base + (reg * 4); + + /* 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_ddr_set_pci_windows(void) +{ + BUG_ON(!ath79_ddr_pci_win_base); + + __raw_writel(AR71XX_PCI_WIN0_OFFS, ath79_ddr_pci_win_base + 0x0); + __raw_writel(AR71XX_PCI_WIN1_OFFS, ath79_ddr_pci_win_base + 0x4); + __raw_writel(AR71XX_PCI_WIN2_OFFS, ath79_ddr_pci_win_base + 0x8); + __raw_writel(AR71XX_PCI_WIN3_OFFS, ath79_ddr_pci_win_base + 0xc); + __raw_writel(AR71XX_PCI_WIN4_OFFS, ath79_ddr_pci_win_base + 0x10); + __raw_writel(AR71XX_PCI_WIN5_OFFS, ath79_ddr_pci_win_base + 0x14); + __raw_writel(AR71XX_PCI_WIN6_OFFS, ath79_ddr_pci_win_base + 0x18); + __raw_writel(AR71XX_PCI_WIN7_OFFS, ath79_ddr_pci_win_base + 0x1c); +} +EXPORT_SYMBOL_GPL(ath79_ddr_set_pci_windows); + +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_qca953x()) + reg = QCA953X_RESET_REG_RESET_MODULE; + else if (soc_is_qca955x()) + reg = QCA955X_RESET_REG_RESET_MODULE; + else if (soc_is_qca956x() || soc_is_tp9343()) + reg = QCA956X_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_qca953x()) + reg = QCA953X_RESET_REG_RESET_MODULE; + else if (soc_is_qca955x()) + reg = QCA955X_RESET_REG_RESET_MODULE; + else if (soc_is_qca956x() || soc_is_tp9343()) + reg = QCA956X_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/arch/mips/ath79/common.h b/arch/mips/ath79/common.h new file mode 100644 index 000000000..870c6b2e9 --- /dev/null +++ b/arch/mips/ath79/common.h @@ -0,0 +1,29 @@ +/* + * 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_ctrl_init(void); + +void ath79_gpio_init(void); + +#endif /* __ATH79_COMMON_H */ diff --git a/arch/mips/ath79/dev-common.c b/arch/mips/ath79/dev-common.c new file mode 100644 index 000000000..9d0172a4d --- /dev/null +++ b/arch/mips/ath79/dev-common.c @@ -0,0 +1,159 @@ +/* + * 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/platform_data/gpio-ath79.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); +} + +static struct ath79_gpio_platform_data ath79_gpio_pdata; + +static struct resource ath79_gpio_resources[] = { + { + .flags = IORESOURCE_MEM, + .start = AR71XX_GPIO_BASE, + .end = AR71XX_GPIO_BASE + AR71XX_GPIO_SIZE - 1, + }, + { + .start = ATH79_MISC_IRQ(2), + .end = ATH79_MISC_IRQ(2), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ath79_gpio_device = { + .name = "ath79-gpio", + .id = -1, + .resource = ath79_gpio_resources, + .num_resources = ARRAY_SIZE(ath79_gpio_resources), + .dev = { + .platform_data = &ath79_gpio_pdata + }, +}; + +void __init ath79_gpio_init(void) +{ + if (soc_is_ar71xx()) { + ath79_gpio_pdata.ngpios = AR71XX_GPIO_COUNT; + } else if (soc_is_ar7240()) { + ath79_gpio_pdata.ngpios = AR7240_GPIO_COUNT; + } else if (soc_is_ar7241() || soc_is_ar7242()) { + ath79_gpio_pdata.ngpios = AR7241_GPIO_COUNT; + } else if (soc_is_ar913x()) { + ath79_gpio_pdata.ngpios = AR913X_GPIO_COUNT; + } else if (soc_is_ar933x()) { + ath79_gpio_pdata.ngpios = AR933X_GPIO_COUNT; + } else if (soc_is_ar934x()) { + ath79_gpio_pdata.ngpios = AR934X_GPIO_COUNT; + ath79_gpio_pdata.oe_inverted = 1; + } else if (soc_is_qca955x()) { + ath79_gpio_pdata.ngpios = QCA955X_GPIO_COUNT; + ath79_gpio_pdata.oe_inverted = 1; + } else { + BUG(); + } + + platform_device_register(&ath79_gpio_device); +} diff --git a/arch/mips/ath79/dev-common.h b/arch/mips/ath79/dev-common.h new file mode 100644 index 000000000..0f514e1af --- /dev/null +++ b/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/arch/mips/ath79/dev-gpio-buttons.c b/arch/mips/ath79/dev-gpio-buttons.c new file mode 100644 index 000000000..366b35fb1 --- /dev/null +++ b/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/arch/mips/ath79/dev-gpio-buttons.h b/arch/mips/ath79/dev-gpio-buttons.h new file mode 100644 index 000000000..481847ac1 --- /dev/null +++ b/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/arch/mips/ath79/dev-leds-gpio.c b/arch/mips/ath79/dev-leds-gpio.c new file mode 100644 index 000000000..dcb1debce --- /dev/null +++ b/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/arch/mips/ath79/dev-leds-gpio.h b/arch/mips/ath79/dev-leds-gpio.h new file mode 100644 index 000000000..6e5d8851e --- /dev/null +++ b/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/arch/mips/ath79/dev-spi.c b/arch/mips/ath79/dev-spi.c new file mode 100644 index 000000000..aa30163ef --- /dev/null +++ b/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/arch/mips/ath79/dev-spi.h b/arch/mips/ath79/dev-spi.h new file mode 100644 index 000000000..d732565ca --- /dev/null +++ b/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/arch/mips/ath79/dev-usb.c b/arch/mips/ath79/dev-usb.c new file mode 100644 index 000000000..8227265bc --- /dev/null +++ b/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/arch/mips/ath79/dev-usb.h b/arch/mips/ath79/dev-usb.h new file mode 100644 index 000000000..4b86a69ca --- /dev/null +++ b/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/arch/mips/ath79/dev-wmac.c b/arch/mips/ath79/dev-wmac.c new file mode 100644 index 000000000..da190b1b8 --- /dev/null +++ b/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/arch/mips/ath79/dev-wmac.h b/arch/mips/ath79/dev-wmac.h new file mode 100644 index 000000000..c9cd8709f --- /dev/null +++ b/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/arch/mips/ath79/early_printk.c b/arch/mips/ath79/early_printk.c new file mode 100644 index 000000000..4b1063117 --- /dev/null +++ b/arch/mips/ath79/early_printk.c @@ -0,0 +1,149 @@ +/* + * 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/setup.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)(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); +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static void prom_putchar_ar71xx(char ch) +{ + void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE)); + + prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY); + __raw_writel((unsigned char)ch, base + UART_TX * 4); + prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY); +} + +static void prom_putchar_ar933x(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 | (unsigned char)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(char ch) +{ + /* nothing to do */ +} + +static void prom_enable_uart(u32 id) +{ + void __iomem *gpio_base; + u32 uart_en; + u32 t; + + switch (id) { + case REV_ID_MAJOR_AR71XX: + uart_en = AR71XX_GPIO_FUNC_UART_EN; + break; + + case REV_ID_MAJOR_AR7240: + case REV_ID_MAJOR_AR7241: + case REV_ID_MAJOR_AR7242: + uart_en = AR724X_GPIO_FUNC_UART_EN; + break; + + case REV_ID_MAJOR_AR913X: + uart_en = AR913X_GPIO_FUNC_UART_EN; + break; + + case REV_ID_MAJOR_AR9330: + case REV_ID_MAJOR_AR9331: + uart_en = AR933X_GPIO_FUNC_UART_EN; + break; + + case REV_ID_MAJOR_AR9341: + case REV_ID_MAJOR_AR9342: + case REV_ID_MAJOR_AR9344: + /* TODO */ + default: + return; + } + + gpio_base = (void __iomem *)KSEG1ADDR(AR71XX_GPIO_BASE); + t = __raw_readl(gpio_base + AR71XX_GPIO_REG_FUNC); + t |= uart_en; + __raw_writel(t, gpio_base + AR71XX_GPIO_REG_FUNC); +} + +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_QCA9533: + case REV_ID_MAJOR_QCA9533_V2: + case REV_ID_MAJOR_QCA9556: + case REV_ID_MAJOR_QCA9558: + case REV_ID_MAJOR_TP9343: + case REV_ID_MAJOR_QCA956X: + _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; + return; + } + + prom_enable_uart(id); +} + +void prom_putchar(char ch) +{ + if (!_prom_putchar) + prom_putchar_init(); + + _prom_putchar(ch); +} diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c new file mode 100644 index 000000000..2dfff1f19 --- /dev/null +++ b/arch/mips/ath79/irq.c @@ -0,0 +1,169 @@ +/* + * 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/irqchip.h> +#include <linux/of_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" +#include "machtypes.h" + + +static void ar934x_ip2_irq_dispatch(struct irq_desc *desc) +{ + u32 status; + + status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS); + + if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) { + ath79_ddr_wb_flush(3); + generic_handle_irq(ATH79_IP2_IRQ(0)); + } else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) { + ath79_ddr_wb_flush(4); + generic_handle_irq(ATH79_IP2_IRQ(1)); + } else { + spurious_interrupt(); + } +} + +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(struct irq_desc *desc) +{ + u32 status; + + 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(); + return; + } + + 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)); + } +} + +static void qca955x_ip3_irq_dispatch(struct irq_desc *desc) +{ + u32 status; + + 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(); + return; + } + + 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)); + } +} + +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); +} + +void __init arch_init_irq(void) +{ + unsigned irq_wb_chan2 = -1; + unsigned irq_wb_chan3 = -1; + bool misc_is_ar71xx; + + if (mips_machtype == ATH79_MACH_GENERIC_OF) { + irqchip_init(); + return; + } + + if (soc_is_ar71xx() || soc_is_ar724x() || + soc_is_ar913x() || soc_is_ar933x()) { + irq_wb_chan2 = 3; + irq_wb_chan3 = 2; + } else if (soc_is_ar934x()) { + irq_wb_chan3 = 2; + } + + ath79_cpu_irq_init(irq_wb_chan2, irq_wb_chan3); + + if (soc_is_ar71xx() || soc_is_ar913x()) + misc_is_ar71xx = true; + else if (soc_is_ar724x() || + soc_is_ar933x() || + soc_is_ar934x() || + soc_is_qca955x()) + misc_is_ar71xx = false; + else + BUG(); + ath79_misc_irq_init( + ath79_reset_base + AR71XX_RESET_REG_MISC_INT_STATUS, + ATH79_CPU_IRQ(6), ATH79_MISC_IRQ_BASE, misc_is_ar71xx); + + if (soc_is_ar934x()) + ar934x_ip2_irq_init(); + else if (soc_is_qca955x()) + qca955x_irq_init(); +} diff --git a/arch/mips/ath79/mach-ap121.c b/arch/mips/ath79/mach-ap121.c new file mode 100644 index 000000000..1bf73f2a0 --- /dev/null +++ b/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/arch/mips/ath79/mach-ap136.c b/arch/mips/ath79/mach-ap136.c new file mode 100644 index 000000000..07eac58c3 --- /dev/null +++ b/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/arch/mips/ath79/mach-ap81.c b/arch/mips/ath79/mach-ap81.c new file mode 100644 index 000000000..1c78d497f --- /dev/null +++ b/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/arch/mips/ath79/mach-db120.c b/arch/mips/ath79/mach-db120.c new file mode 100644 index 000000000..9423f5aed --- /dev/null +++ b/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/arch/mips/ath79/mach-pb44.c b/arch/mips/ath79/mach-pb44.c new file mode 100644 index 000000000..75fb96ca6 --- /dev/null +++ b/arch/mips/ath79/mach-pb44.c @@ -0,0 +1,128 @@ +/* + * 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/gpio/machine.h> +#include <linux/platform_data/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 gpiod_lookup_table pb44_i2c_gpiod_table = { + .dev_id = "i2c-gpio.0", + .table = { + GPIO_LOOKUP_IDX("ath79-gpio", PB44_GPIO_I2C_SDA, + NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), + GPIO_LOOKUP_IDX("ath79-gpio", PB44_GPIO_I2C_SCL, + NULL, 1, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), + }, +}; + +static struct platform_device pb44_i2c_gpio_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { + .platform_data = NULL, + } +}; + +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) +{ + gpiod_add_lookup_table(&pb44_i2c_gpiod_table); + 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/arch/mips/ath79/mach-ubnt-xm.c b/arch/mips/ath79/mach-ubnt-xm.c new file mode 100644 index 000000000..4a3c60694 --- /dev/null +++ b/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/arch/mips/ath79/machtypes.h b/arch/mips/ath79/machtypes.h new file mode 100644 index 000000000..a13db3d15 --- /dev/null +++ b/arch/mips/ath79/machtypes.h @@ -0,0 +1,28 @@ +/* + * 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_OF = -1, /* Device tree board */ + 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/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c new file mode 100644 index 000000000..b816cb4a2 --- /dev/null +++ b/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; +static unsigned ath79_pci_nr_irqs; + +static const struct ath79_pci_irq ar71xx_pci_irq_map[] = { + { + .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[] = { + { + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(0), + } +}; + +static const struct ath79_pci_irq qca955x_pci_irq_map[] = { + { + .bus = 0, + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(0), + }, + { + .bus = 1, + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(1), + }, +}; + +int 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/arch/mips/ath79/pci.h b/arch/mips/ath79/pci.h new file mode 100644 index 000000000..1d00a3803 --- /dev/null +++ b/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/arch/mips/ath79/prom.c b/arch/mips/ath79/prom.c new file mode 100644 index 000000000..597899ad5 --- /dev/null +++ b/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/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c new file mode 100644 index 000000000..4c7a93f40 --- /dev/null +++ b/arch/mips/ath79/setup.c @@ -0,0 +1,343 @@ +/* + * 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 <linux/clk-provider.h> +#include <linux/of_fdt.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/prom.h> +#include <asm/fw/fw.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 + +static char ath79_sys_type[ATH79_SYS_TYPE_LEN]; + +static void ath79_restart(char *command) +{ + local_irq_disable(); + 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; + u32 ver = 1; + + 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_QCA9533_V2: + ver = 2; + ath79_soc_rev = 2; + /* drop through */ + + case REV_ID_MAJOR_QCA9533: + ath79_soc = ATH79_SOC_QCA9533; + chip = "9533"; + rev = id & QCA953X_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; + + case REV_ID_MAJOR_QCA956X: + ath79_soc = ATH79_SOC_QCA956X; + chip = "956X"; + rev = id & QCA956X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_TP9343: + ath79_soc = ATH79_SOC_TP9343; + chip = "9343"; + rev = id & QCA956X_REV_ID_REVISION_MASK; + break; + + default: + panic("ath79: unknown SoC, id:0x%08x", id); + } + + if (ver == 1) + ath79_soc_rev = rev; + + if (soc_is_qca953x() || soc_is_qca955x() || soc_is_qca956x()) + sprintf(ath79_sys_type, "Qualcomm Atheros QCA%s ver %u rev %u", + chip, ver, rev); + else if (soc_is_tp9343()) + sprintf(ath79_sys_type, "Qualcomm Atheros TP%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); +} +EXPORT_SYMBOL_GPL(get_c0_perfcount_int); + +unsigned int get_c0_compare_int(void) +{ + return CP0_LEGACY_COMPARE_IRQ; +} + +void __init plat_mem_setup(void) +{ + unsigned long fdt_start; + + set_io_port_base(KSEG1); + + /* Get the position of the FDT passed by the bootloader */ + fdt_start = fw_getenvl("fdt_start"); + if (fdt_start) + __dt_setup_arch((void *)KSEG0ADDR(fdt_start)); + else if (fw_passed_dtb) + __dt_setup_arch((void *)KSEG0ADDR(fw_passed_dtb)); + + if (mips_machtype != ATH79_MACH_GENERIC_OF) { + ath79_reset_base = ioremap_nocache(AR71XX_RESET_BASE, + AR71XX_RESET_SIZE); + ath79_pll_base = ioremap_nocache(AR71XX_PLL_BASE, + AR71XX_PLL_SIZE); + ath79_detect_sys_type(); + ath79_ddr_ctrl_init(); + + detect_memory_region(0, ATH79_MEM_SIZE_MIN, ATH79_MEM_SIZE_MAX); + + /* OF machines should use the reset driver */ + _machine_restart = ath79_restart; + } + + _machine_halt = ath79_halt; + pm_power_off = ath79_halt; +} + +static void __init ath79_of_plat_time_init(void) +{ + struct device_node *np; + struct clk *clk; + unsigned long cpu_clk_rate; + + of_clk_init(NULL); + + np = of_get_cpu_node(0, NULL); + if (!np) { + pr_err("Failed to get CPU node\n"); + return; + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk)); + return; + } + + cpu_clk_rate = clk_get_rate(clk); + + pr_info("CPU clock: %lu.%03lu MHz\n", + cpu_clk_rate / 1000000, (cpu_clk_rate / 1000) % 1000); + + mips_hpt_frequency = cpu_clk_rate / 2; + + clk_put(clk); +} + +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; + + if (IS_ENABLED(CONFIG_OF) && mips_machtype == ATH79_MACH_GENERIC_OF) { + ath79_of_plat_time_init(); + return; + } + + 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) +{ + if (mips_machtype == ATH79_MACH_GENERIC_OF) + return 0; + + ath79_gpio_init(); + ath79_register_uart(); + ath79_register_wdt(); + + mips_machine_setup(); + + return 0; +} + +arch_initcall(ath79_setup); + +void __init device_tree_init(void) +{ + unflatten_and_copy_device_tree(); +} + +MIPS_MACHINE(ATH79_MACH_GENERIC, + "Generic", + "Generic AR71XX/AR724X/AR913X based board", + NULL); + +MIPS_MACHINE(ATH79_MACH_GENERIC_OF, + "DTB", + "Generic AR71XX/AR724X/AR913X based board (DT)", + NULL); |