From d072c9658c3c2aa204c22e3ef27a0db6b4e3befa Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 15 Dec 2014 16:59:20 +0100 Subject: [PATCH 1/9] clocksource: sirf: Remove unused variable Commit ef89af1f4380 ("clocksource: sirf: Remove hard-coded clock rate") removes all uses of the timer_div variable. Since the variable is no longer used it should be removed. Signed-off-by: Thierry Reding Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-marco.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c index 361a789d4bee..3ddb81f7ee66 100644 --- a/drivers/clocksource/timer-marco.c +++ b/drivers/clocksource/timer-marco.c @@ -257,7 +257,6 @@ static void __init sirfsoc_clockevent_init(void) /* initialize the kernel jiffy timer source */ static void __init sirfsoc_marco_timer_init(struct device_node *np) { - u32 timer_div; struct clk *clk; clk = of_clk_get(np, 0); From 5833ac98651f7985037e52d4b41f7d4e02e32064 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Mon, 12 Jan 2015 00:04:43 +0800 Subject: [PATCH 2/9] clocksource: marco: Rename marco to atlas7 marco project is replaced by atlas7 and we should obliterate its all traces. Signed-off-by: Barry Song Acked-by: Arnd Bergmann Acked-by: Daniel Lezcano Signed-off-by: Daniel Lezcano --- MAINTAINERS | 2 +- drivers/clocksource/Makefile | 2 +- .../clocksource/{timer-marco.c => timer-atlas7.c} | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) rename drivers/clocksource/{timer-marco.c => timer-atlas7.c} (95%) diff --git a/MAINTAINERS b/MAINTAINERS index 2fa385321245..f6923cf22d3f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -958,7 +958,7 @@ S: Maintained F: arch/arm/mach-prima2/ F: drivers/clk/sirf/ F: drivers/clocksource/timer-prima2.c -F: drivers/clocksource/timer-marco.c +F: drivers/clocksource/timer-atlas7.c N: [^a-z]sirf ARM/EBSA110 MACHINE SUPPORT diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 94d90b24b56b..306bc4770be0 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ORION_TIMER) += time-orion.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o obj-$(CONFIG_ARCH_CLPS711X) += clps711x-timer.o -obj-$(CONFIG_ARCH_MARCO) += timer-marco.o +obj-$(CONFIG_ARCH_ATLAS7) += timer-atlas7.o obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o obj-$(CONFIG_ARCH_MXS) += mxs_timer.o obj-$(CONFIG_ARCH_PXA) += pxa_timer.o diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-atlas7.c similarity index 95% rename from drivers/clocksource/timer-marco.c rename to drivers/clocksource/timer-atlas7.c index 3ddb81f7ee66..60f9de3438b0 100644 --- a/drivers/clocksource/timer-marco.c +++ b/drivers/clocksource/timer-atlas7.c @@ -38,7 +38,7 @@ #define SIRFSOC_TIMER_REG_CNT 6 -static unsigned long marco_timer_rate; +static unsigned long atlas7_timer_rate; static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { SIRFSOC_TIMER_WATCHDOG_EN, @@ -195,7 +195,7 @@ static int sirfsoc_local_timer_setup(struct clock_event_device *ce) ce->rating = 200; ce->set_mode = sirfsoc_timer_set_mode; ce->set_next_event = sirfsoc_timer_set_next_event; - clockevents_calc_mult_shift(ce, marco_timer_rate, 60); + clockevents_calc_mult_shift(ce, atlas7_timer_rate, 60); ce->max_delta_ns = clockevent_delta2ns(-2, ce); ce->min_delta_ns = clockevent_delta2ns(2, ce); ce->cpumask = cpumask_of(cpu); @@ -255,7 +255,7 @@ static void __init sirfsoc_clockevent_init(void) } /* initialize the kernel jiffy timer source */ -static void __init sirfsoc_marco_timer_init(struct device_node *np) +static void __init sirfsoc_atlas7_timer_init(struct device_node *np) { struct clk *clk; @@ -264,7 +264,7 @@ static void __init sirfsoc_marco_timer_init(struct device_node *np) BUG_ON(clk_prepare_enable(clk)); - marco_timer_rate = clk_get_rate(clk); + atlas7_timer_rate = clk_get_rate(clk); /* timer dividers: 0, not divided */ writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); @@ -282,7 +282,7 @@ static void __init sirfsoc_marco_timer_init(struct device_node *np) /* Clear all interrupts */ writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); - BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, marco_timer_rate)); + BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, atlas7_timer_rate)); sirfsoc_clockevent_init(); } @@ -301,6 +301,6 @@ static void __init sirfsoc_of_timer_init(struct device_node *np) if (!sirfsoc_timer1_irq.irq) panic("No irq passed for timer1 via DT\n"); - sirfsoc_marco_timer_init(np); + sirfsoc_atlas7_timer_init(np); } -CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init ); +CLOCKSOURCE_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init); From 8d8bd7be8bf0981564fd557d4b68eeeaaa2325d0 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 8 Jan 2015 10:07:22 +0100 Subject: [PATCH 3/9] ARM: clocksource: Add asm9260_timer driver In some cases asm9260 looks similar to iMX2x. One of exceptions is timer controller. So this patch introduces new driver for this special case. Signed-off-by: Oleksij Rempel Acked-by: Daniel Lezcano Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 10 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/asm9260_timer.c | 220 ++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 drivers/clocksource/asm9260_timer.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index fc01ec27d3c8..bfaaae4c21b4 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -229,4 +229,14 @@ config CLKSRC_MIPS_GIC depends on MIPS_GIC select CLKSRC_OF +config ASM9260_TIMER + bool "Alphascale ASM9260 timer driver" + depends on GENERIC_CLOCKEVENTS + select CLKSRC_MMIO + select CLKSRC_OF + default y if MACH_ASM9260 + help + This enables build of a clocksource and clockevent driver for + the 32-bit System Timer hardware available on a Alphascale ASM9260. + endmenu diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 306bc4770be0..e5661cc95ae1 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -48,3 +48,4 @@ obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o +obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c new file mode 100644 index 000000000000..2c9c993727c8 --- /dev/null +++ b/drivers/clocksource/asm9260_timer.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 Oleksij Rempel + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "asm9260-timer" + +/* + * this device provide 4 offsets for each register: + * 0x0 - plain read write mode + * 0x4 - set mode, OR logic. + * 0x8 - clr mode, XOR logic. + * 0xc - togle mode. + */ +#define SET_REG 4 +#define CLR_REG 8 + +#define HW_IR 0x0000 /* RW. Interrupt */ +#define BM_IR_CR0 BIT(4) +#define BM_IR_MR3 BIT(3) +#define BM_IR_MR2 BIT(2) +#define BM_IR_MR1 BIT(1) +#define BM_IR_MR0 BIT(0) + +#define HW_TCR 0x0010 /* RW. Timer controller */ +/* BM_C*_RST + * Timer Counter and the Prescale Counter are synchronously reset on the + * next positive edge of PCLK. The counters remain reset until TCR[1] is + * returned to zero. */ +#define BM_C3_RST BIT(7) +#define BM_C2_RST BIT(6) +#define BM_C1_RST BIT(5) +#define BM_C0_RST BIT(4) +/* BM_C*_EN + * 1 - Timer Counter and Prescale Counter are enabled for counting + * 0 - counters are disabled */ +#define BM_C3_EN BIT(3) +#define BM_C2_EN BIT(2) +#define BM_C1_EN BIT(1) +#define BM_C0_EN BIT(0) + +#define HW_DIR 0x0020 /* RW. Direction? */ +/* 00 - count up + * 01 - count down + * 10 - ?? 2^n/2 */ +#define BM_DIR_COUNT_UP 0 +#define BM_DIR_COUNT_DOWN 1 +#define BM_DIR0_SHIFT 0 +#define BM_DIR1_SHIFT 4 +#define BM_DIR2_SHIFT 8 +#define BM_DIR3_SHIFT 12 +#define BM_DIR_DEFAULT (BM_DIR_COUNT_UP << BM_DIR0_SHIFT | \ + BM_DIR_COUNT_UP << BM_DIR1_SHIFT | \ + BM_DIR_COUNT_UP << BM_DIR2_SHIFT | \ + BM_DIR_COUNT_UP << BM_DIR3_SHIFT) + +#define HW_TC0 0x0030 /* RO. Timer counter 0 */ +/* HW_TC*. Timer counter owerflow (0xffff.ffff to 0x0000.0000) do not generate + * interrupt. This registers can be used to detect overflow */ +#define HW_TC1 0x0040 +#define HW_TC2 0x0050 +#define HW_TC3 0x0060 + +#define HW_PR 0x0070 /* RW. prescaler */ +#define BM_PR_DISABLE 0 +#define HW_PC 0x0080 /* RO. Prescaler counter */ +#define HW_MCR 0x0090 /* RW. Match control */ +/* enable interrupt on match */ +#define BM_MCR_INT_EN(n) (1 << (n * 3 + 0)) +/* enable TC reset on match */ +#define BM_MCR_RES_EN(n) (1 << (n * 3 + 1)) +/* enable stop TC on match */ +#define BM_MCR_STOP_EN(n) (1 << (n * 3 + 2)) + +#define HW_MR0 0x00a0 /* RW. Match reg */ +#define HW_MR1 0x00b0 +#define HW_MR2 0x00C0 +#define HW_MR3 0x00D0 + +#define HW_CTCR 0x0180 /* Counter control */ +#define BM_CTCR0_SHIFT 0 +#define BM_CTCR1_SHIFT 2 +#define BM_CTCR2_SHIFT 4 +#define BM_CTCR3_SHIFT 6 +#define BM_CTCR_TM 0 /* Timer mode. Every rising PCLK edge. */ +#define BM_CTCR_DEFAULT (BM_CTCR_TM << BM_CTCR0_SHIFT | \ + BM_CTCR_TM << BM_CTCR1_SHIFT | \ + BM_CTCR_TM << BM_CTCR2_SHIFT | \ + BM_CTCR_TM << BM_CTCR3_SHIFT) + +static struct asm9260_timer_priv { + void __iomem *base; + unsigned long ticks_per_jiffy; +} priv; + +static int asm9260_timer_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + /* configure match count for TC0 */ + writel_relaxed(delta, priv.base + HW_MR0); + /* enable TC0 */ + writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG); + return 0; +} + +static void asm9260_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* stop timer0 */ + writel_relaxed(BM_C0_EN, priv.base + HW_TCR + CLR_REG); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* disable reset and stop on match */ + writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0), + priv.base + HW_MCR + CLR_REG); + /* configure match count for TC0 */ + writel_relaxed(priv.ticks_per_jiffy, priv.base + HW_MR0); + /* enable TC0 */ + writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* enable reset and stop on match */ + writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0), + priv.base + HW_MCR + SET_REG); + break; + default: + break; + } +} + +static struct clock_event_device event_dev = { + .name = DRIVER_NAME, + .rating = 200, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = asm9260_timer_set_next_event, + .set_mode = asm9260_timer_set_mode, +}; + +static irqreturn_t asm9260_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + evt->event_handler(evt); + + writel_relaxed(BM_IR_MR0, priv.base + HW_IR); + + return IRQ_HANDLED; +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init asm9260_timer_init(struct device_node *np) +{ + int irq; + struct clk *clk; + int ret; + unsigned long rate; + + priv.base = of_io_request_and_map(np, 0, np->name); + if (!priv.base) + panic("%s: unable to map resource", np->name); + + clk = of_clk_get(np, 0); + + ret = clk_prepare_enable(clk); + if (ret) + panic("Failed to enable clk!\n"); + + irq = irq_of_parse_and_map(np, 0); + ret = request_irq(irq, asm9260_timer_interrupt, IRQF_TIMER, + DRIVER_NAME, &event_dev); + if (ret) + panic("Failed to setup irq!\n"); + + /* set all timers for count-up */ + writel_relaxed(BM_DIR_DEFAULT, priv.base + HW_DIR); + /* disable divider */ + writel_relaxed(BM_PR_DISABLE, priv.base + HW_PR); + /* make sure all timers use every rising PCLK edge. */ + writel_relaxed(BM_CTCR_DEFAULT, priv.base + HW_CTCR); + /* enable interrupt for TC0 and clean setting for all other lines */ + writel_relaxed(BM_MCR_INT_EN(0) , priv.base + HW_MCR); + + rate = clk_get_rate(clk); + clocksource_mmio_init(priv.base + HW_TC1, DRIVER_NAME, rate, + 200, 32, clocksource_mmio_readl_up); + + /* Seems like we can't use counter without match register even if + * actions for MR are disabled. So, set MR to max value. */ + writel_relaxed(0xffffffff, priv.base + HW_MR1); + /* enable TC1 */ + writel_relaxed(BM_C1_EN, priv.base + HW_TCR + SET_REG); + + priv.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ); + event_dev.cpumask = cpumask_of(0); + clockevents_config_and_register(&event_dev, rate, 0x2c00, 0xfffffffe); +} +CLOCKSOURCE_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer", + asm9260_timer_init); From 468b8c4cf3962d4d24eca58da18bb63368ff4fcd Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 25 Jan 2015 22:06:02 +0100 Subject: [PATCH 4/9] clockevents: rockchip: Add rockchip timer for rk3288 The rk3288 board uses the architected timers and these ones are shutdown when the cpu is powered down. There is a need of a broadcast timer in this case to ensure proper wakeup when the cpus are in sleep mode and a timer expires. This driver provides the basic timer functionnality as a backup for the local timers at sleep time. The timer belongs to the alive subsystem. It includes two programmables 64 bits timer channels but the driver only uses 32bits. It works with two operations mode: free running and user defined count. Programing sequence: 1. Timer initialization: * Disable the timer by writing '0' to the CONTROLREG register * Program the timer mode by writing the mode to the CONTROLREG register * Set the interrupt mask 2. Setting the count value: * Load the count value to the registers COUNT0 and COUNT1 (not used). 3. Enable the timer * Write '1' to the CONTROLREG register with the mode (free running or user) Signed-off-by: Daniel Lezcano Reviewed-by: Heiko Stuebner --- .../bindings/timer/rockchip,rk3288-timer.txt | 18 ++ arch/arm/mach-rockchip/Kconfig | 1 + drivers/clocksource/Kconfig | 4 + drivers/clocksource/Makefile | 1 + drivers/clocksource/rockchip_timer.c | 180 ++++++++++++++++++ 5 files changed, 204 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt create mode 100644 drivers/clocksource/rockchip_timer.c diff --git a/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt b/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt new file mode 100644 index 000000000000..87f0b0042bae --- /dev/null +++ b/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt @@ -0,0 +1,18 @@ +Rockchip rk3288 timer + +Required properties: +- compatible: shall be "rockchip,rk3288-timer" +- reg: base address of the timer register starting with TIMERS CONTROL register +- interrupts: should contain the interrupts for Timer0 +- clocks : must contain an entry for each entry in clock-names +- clock-names : must include the following entries: + "timer", "pclk" + +Example: + timer: timer@ff810000 { + compatible = "rockchip,rk3288-timer"; + reg = <0xff810000 0x20>; + interrupts = ; + clocks = <&xin24m>, <&cru PCLK_TIMER>; + clock-names = "timer", "pclk"; + }; diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig index ac5803cac98d..5078932c1683 100644 --- a/arch/arm/mach-rockchip/Kconfig +++ b/arch/arm/mach-rockchip/Kconfig @@ -11,6 +11,7 @@ config ARCH_ROCKCHIP select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP select DW_APB_TIMER_OF + select ROCKCHIP_TIMER select ARM_GLOBAL_TIMER select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK help diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index bfaaae4c21b4..a89120bf640c 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -26,6 +26,10 @@ config DW_APB_TIMER_OF select DW_APB_TIMER select CLKSRC_OF +config ROCKCHIP_TIMER + bool + select CLKSRC_OF + config ARMADA_370_XP_TIMER bool select CLKSRC_OF diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index e5661cc95ae1..21c11e28cf7a 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_CLKBLD_I8253) += i8253.o obj-$(CONFIG_CLKSRC_MMIO) += mmio.o obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o +obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c new file mode 100644 index 000000000000..a35993bafb20 --- /dev/null +++ b/drivers/clocksource/rockchip_timer.c @@ -0,0 +1,180 @@ +/* + * Rockchip timer support + * + * Copyright (C) Daniel Lezcano + * + * 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 +#include +#include +#include +#include +#include +#include + +#define TIMER_NAME "rk_timer" + +#define TIMER_LOAD_COUNT0 0x00 +#define TIMER_LOAD_COUNT1 0x04 +#define TIMER_CONTROL_REG 0x10 +#define TIMER_INT_STATUS 0x18 + +#define TIMER_DISABLE 0x0 +#define TIMER_ENABLE 0x1 +#define TIMER_MODE_FREE_RUNNING (0 << 1) +#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1) +#define TIMER_INT_UNMASK (1 << 2) + +struct bc_timer { + struct clock_event_device ce; + void __iomem *base; + u32 freq; +}; + +static struct bc_timer bc_timer; + +static inline struct bc_timer *rk_timer(struct clock_event_device *ce) +{ + return container_of(ce, struct bc_timer, ce); +} + +static inline void __iomem *rk_base(struct clock_event_device *ce) +{ + return rk_timer(ce)->base; +} + +static inline void rk_timer_disable(struct clock_event_device *ce) +{ + writel_relaxed(TIMER_DISABLE, rk_base(ce) + TIMER_CONTROL_REG); + dsb(); +} + +static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags) +{ + writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags, + rk_base(ce) + TIMER_CONTROL_REG); + dsb(); +} + +static void rk_timer_update_counter(unsigned long cycles, + struct clock_event_device *ce) +{ + writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0); + writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1); + dsb(); +} + +static void rk_timer_interrupt_clear(struct clock_event_device *ce) +{ + writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS); + dsb(); +} + +static inline int rk_timer_set_next_event(unsigned long cycles, + struct clock_event_device *ce) +{ + rk_timer_disable(ce); + rk_timer_update_counter(cycles, ce); + rk_timer_enable(ce, TIMER_MODE_USER_DEFINED_COUNT); + return 0; +} + +static inline void rk_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *ce) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + rk_timer_disable(ce); + rk_timer_update_counter(rk_timer(ce)->freq / HZ - 1, ce); + rk_timer_enable(ce, TIMER_MODE_FREE_RUNNING); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_RESUME: + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + rk_timer_disable(ce); + break; + } +} + +static irqreturn_t rk_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *ce = dev_id; + + rk_timer_interrupt_clear(ce); + + if (ce->mode == CLOCK_EVT_MODE_ONESHOT) + rk_timer_disable(ce); + + ce->event_handler(ce); + + return IRQ_HANDLED; +} + +static void __init rk_timer_init(struct device_node *np) +{ + struct clock_event_device *ce = &bc_timer.ce; + struct clk *timer_clk; + struct clk *pclk; + int ret, irq; + + bc_timer.base = of_iomap(np, 0); + if (!bc_timer.base) { + pr_err("Failed to get base address for '%s'\n", TIMER_NAME); + return; + } + + pclk = of_clk_get_by_name(np, "pclk"); + if (IS_ERR(pclk)) { + pr_err("Failed to get pclk for '%s'\n", TIMER_NAME); + return; + } + + if (clk_prepare_enable(pclk)) { + pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME); + return; + } + + timer_clk = of_clk_get_by_name(np, "timer"); + if (IS_ERR(timer_clk)) { + pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME); + return; + } + + if (clk_prepare_enable(timer_clk)) { + pr_err("Failed to enable timer clock\n"); + return; + } + + bc_timer.freq = clk_get_rate(timer_clk); + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME); + return; + } + + ce->name = TIMER_NAME; + ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + ce->set_next_event = rk_timer_set_next_event; + ce->set_mode = rk_timer_set_mode; + ce->irq = irq; + ce->cpumask = cpumask_of(0); + ce->rating = 250; + + rk_timer_interrupt_clear(ce); + rk_timer_disable(ce); + + ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce); + if (ret) { + pr_err("Failed to initialize '%s': %d\n", TIMER_NAME, ret); + return; + } + + clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX); +} +CLOCKSOURCE_OF_DECLARE(rk_timer, "rockchip,rk3288-timer", rk_timer_init); From 9ff99be7dc69a56ec16d5a928a4e7622023abda5 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 26 Jan 2015 20:35:17 +0200 Subject: [PATCH 5/9] clocksource: devicetree: Document Conexant Digicolor timer binding The Conexant CX92755 SoC provides 8 32-bit timers as part of its so called "Agent Communication" block. Timers can be configures either as free running or one shot. Each timer has a dedicated interrupt source in the CX92755 interrupts controller. The first timer (Timer A) can also be configured as watchdog. This commit adds devicetree binding definition of this hardware module. The binding defined here should be reusable for other SoCs in the Digicolor series. Signed-off-by: Baruch Siach Signed-off-by: Daniel Lezcano --- .../bindings/timer/digicolor-timer.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/digicolor-timer.txt diff --git a/Documentation/devicetree/bindings/timer/digicolor-timer.txt b/Documentation/devicetree/bindings/timer/digicolor-timer.txt new file mode 100644 index 000000000000..d1b659bbc29f --- /dev/null +++ b/Documentation/devicetree/bindings/timer/digicolor-timer.txt @@ -0,0 +1,18 @@ +Conexant Digicolor SoCs Timer Controller + +Required properties: + +- compatible : should be "cnxt,cx92755-timer" +- reg : Specifies base physical address and size of the "Agent Communication" + timer registers +- interrupts : Contains 8 interrupts, one for each timer +- clocks: phandle to the main clock + +Example: + + timer@f0000fc0 { + compatible = "cnxt,cx92755-timer"; + reg = <0xf0000fc0 0x40>; + interrupts = <19>, <31>, <34>, <35>, <52>, <53>, <54>, <55>; + clocks = <&main_clk>; + }; From 9b8bb7736b78e954044e12b94a2b16aafb0ee0e3 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 26 Jan 2015 20:35:18 +0200 Subject: [PATCH 6/9] clocksource: Driver for Conexant Digicolor SoC timer Add clocksource driver to the Conexant CX92755 SoC, part of the Digicolor SoCs series. Hardware provides 8 timers, A to H. Timer A is dedicated to a future watchdog driver so we don't use it here. Use timer B for sched_clock, and timer C for clock_event. Signed-off-by: Baruch Siach Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-digicolor.c | 199 ++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 drivers/clocksource/timer-digicolor.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index a89120bf640c..72a38273f7f5 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -18,6 +18,9 @@ config CLKBLD_I8253 config CLKSRC_MMIO bool +config DIGICOLOR_TIMER + bool + config DW_APB_TIMER bool diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 21c11e28cf7a..0f8003d76c32 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o obj-$(CONFIG_EM_TIMER_STI) += em_sti.o obj-$(CONFIG_CLKBLD_I8253) += i8253.o obj-$(CONFIG_CLKSRC_MMIO) += mmio.o +obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c new file mode 100644 index 000000000000..7f8388cfa810 --- /dev/null +++ b/drivers/clocksource/timer-digicolor.c @@ -0,0 +1,199 @@ +/* + * Conexant Digicolor timer driver + * + * Author: Baruch Siach + * + * Copyright (C) 2014 Paradox Innovation Ltd. + * + * Based on: + * Allwinner SoCs hstimer driver + * + * Copyright (C) 2013 Maxime Ripard + * + * Maxime Ripard + * + * 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. + */ + +/* + * Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to + * "Timer H". Timer A is the only one with watchdog support, so it is dedicated + * to the watchdog driver. This driver uses Timer B for sched_clock(), and + * Timer C for clockevents. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + TIMER_A, + TIMER_B, + TIMER_C, + TIMER_D, + TIMER_E, + TIMER_F, + TIMER_G, + TIMER_H, +}; + +#define CONTROL(t) ((t)*8) +#define COUNT(t) ((t)*8 + 4) + +#define CONTROL_DISABLE 0 +#define CONTROL_ENABLE BIT(0) +#define CONTROL_MODE(m) ((m) << 4) +#define CONTROL_MODE_ONESHOT CONTROL_MODE(1) +#define CONTROL_MODE_PERIODIC CONTROL_MODE(2) + +struct digicolor_timer { + struct clock_event_device ce; + void __iomem *base; + u32 ticks_per_jiffy; + int timer_id; /* one of TIMER_* */ +}; + +struct digicolor_timer *dc_timer(struct clock_event_device *ce) +{ + return container_of(ce, struct digicolor_timer, ce); +} + +static inline void dc_timer_disable(struct clock_event_device *ce) +{ + struct digicolor_timer *dt = dc_timer(ce); + writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id)); +} + +static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode) +{ + struct digicolor_timer *dt = dc_timer(ce); + writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id)); +} + +static inline void dc_timer_set_count(struct clock_event_device *ce, + unsigned long count) +{ + struct digicolor_timer *dt = dc_timer(ce); + writel(count, dt->base + COUNT(dt->timer_id)); +} + +static void digicolor_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *ce) +{ + struct digicolor_timer *dt = dc_timer(ce); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + dc_timer_disable(ce); + dc_timer_set_count(ce, dt->ticks_per_jiffy); + dc_timer_enable(ce, CONTROL_MODE_PERIODIC); + break; + case CLOCK_EVT_MODE_ONESHOT: + dc_timer_disable(ce); + dc_timer_enable(ce, CONTROL_MODE_ONESHOT); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + dc_timer_disable(ce); + break; + } +} + +static int digicolor_clkevt_next_event(unsigned long evt, + struct clock_event_device *ce) +{ + dc_timer_disable(ce); + dc_timer_set_count(ce, evt); + dc_timer_enable(ce, CONTROL_MODE_ONESHOT); + + return 0; +} + +static struct digicolor_timer dc_timer_dev = { + .ce = { + .name = "digicolor_tick", + .rating = 340, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = digicolor_clkevt_mode, + .set_next_event = digicolor_clkevt_next_event, + }, + .timer_id = TIMER_C, +}; + +static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static u64 digicolor_timer_sched_read(void) +{ + return ~readl(dc_timer_dev.base + COUNT(TIMER_B)); +} + +static void __init digicolor_timer_init(struct device_node *node) +{ + unsigned long rate; + struct clk *clk; + int ret, irq; + + /* + * timer registers are shared with the watchdog timer; + * don't map exclusively + */ + dc_timer_dev.base = of_iomap(node, 0); + if (!dc_timer_dev.base) { + pr_err("Can't map registers"); + return; + } + + irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id); + if (irq <= 0) { + pr_err("Can't parse IRQ"); + return; + } + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) { + pr_err("Can't get timer clock"); + return; + } + clk_prepare_enable(clk); + rate = clk_get_rate(clk); + dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); + + writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B)); + writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B)); + writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B)); + + sched_clock_register(digicolor_timer_sched_read, 32, rate); + clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name, + rate, 340, 32, clocksource_mmio_readl_down); + + ret = request_irq(irq, digicolor_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC", + &dc_timer_dev.ce); + if (ret) + pr_warn("request of timer irq %d failed (%d)\n", irq, ret); + + dc_timer_dev.ce.cpumask = cpu_possible_mask; + dc_timer_dev.ce.irq = irq; + + clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff); +} +CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer", + digicolor_timer_init); From 1a18554b969861aae99e3b002b96d26b2233463d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Jan 2015 11:56:30 -0600 Subject: [PATCH 7/9] dt/bindings: Add binding for Versatile system registers Add binding for Versatile board system registers found in the FPGA of the Versatile/AB and Versatile/PB boards. Signed-off-by: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: devicetree@vger.kernel.org Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/arm/versatile-sysreg.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/versatile-sysreg.txt diff --git a/Documentation/devicetree/bindings/arm/versatile-sysreg.txt b/Documentation/devicetree/bindings/arm/versatile-sysreg.txt new file mode 100644 index 000000000000..a4f15262d717 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/versatile-sysreg.txt @@ -0,0 +1,10 @@ +ARM Versatile system registers +-------------------------------------- + +This is a system control registers block, providing multiple low level +platform functions like board detection and identification, software +interrupt generation, MMC and NOR Flash control etc. + +Required node properties: +- compatible value : = "arm,versatile-sysreg", "syscon" +- reg : physical base address and the size of the registers window From f2fa0299d34236d7e6ed213a28dca380b8c68ac0 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Jan 2015 11:56:31 -0600 Subject: [PATCH 8/9] clocksource: versatile: Adapt for Versatile AB and PB boards The same 24MHz counter is also present on Versatile AB and PB boards, so add the compatible string for them. Signed-off-by: Rob Herring Acked-by: Liviu Dudau Cc: Liviu Dudau Cc: Sudeep Holla Cc: Lorenzo Pieralisi Cc: Daniel Lezcano Cc: Thomas Gleixner Signed-off-by: Daniel Lezcano --- drivers/clocksource/versatile.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c index 2798e7492234..0a26d3dde6c0 100644 --- a/drivers/clocksource/versatile.c +++ b/drivers/clocksource/versatile.c @@ -36,5 +36,7 @@ static void __init versatile_sched_clock_init(struct device_node *node) sched_clock_register(versatile_sys_24mhz_read, 32, 24000000); } -CLOCKSOURCE_OF_DECLARE(versatile, "arm,vexpress-sysreg", +CLOCKSOURCE_OF_DECLARE(vexpress, "arm,vexpress-sysreg", + versatile_sched_clock_init); +CLOCKSOURCE_OF_DECLARE(versatile, "arm,versatile-sysreg", versatile_sched_clock_init); From 35a8578e8b83eb13f8d57ab40b98bcfd5199d3d4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Jan 2015 11:56:32 -0600 Subject: [PATCH 9/9] dts: versatile: Add sysregs node The Versatile boards have the same sysregs as other ARM Ltd boards. Add the nodes in order to enable support for 24MHz counter as sched_clock. This is a minimal node definition as the existing sub node definition used on VExpress has some issues raised by Linus W. Signed-off-by: Rob Herring Cc: Russell King Cc: Linus Walleij Cc: devicetree@vger.kernel.org Signed-off-by: Daniel Lezcano --- arch/arm/boot/dts/versatile-ab.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts index 27d0d9c8adf3..01f40197ea13 100644 --- a/arch/arm/boot/dts/versatile-ab.dts +++ b/arch/arm/boot/dts/versatile-ab.dts @@ -252,6 +252,11 @@ fpga { #size-cells = <1>; ranges = <0 0x10000000 0x10000>; + sysreg@0 { + compatible = "arm,versatile-sysreg", "syscon"; + reg = <0x00000 0x1000>; + }; + aaci@4000 { compatible = "arm,primecell"; reg = <0x4000 0x1000>;