mirror of https://gitee.com/openkylin/linux.git
Cleanups for Allwinner sunXi architecture:
- Remove sunxi.dtsi - Switch to clocksource/irqchip device tree handlers - Cleanup the watchdog code -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRZDauAAoJEBx+YmzsjxAgu8IP/3V/W2NsFOwQTz7+nVxw19zu cGyCqqTTXed0ku0lmA4mP9x234eSwCplkzyFhnNU2hOdNaObZql8Mibkp3FBIcVn eSq6lbCNpu0VPTjmTqaHYueYFrxmM3DeVQgiZ6m3bDdloTinfndjPKyFK1b8k6ti OluYF25TaW8OFBJDUhXMxRiEbCLls38Z83aFpL/6k3krf/Royn75ey8uq066jyRL d26tL2Rh0vJWojR+RUFdu6+y3CzLdOl5vUwJ/29SaarBDDxWKMIbUY4ezwmxXq2N eCqGsxwtM6VuhYps5b//T+FdKTZbB/3U2ybH4xCU2RYDXZPigeVFT7unE0npYRry LIern3PAKv3jOJ6kwKyEYCX1++6Yt6kyoWNKLk2FYik92KB9Pdw9yCCcpm5F/C9N DetA4GPY/4009bNkKJrlA4Iks+TzrYp+z/qg0OxvDLYaLbknecjYutNL7on9wKrV f5eUlVoRvecHHuf/S+V+DMId4CQrH1RP+jsDKDD6Z8hLpFQ6bnFv2QgSaVwC0f+8 dRMbN9GAReibMAR3BiYodQZnb5x9yF9sBYnRY+ZVgkS7VvsRFi6C9F7cZYQnIia1 j1x8AlFxEvNsSv1bKg5bFaWT3EpO1X2zmT30wPYuGO2XL/UVsz23Einw6jSHiJDi M77vFMzmH30YQ2GLNbKn =227f -----END PGP SIGNATURE----- Merge tag 'sunxi-cleanup-for-3.10' of git://github.com/mripard/linux into next/cleanup From Maxime Ripard: Cleanups for Allwinner sunXi architecture: - Remove sunxi.dtsi - Switch to clocksource/irqchip device tree handlers - Cleanup the watchdog code * tag 'sunxi-cleanup-for-3.10' of git://github.com/mripard/linux: ARM: sunxi: Rework the restart code irqchip: sunxi: Rename sunxi to sun4i irqchip: sunxi: Make use of the IRQCHIP_DECLARE macro clocksource: sunxi: Rename sunxi to sun4i clocksource: sunxi: make use of CLKSRC_OF clocksource: sunxi: Cleanup the timer code clocksource: make CLOCKSOURCE_OF_DECLARE type safe Signed-off-by: Olof Johansson <olof@lixom.net> Add/change conflict in drivers/clocksource/Makefile resolved.
This commit is contained in:
commit
b9d5868e34
|
@ -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>;
|
|
@ -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>;
|
|
@ -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>;
|
||||
};
|
|
@ -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
|
||||
select SUN4I_TIMER
|
||||
select PINCTRL_SUNXI
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
.handler = sun4i_timer_interrupt,
|
||||
.dev_id = &sun4i_clockevent,
|
||||
};
|
||||
|
||||
static struct of_device_id sunxi_timer_dt_ids[] = {
|
||||
{ .compatible = "allwinner,sunxi-timer" },
|
||||
{ }
|
||||
};
|
||||
|
||||
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);
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue