diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 12:05:15 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 12:05:15 -0800 |
commit | d027db132b395dabfac208e52a7e510e441bb9d2 (patch) | |
tree | 24b055b2385f9848e77e646ce475991d8675c3c4 /drivers/clocksource | |
parent | d01e4afdbb65e030fd6f1f96c30a558e2eb0f279 (diff) | |
parent | 5faf7cbb848da827f6ea1458b5a1c26a44e7510a (diff) |
Merge tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC updates from Olof Johansson:
"This contains the bulk of new SoC development for this merge window.
Two new platforms have been added, the sunxi platforms (Allwinner A1x
SoCs) by Maxime Ripard, and a generic Broadcom platform for a new
series of ARMv7 platforms from them, where the hope is that we can
keep the platform code generic enough to have them all share one mach
directory. The new Broadcom platform is contributed by Christian
Daudt.
Highbank has grown support for Calxeda's next generation of hardware,
ECX-2000.
clps711x has seen a lot of cleanup from Alexander Shiyan, and he's
also taken on maintainership of the platform.
Beyond this there has been a bunch of work from a number of people on
converting more platforms to IRQ domains, pinctrl conversion, cleanup
and general feature enablement across most of the active platforms."
Fix up trivial conflicts as per Olof.
* tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (174 commits)
mfd: vexpress-sysreg: Remove LEDs code
irqchip: irq-sunxi: Add terminating entry for sunxi_irq_dt_ids
clocksource: sunxi_timer: Add terminating entry for sunxi_timer_dt_ids
irq: versatile: delete dangling variable
ARM: sunxi: add missing include for mdelay()
ARM: EXYNOS: Avoid early use of of_machine_is_compatible()
ARM: dts: add node for PL330 MDMA1 controller for exynos4
ARM: EXYNOS: Add support for secondary CPU bring-up on Exynos4412
ARM: EXYNOS: add UART3 to DEBUG_LL ports
ARM: S3C24XX: Add clkdev entry for camif-upll clock
ARM: SAMSUNG: Add s3c24xx/s3c64xx CAMIF GPIO setup helpers
ARM: sunxi: Add missing sun4i.dtsi file
pinctrl: samsung: Do not initialise statics to 0
ARM i.MX6: remove gate_mask from pllv3
ARM i.MX6: Fix ethernet PLL clocks
ARM i.MX6: rename PLLs according to datasheet
ARM i.MX6: Add pwm support
ARM i.MX51: Add pwm support
ARM i.MX53: Add pwm support
ARM: mx5: Replace clk_register_clkdev with clock DT lookup
...
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Kconfig | 3 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/sunxi_timer.c | 171 |
3 files changed, 175 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 6a78073c3808..a0985732f1e2 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -22,6 +22,9 @@ config DW_APB_TIMER_OF config ARMADA_370_XP_TIMER bool +config SUNXI_TIMER + bool + config CLKSRC_DBX500_PRCMU bool "Clocksource PRCMU Timer" depends on UX500_SOC_DB8500 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 603be366f762..36f06de4c5ab 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o 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_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c new file mode 100644 index 000000000000..3cd1bd3d7aee --- /dev/null +++ b/drivers/clocksource/sunxi_timer.c @@ -0,0 +1,171 @@ +/* + * Allwinner A1X SoCs timer handling. + * + * 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/clk.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#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_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_SCAL 16 + +static void __iomem *timer_base; + +static void sunxi_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + u32 u = readl(timer_base + TIMER0_CTL_REG); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + u &= ~(TIMER0_CTL_ONESHOT); + writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); + break; + + case CLOCK_EVT_MODE_ONESHOT: + writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); + break; + } +} + +static int sunxi_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); + + return 0; +} + +static struct clock_event_device sunxi_clockevent = { + .name = "sunxi_tick", + .shift = 32, + .rating = 300, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sunxi_clkevt_mode, + .set_next_event = sunxi_clkevt_next_event, +}; + + +static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + writel(0x1, timer_base + TIMER_IRQ_ST_REG); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction sunxi_timer_irq = { + .name = "sunxi_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" }, + { } +}; + +static void __init sunxi_timer_init(void) +{ + 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"); + + irq = irq_of_parse_and_map(node, 0); + 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"); + + rate = clk_get_rate(clk); + + writel(rate / (TIMER_SCAL * HZ), + timer_base + TIMER0_INTVAL_REG); + + /* set clock source to HOSC, 16 pre-division */ + val = readl(timer_base + TIMER0_CTL_REG); + val &= ~(0x07 << 4); + val &= ~(0x03 << 2); + val |= (4 << 4) | (1 << 2); + writel(val, timer_base + TIMER0_CTL_REG); + + /* set mode to auto reload */ + val = readl(timer_base + TIMER0_CTL_REG); + writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); + + ret = setup_irq(irq, &sunxi_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); + + sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL, + NSEC_PER_SEC, + sunxi_clockevent.shift); + sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff, + &sunxi_clockevent); + sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1, + &sunxi_clockevent); + sunxi_clockevent.cpumask = cpumask_of(0); + + clockevents_register_device(&sunxi_clockevent); +} + +struct sys_timer sunxi_timer = { + .init = sunxi_timer_init, +}; |