diff options
16 files changed, 261 insertions, 299 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt index 7f9fb85f5456..e7f4dc14eff2 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt @@ -2,7 +2,7 @@ Allwinner Sunxi Interrupt Controller Required properties: -- compatible : should be "allwinner,sunxi-ic" +- compatible : should be "allwinner,sun4i-ic" - reg : Specifies base physical address and size of the registers. - interrupt-controller : Identifies the node as an interrupt controller - #interrupt-cells : Specifies the number of cells needed to encode an @@ -97,7 +97,7 @@ The interrupt sources are as follows: Example: intc: interrupt-controller { - compatible = "allwinner,sunxi-ic"; + compatible = "allwinner,sun4i-ic"; reg = <0x01c20400 0x400>; interrupt-controller; #interrupt-cells = <2>; diff --git a/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt b/Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt index 0c7b64e95a61..48aeb7884ed3 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt +++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-timer.txt @@ -2,7 +2,7 @@ Allwinner A1X SoCs Timer Controller Required properties: -- compatible : should be "allwinner,sunxi-timer" +- compatible : should be "allwinner,sun4i-timer" - reg : Specifies base physical address and size of the registers. - interrupts : The interrupt of the first timer - clocks: phandle to the source clock (usually a 24 MHz fixed clock) @@ -10,7 +10,7 @@ Required properties: Example: timer { - compatible = "allwinner,sunxi-timer"; + compatible = "allwinner,sun4i-timer"; reg = <0x01c20c00 0x400>; interrupts = <22>; clocks = <&osc>; diff --git a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt b/Documentation/devicetree/bindings/watchdog/sun4i-wdt.txt index 0b2717775600..ecd650adff31 100644 --- a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/sun4i-wdt.txt @@ -1,13 +1,13 @@ -Allwinner sunXi Watchdog timer +Allwinner sun4i Watchdog timer Required properties: -- compatible : should be "allwinner,sunxi-wdt" +- compatible : should be "allwinner,sun4i-wdt" - reg : Specifies base physical address and size of the registers. Example: wdt: watchdog@01c20c90 { - compatible = "allwinner,sunxi-wdt"; + compatible = "allwinner,sun4i-wdt"; reg = <0x01c20c90 0x10>; }; diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 8709a39bd34c..d259c782d742 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -1,10 +1,11 @@ config ARCH_SUNXI bool "Allwinner A1X SOCs" if ARCH_MULTI_V7 select CLKSRC_MMIO + select CLKSRC_OF select COMMON_CLK select GENERIC_CLOCKEVENTS select GENERIC_IRQ_CHIP select PINCTRL select SPARSE_IRQ - select SUNXI_TIMER - select PINCTRL_SUNXI
\ No newline at end of file + select SUN4I_TIMER + select PINCTRL_SUNXI diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c index 23afb732cb40..706ce35396b8 100644 --- a/arch/arm/mach-sunxi/sunxi.c +++ b/arch/arm/mach-sunxi/sunxi.c @@ -10,63 +10,77 @@ * warranty of any kind, whether express or implied. */ +#include <linux/clocksource.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqchip.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/io.h> -#include <linux/sunxi_timer.h> -#include <linux/irqchip/sunxi.h> +#include <linux/clk/sunxi.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> +#include <asm/system_misc.h> #include "sunxi.h" -#define WATCHDOG_CTRL_REG 0x00 -#define WATCHDOG_CTRL_RESTART (1 << 0) -#define WATCHDOG_MODE_REG 0x04 -#define WATCHDOG_MODE_ENABLE (1 << 0) -#define WATCHDOG_MODE_RESET_ENABLE (1 << 1) +#define SUN4I_WATCHDOG_CTRL_REG 0x00 +#define SUN4I_WATCHDOG_CTRL_RESTART (1 << 0) +#define SUN4I_WATCHDOG_MODE_REG 0x04 +#define SUN4I_WATCHDOG_MODE_ENABLE (1 << 0) +#define SUN4I_WATCHDOG_MODE_RESET_ENABLE (1 << 1) static void __iomem *wdt_base; -static void sunxi_setup_restart(void) -{ - struct device_node *np = of_find_compatible_node(NULL, NULL, - "allwinner,sunxi-wdt"); - if (WARN(!np, "unable to setup watchdog restart")) - return; - - wdt_base = of_iomap(np, 0); - WARN(!wdt_base, "failed to map watchdog base address"); -} - -static void sunxi_restart(char mode, const char *cmd) +static void sun4i_restart(char mode, const char *cmd) { if (!wdt_base) return; /* Enable timer and set reset bit in the watchdog */ - writel(WATCHDOG_MODE_ENABLE | WATCHDOG_MODE_RESET_ENABLE, - wdt_base + WATCHDOG_MODE_REG); + writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE, + wdt_base + SUN4I_WATCHDOG_MODE_REG); /* * Restart the watchdog. The default (and lowest) interval * value for the watchdog is 0.5s. */ - writel(WATCHDOG_CTRL_RESTART, wdt_base + WATCHDOG_CTRL_REG); + writel(SUN4I_WATCHDOG_CTRL_RESTART, wdt_base + SUN4I_WATCHDOG_CTRL_REG); while (1) { mdelay(5); - writel(WATCHDOG_MODE_ENABLE | WATCHDOG_MODE_RESET_ENABLE, - wdt_base + WATCHDOG_MODE_REG); + writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE, + wdt_base + SUN4I_WATCHDOG_MODE_REG); } } +static struct of_device_id sunxi_restart_ids[] = { + { .compatible = "allwinner,sun4i-wdt", .data = sun4i_restart }, + { /*sentinel*/ } +}; + +static void sunxi_setup_restart(void) +{ + const struct of_device_id *of_id; + struct device_node *np; + + np = of_find_matching_node(NULL, sunxi_restart_ids); + if (WARN(!np, "unable to setup watchdog restart")) + return; + + wdt_base = of_iomap(np, 0); + WARN(!wdt_base, "failed to map watchdog base address"); + + of_id = of_match_node(sunxi_restart_ids, np); + WARN(!of_id, "restart function not available"); + + arm_pm_restart = of_id->data; +} + static struct map_desc sunxi_io_desc[] __initdata = { { .virtual = (unsigned long) SUNXI_REGS_VIRT_BASE, @@ -81,6 +95,12 @@ void __init sunxi_map_io(void) iotable_init(sunxi_io_desc, ARRAY_SIZE(sunxi_io_desc)); } +static void __init sunxi_timer_init(void) +{ + sunxi_init_clocks(); + clocksource_of_init(); +} + static void __init sunxi_dt_init(void) { sunxi_setup_restart(); @@ -97,9 +117,7 @@ static const char * const sunxi_board_dt_compat[] = { DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)") .init_machine = sunxi_dt_init, .map_io = sunxi_map_io, - .init_irq = sunxi_init_irq, - .handle_irq = sunxi_handle_irq, - .restart = sunxi_restart, - .init_time = &sunxi_timer_init, + .init_irq = irqchip_init, + .init_time = sunxi_timer_init, .dt_compat = sunxi_board_dt_compat, MACHINE_END diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7df60b..9002185a0a1a 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -25,7 +25,7 @@ config DW_APB_TIMER_OF config ARMADA_370_XP_TIMER bool -config SUNXI_TIMER +config SUN4I_TIMER bool config VT8500_TIMER diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 89c5adc498b3..98f220a7a92c 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o obj-$(CONFIG_ARCH_MXS) += mxs_timer.o -obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o +obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index 3ef11fba781c..37f5325bec95 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/of.h> +#include <linux/clocksource.h> extern struct of_device_id __clksrc_of_table[]; @@ -26,7 +27,7 @@ void __init clocksource_of_init(void) { struct device_node *np; const struct of_device_id *match; - void (*init_func)(struct device_node *); + clocksource_of_init_fn init_func; for_each_matching_node_and_match(np, __clksrc_of_table, &match) { init_func = match->data; diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sun4i_timer.c index 0ce85e29769b..d4674e78ef35 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -22,66 +22,64 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/sunxi_timer.h> -#include <linux/clk/sunxi.h> -#define TIMER_CTL_REG 0x00 -#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_EN(val) (1 << val) #define TIMER_IRQ_ST_REG 0x04 -#define TIMER0_CTL_REG 0x10 -#define TIMER0_CTL_ENABLE (1 << 0) -#define TIMER0_CTL_AUTORELOAD (1 << 1) -#define TIMER0_CTL_ONESHOT (1 << 7) -#define TIMER0_INTVAL_REG 0x14 -#define TIMER0_CNTVAL_REG 0x18 +#define TIMER_CTL_REG(val) (0x10 * val + 0x10) +#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_CTL_AUTORELOAD (1 << 1) +#define TIMER_CTL_ONESHOT (1 << 7) +#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) +#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) #define TIMER_SCAL 16 static void __iomem *timer_base; -static void sunxi_clkevt_mode(enum clock_event_mode mode, +static void sun4i_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *clk) { - u32 u = readl(timer_base + TIMER0_CTL_REG); + u32 u = readl(timer_base + TIMER_CTL_REG(0)); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - u &= ~(TIMER0_CTL_ONESHOT); - writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); + u &= ~(TIMER_CTL_ONESHOT); + writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); break; case CLOCK_EVT_MODE_ONESHOT: - writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); + writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: default: - writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); + writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); break; } } -static int sunxi_clkevt_next_event(unsigned long evt, +static int sun4i_clkevt_next_event(unsigned long evt, struct clock_event_device *unused) { - u32 u = readl(timer_base + TIMER0_CTL_REG); - writel(evt, timer_base + TIMER0_CNTVAL_REG); - writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, - timer_base + TIMER0_CTL_REG); + u32 u = readl(timer_base + TIMER_CTL_REG(0)); + writel(evt, timer_base + TIMER_CNTVAL_REG(0)); + writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, + timer_base + TIMER_CTL_REG(0)); return 0; } -static struct clock_event_device sunxi_clockevent = { - .name = "sunxi_tick", +static struct clock_event_device sun4i_clockevent = { + .name = "sun4i_tick", .rating = 300, .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = sunxi_clkevt_mode, - .set_next_event = sunxi_clkevt_next_event, + .set_mode = sun4i_clkevt_mode, + .set_next_event = sun4i_clkevt_next_event, }; -static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) +static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = (struct clock_event_device *)dev_id; @@ -91,30 +89,20 @@ static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction sunxi_timer_irq = { - .name = "sunxi_timer0", +static struct irqaction sun4i_timer_irq = { + .name = "sun4i_timer0", .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = sunxi_timer_interrupt, - .dev_id = &sunxi_clockevent, -}; - -static struct of_device_id sunxi_timer_dt_ids[] = { - { .compatible = "allwinner,sunxi-timer" }, - { } + .handler = sun4i_timer_interrupt, + .dev_id = &sun4i_clockevent, }; -void __init sunxi_timer_init(void) +static void __init sun4i_timer_init(struct device_node *node) { - struct device_node *node; unsigned long rate = 0; struct clk *clk; int ret, irq; u32 val; - node = of_find_matching_node(NULL, sunxi_timer_dt_ids); - if (!node) - panic("No sunxi timer node"); - timer_base = of_iomap(node, 0); if (!timer_base) panic("Can't map registers"); @@ -123,8 +111,6 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - sunxi_init_clocks(); - clk = of_clk_get(node, 0); if (IS_ERR(clk)) panic("Can't get timer clock"); @@ -132,29 +118,31 @@ void __init sunxi_timer_init(void) rate = clk_get_rate(clk); writel(rate / (TIMER_SCAL * HZ), - timer_base + TIMER0_INTVAL_REG); + timer_base + TIMER_INTVAL_REG(0)); /* set clock source to HOSC, 16 pre-division */ - val = readl(timer_base + TIMER0_CTL_REG); + val = readl(timer_base + TIMER_CTL_REG(0)); val &= ~(0x07 << 4); val &= ~(0x03 << 2); val |= (4 << 4) | (1 << 2); - writel(val, timer_base + TIMER0_CTL_REG); + writel(val, timer_base + TIMER_CTL_REG(0)); /* set mode to auto reload */ - val = readl(timer_base + TIMER0_CTL_REG); - writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); + val = readl(timer_base + TIMER_CTL_REG(0)); + writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); - ret = setup_irq(irq, &sunxi_timer_irq); + ret = setup_irq(irq, &sun4i_timer_irq); if (ret) pr_warn("failed to setup irq %d\n", irq); /* Enable timer0 interrupt */ - val = readl(timer_base + TIMER_CTL_REG); - writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); + val = readl(timer_base + TIMER_IRQ_EN_REG); + writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); - sunxi_clockevent.cpumask = cpumask_of(0); + sun4i_clockevent.cpumask = cpumask_of(0); - clockevents_config_and_register(&sunxi_clockevent, rate / TIMER_SCAL, + clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL, 0x1, 0xff); } +CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", + sun4i_timer_init); diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c index 242255285597..64f553f04fa4 100644 --- a/drivers/clocksource/vt8500_timer.c +++ b/drivers/clocksource/vt8500_timer.c @@ -165,4 +165,4 @@ static void __init vt8500_timer_init(struct device_node *np) 4, 0xf0000000); } -CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init) +CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init); diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 9d8f4f1c6e39..d5e119ca9425 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o obj-$(CONFIG_ARCH_MXS) += irq-mxs.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o -obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o +obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c new file mode 100644 index 000000000000..b66d4ae06898 --- /dev/null +++ b/drivers/irqchip/irq-sun4i.c @@ -0,0 +1,149 @@ +/* + * Allwinner A1X SoCs IRQ chip driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Benn Huang <benn@allwinnertech.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#include "irqchip.h" + +#define SUN4I_IRQ_VECTOR_REG 0x00 +#define SUN4I_IRQ_PROTECTION_REG 0x08 +#define SUN4I_IRQ_NMI_CTRL_REG 0x0c +#define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) +#define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) +#define SUN4I_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) +#define SUN4I_IRQ_MASK_REG(x) (0x50 + 0x4 * x) + +static void __iomem *sun4i_irq_base; +static struct irq_domain *sun4i_irq_domain; + +static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); + +void sun4i_irq_ack(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); + writel(val | (1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); +} + +static void sun4i_irq_mask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + writel(val & ~(1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); +} + +static void sun4i_irq_unmask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + writel(val | (1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); +} + +static struct irq_chip sun4i_irq_chip = { + .name = "sun4i_irq", + .irq_ack = sun4i_irq_ack, + .irq_mask = sun4i_irq_mask, + .irq_unmask = sun4i_irq_unmask, +}; + +static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &sun4i_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +} + +static struct irq_domain_ops sun4i_irq_ops = { + .map = sun4i_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init sun4i_of_init(struct device_node *node, + struct device_node *parent) +{ + sun4i_irq_base = of_iomap(node, 0); + if (!sun4i_irq_base) + panic("%s: unable to map IC registers\n", + node->full_name); + + /* Disable all interrupts */ + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0)); + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); + + /* Mask all the interrupts */ + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); + + /* Clear all the pending interrupts */ + writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); + writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1)); + writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2)); + + /* Enable protection mode */ + writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG); + + /* Configure the external interrupt source type */ + writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG); + + sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32, + &sun4i_irq_ops, NULL); + if (!sun4i_irq_domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + + set_handle_irq(sun4i_handle_irq); + + return 0; +} +IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-ic", sun4i_of_init); + +static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) +{ + u32 irq, hwirq; + + hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; + while (hwirq != 0) { + irq = irq_find_mapping(sun4i_irq_domain, hwirq); + handle_IRQ(irq, regs); + hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; + } +} diff --git a/drivers/irqchip/irq-sunxi.c b/drivers/irqchip/irq-sunxi.c deleted file mode 100644 index 10974fa42653..000000000000 --- a/drivers/irqchip/irq-sunxi.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Allwinner A1X SoCs IRQ chip driver. - * - * Copyright (C) 2012 Maxime Ripard - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * Based on code from - * Allwinner Technology Co., Ltd. <www.allwinnertech.com> - * Benn Huang <benn@allwinnertech.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> - -#include <linux/irqchip/sunxi.h> - -#define SUNXI_IRQ_VECTOR_REG 0x00 -#define SUNXI_IRQ_PROTECTION_REG 0x08 -#define SUNXI_IRQ_NMI_CTRL_REG 0x0c -#define SUNXI_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) -#define SUNXI_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) -#define SUNXI_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) -#define SUNXI_IRQ_MASK_REG(x) (0x50 + 0x4 * x) - -static void __iomem *sunxi_irq_base; -static struct irq_domain *sunxi_irq_domain; - -void sunxi_irq_ack(struct irq_data *irqd) -{ - unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - - val = readl(sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); - writel(val | (1 << irq_off), - sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); -} - -static void sunxi_irq_mask(struct irq_data *irqd) -{ - unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - - val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); - writel(val & ~(1 << irq_off), - sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); -} - -static void sunxi_irq_unmask(struct irq_data *irqd) -{ - unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - - val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); - writel(val | (1 << irq_off), - sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); -} - -static struct irq_chip sunxi_irq_chip = { - .name = "sunxi_irq", - .irq_ack = sunxi_irq_ack, - .irq_mask = sunxi_irq_mask, - .irq_unmask = sunxi_irq_unmask, -}; - -static int sunxi_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) -{ - irq_set_chip_and_handler(virq, &sunxi_irq_chip, - handle_level_irq); - set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); - - return 0; -} - -static struct irq_domain_ops sunxi_irq_ops = { - .map = sunxi_irq_map, - .xlate = irq_domain_xlate_onecell, -}; - -static int __init sunxi_of_init(struct device_node *node, - struct device_node *parent) -{ - sunxi_irq_base = of_iomap(node, 0); - if (!sunxi_irq_base) - panic("%s: unable to map IC registers\n", - node->full_name); - - /* Disable all interrupts */ - writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(0)); - writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(1)); - writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(2)); - - /* Mask all the interrupts */ - writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(0)); - writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(1)); - writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(2)); - - /* Clear all the pending interrupts */ - writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(0)); - writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(1)); - writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(2)); - - /* Enable protection mode */ - writel(0x01, sunxi_irq_base + SUNXI_IRQ_PROTECTION_REG); - - /* Configure the external interrupt source type */ - writel(0x00, sunxi_irq_base + SUNXI_IRQ_NMI_CTRL_REG); - - sunxi_irq_domain = irq_domain_add_linear(node, 3 * 32, - &sunxi_irq_ops, NULL); - if (!sunxi_irq_domain) - panic("%s: unable to create IRQ domain\n", node->full_name); - - return 0; -} - -static struct of_device_id sunxi_irq_dt_ids[] __initconst = { - { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init }, - { } -}; - -void __init sunxi_init_irq(void) -{ - of_irq_init(sunxi_irq_dt_ids); -} - -asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs) -{ - u32 irq, hwirq; - - hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; - while (hwirq != 0) { - irq = irq_find_mapping(sunxi_irq_domain, hwirq); - handle_IRQ(irq, regs); - hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; - } -} diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 08ed5e19d8c6..192d6d1771ee 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -332,16 +332,23 @@ extern int clocksource_mmio_init(void __iomem *, const char *, extern int clocksource_i8253_init(void); +struct device_node; +typedef void(*clocksource_of_init_fn)(struct device_node *); #ifdef CONFIG_CLKSRC_OF extern void clocksource_of_init(void); #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ static const struct of_device_id __clksrc_of_table_##name \ __used __section(__clksrc_of_table) \ - = { .compatible = compat, .data = fn }; + = { .compatible = compat, \ + .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #else static inline void clocksource_of_init(void) {} -#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) +#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ + static const struct of_device_id __clksrc_of_table_##name \ + __attribute__((unused)) \ + = { .compatible = compat, \ + .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #endif #endif /* _LINUX_CLOCKSOURCE_H */ diff --git a/include/linux/irqchip/sunxi.h b/include/linux/irqchip/sunxi.h deleted file mode 100644 index 1fe2c2260e2b..000000000000 --- a/include/linux/irqchip/sunxi.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 Maxime Ripard - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __LINUX_IRQCHIP_SUNXI_H -#define __LINUX_IRQCHIP_SUNXI_H - -#include <asm/exception.h> - -extern void sunxi_init_irq(void); - -extern asmlinkage void __exception_irq_entry sunxi_handle_irq( - struct pt_regs *regs); - -#endif diff --git a/include/linux/sunxi_timer.h b/include/linux/sunxi_timer.h deleted file mode 100644 index 18081787e5f3..000000000000 --- a/include/linux/sunxi_timer.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 Maxime Ripard - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __SUNXI_TIMER_H -#define __SUNXI_TIMER_H - -#include <asm/mach/time.h> - -void sunxi_timer_init(void); - -#endif |