From patchwork Tue Mar 26 17:43:35 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 2342571 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id D8EBF3FD40 for ; Tue, 26 Mar 2013 17:52:48 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UKXzd-0002Zr-8B; Tue, 26 Mar 2013 17:49:05 +0000 Received: from mail-we0-x233.google.com ([2a00:1450:400c:c03::233]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UKXuf-000057-WA for linux-arm-kernel@lists.infradead.org; Tue, 26 Mar 2013 17:44:17 +0000 Received: by mail-we0-f179.google.com with SMTP id p43so876654wea.38 for ; Tue, 26 Mar 2013 10:43:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:sender:from:to:subject:date:message-id:x-mailer :in-reply-to:references:in-reply-to:references:x-gm-message-state; bh=Gol6ii/ghVsyUm1sc6MRU6LfrXIVvxMBbtobJM1+Amg=; b=m7mJDeP+mZpAnQsdeTbWZJpFTonVAxmpxCpCcukvMupdcoM9MRYQhRrsvB2+OgMWRM oCF6115r6qtS+IZ5J7xjdOtLhms0JFWfflgfJnpRI0CgHPIcbFckq+ewfX+uwh3kvZEf dJ9dn1ITGwIVudECbf3lmuRWtMjtGVO9asExBTHERUOE9I08PxkdO9XWl7IAu9Fft0Ab mIcHdOyhd8pwW3/4x0f3ANOMNXy151PIFbJ8FUP8XXCvWaclqsh+vMvy0wvgJDZYp47E bkhuVj+0GGEBqjvxjiiL67FWYkLSFQkBiVmtjYQX7QC8NWGZZ31+37MI59KFT6upCW1U rfgQ== X-Received: by 10.194.63.109 with SMTP id f13mr26554125wjs.11.1364319835651; Tue, 26 Mar 2013 10:43:55 -0700 (PDT) Received: from localhost (nat-63.starnet.cz. [178.255.168.63]) by mx.google.com with ESMTPS id fp2sm4253500wib.7.2013.03.26.10.43.54 (version=TLSv1.1 cipher=RC4-SHA bits=128/128); Tue, 26 Mar 2013 10:43:54 -0700 (PDT) From: Michal Simek To: linux-arm-kernel@lists.infradead.org, monstr@monstr.eu, Josh Cartwright , Steffen Trumtrar , Rob Herring Subject: [PATCH v2 03/10] arm: zynq: Move timer to generic location Date: Tue, 26 Mar 2013 18:43:35 +0100 Message-Id: <70f6127314dc19991d011349da9fd77dad880849.1364319776.git.michal.simek@xilinx.com> X-Mailer: git-send-email 1.7.9.7 In-Reply-To: <1364319822-5504-1-git-send-email-michal.simek@xilinx.com> References: <1364319822-5504-1-git-send-email-michal.simek@xilinx.com> In-Reply-To: References: X-Gm-Message-State: ALoCoQnm/chqrHJFWfuzq0mfpSX4mg09l6yh6XY98zsinN/WRb583Bgd3Rya4IyIfkNKP2CZ0jq+ X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130326_134358_492483_B8DB7D5F X-CRM114-Status: GOOD ( 26.81 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Move zynq timer out of mach folder to generic location and enable it. Signed-off-by: Michal Simek --- v2: Add binding documentation Rename zynq_timer.c to cadence_ttc_timer.c Use CADENCE_TTC_TIMER to be able to enable this driver for others Arnd: This is the last patch as we discuss about moving this timer to generic location. --- .../bindings/timer/cadence,ttc-timer.txt | 17 + arch/arm/mach-zynq/Kconfig | 1 + arch/arm/mach-zynq/Makefile | 2 +- arch/arm/mach-zynq/timer.c | 436 -------------------- drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/cadence_ttc_timer.c | 436 ++++++++++++++++++++ 7 files changed, 459 insertions(+), 437 deletions(-) create mode 100644 Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt delete mode 100644 arch/arm/mach-zynq/timer.c create mode 100644 drivers/clocksource/cadence_ttc_timer.c diff --git a/Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt b/Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt new file mode 100644 index 0000000..993695c --- /dev/null +++ b/Documentation/devicetree/bindings/timer/cadence,ttc-timer.txt @@ -0,0 +1,17 @@ +Cadence TTC - Triple Timer Counter + +Required properties: +- compatible : Should be "cdns,ttc". +- reg : Specifies base physical address and size of the registers. +- interrupts : A list of 3 interrupts; one per timer channel. +- clocks: phandle to the source clock + +Example: + +ttc0: ttc0@f8001000 { + interrupt-parent = <&intc>; + interrupts = < 0 10 4 0 11 4 0 12 4 >; + compatible = "cdns,ttc"; + reg = <0xF8001000 0x1000>; + clocks = <&cpu_clk 3>; +}; diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig index adb6c0e..d70651e 100644 --- a/arch/arm/mach-zynq/Kconfig +++ b/arch/arm/mach-zynq/Kconfig @@ -9,5 +9,6 @@ config ARCH_ZYNQ select MIGHT_HAVE_CACHE_L2X0 select USE_OF select SPARSE_IRQ + select CADENCE_TTC_TIMER help Support for Xilinx Zynq ARM Cortex A9 Platform diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile index 397268c..320faed 100644 --- a/arch/arm/mach-zynq/Makefile +++ b/arch/arm/mach-zynq/Makefile @@ -3,4 +3,4 @@ # # Common support -obj-y := common.o timer.o +obj-y := common.o diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c deleted file mode 100644 index ab5b839..0000000 --- a/arch/arm/mach-zynq/timer.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * This file contains driver for the Xilinx PS Timer Counter IP. - * - * Copyright (C) 2011-2013 Xilinx - * - * based on arch/mips/kernel/time.c timer driver - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * This driver configures the 2 16-bit count-up timers as follows: - * - * T1: Timer 1, clocksource for generic timekeeping - * T2: Timer 2, clockevent source for hrtimers - * T3: Timer 3, - * - * The input frequency to the timer module for emulation is 2.5MHz which is - * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, - * the timers are clocked at 78.125KHz (12.8 us resolution). - - * The input frequency to the timer module in silicon is configurable and - * obtained from device tree. The pre-scaler of 32 is used. - */ - -/* - * Timer Register Offset Definitions of Timer 1, Increment base address by 4 - * and use same offsets for Timer 2 - */ -#define XTTCPS_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */ -#define XTTCPS_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ -#define XTTCPS_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ -#define XTTCPS_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */ -#define XTTCPS_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ -#define XTTCPS_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ - -#define XTTCPS_CNT_CNTRL_DISABLE_MASK 0x1 - -/* - * Setup the timers to use pre-scaling, using a fixed value for now that will - * work across most input frequency, but it may need to be more dynamic - */ -#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */ -#define PRESCALE 2048 /* The exponent must match this */ -#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) -#define CLK_CNTRL_PRESCALE_EN 1 -#define CNT_CNTRL_RESET (1 << 4) - -/** - * struct xttcps_timer - This definition defines local timer structure - * - * @base_addr: Base address of timer - * @clk: Associated clock source - * @clk_rate_change_nb Notifier block for clock rate changes - */ -struct xttcps_timer { - void __iomem *base_addr; - struct clk *clk; - struct notifier_block clk_rate_change_nb; -}; - -#define to_xttcps_timer(x) \ - container_of(x, struct xttcps_timer, clk_rate_change_nb) - -struct xttcps_timer_clocksource { - struct xttcps_timer xttc; - struct clocksource cs; -}; - -#define to_xttcps_timer_clksrc(x) \ - container_of(x, struct xttcps_timer_clocksource, cs) - -struct xttcps_timer_clockevent { - struct xttcps_timer xttc; - struct clock_event_device ce; -}; - -#define to_xttcps_timer_clkevent(x) \ - container_of(x, struct xttcps_timer_clockevent, ce) - -/** - * xttcps_set_interval - Set the timer interval value - * - * @timer: Pointer to the timer instance - * @cycles: Timer interval ticks - **/ -static void xttcps_set_interval(struct xttcps_timer *timer, - unsigned long cycles) -{ - u32 ctrl_reg; - - /* Disable the counter, set the counter value and re-enable counter */ - ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); - ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); - - __raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET); - - /* - * Reset the counter (0x10) so that it starts from 0, one-shot - * mode makes this needed for timing to be right. - */ - ctrl_reg |= CNT_CNTRL_RESET; - ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); -} - -/** - * xttcps_clock_event_interrupt - Clock event timer interrupt handler - * - * @irq: IRQ number of the Timer - * @dev_id: void pointer to the xttcps_timer instance - * - * returns: Always IRQ_HANDLED - success - **/ -static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id) -{ - struct xttcps_timer_clockevent *xttce = dev_id; - struct xttcps_timer *timer = &xttce->xttc; - - /* Acknowledge the interrupt and call event handler */ - __raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET); - - xttce->ce.event_handler(&xttce->ce); - - return IRQ_HANDLED; -} - -/** - * __xttc_clocksource_read - Reads the timer counter register - * - * returns: Current timer counter register value - **/ -static cycle_t __xttc_clocksource_read(struct clocksource *cs) -{ - struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc; - - return (cycle_t)__raw_readl(timer->base_addr + - XTTCPS_COUNT_VAL_OFFSET); -} - -/** - * xttcps_set_next_event - Sets the time interval for next event - * - * @cycles: Timer interval ticks - * @evt: Address of clock event instance - * - * returns: Always 0 - success - **/ -static int xttcps_set_next_event(unsigned long cycles, - struct clock_event_device *evt) -{ - struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt); - struct xttcps_timer *timer = &xttce->xttc; - - xttcps_set_interval(timer, cycles); - return 0; -} - -/** - * xttcps_set_mode - Sets the mode of timer - * - * @mode: Mode to be set - * @evt: Address of clock event instance - **/ -static void xttcps_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) -{ - struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt); - struct xttcps_timer *timer = &xttce->xttc; - u32 ctrl_reg; - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - xttcps_set_interval(timer, - DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk), - PRESCALE * HZ)); - break; - case CLOCK_EVT_MODE_ONESHOT: - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_SHUTDOWN: - ctrl_reg = __raw_readl(timer->base_addr + - XTTCPS_CNT_CNTRL_OFFSET); - ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); - break; - case CLOCK_EVT_MODE_RESUME: - ctrl_reg = __raw_readl(timer->base_addr + - XTTCPS_CNT_CNTRL_OFFSET); - ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); - break; - } -} - -static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct clk_notifier_data *ndata = data; - struct xttcps_timer *xttcps = to_xttcps_timer(nb); - struct xttcps_timer_clocksource *xttccs = container_of(xttcps, - struct xttcps_timer_clocksource, xttc); - - switch (event) { - case POST_RATE_CHANGE: - /* - * Do whatever is necessary to maintain a proper time base - * - * I cannot find a way to adjust the currently used clocksource - * to the new frequency. __clocksource_updatefreq_hz() sounds - * good, but does not work. Not sure what's that missing. - * - * This approach works, but triggers two clocksource switches. - * The first after unregister to clocksource jiffies. And - * another one after the register to the newly registered timer. - * - * Alternatively we could 'waste' another HW timer to ping pong - * between clock sources. That would also use one register and - * one unregister call, but only trigger one clocksource switch - * for the cost of another HW timer used by the OS. - */ - clocksource_unregister(&xttccs->cs); - clocksource_register_hz(&xttccs->cs, - ndata->new_rate / PRESCALE); - /* fall through */ - case PRE_RATE_CHANGE: - case ABORT_RATE_CHANGE: - default: - return NOTIFY_DONE; - } -} - -static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base) -{ - struct xttcps_timer_clocksource *ttccs; - int err; - - ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); - if (WARN_ON(!ttccs)) - return; - - ttccs->xttc.clk = clk; - - err = clk_prepare_enable(ttccs->xttc.clk); - if (WARN_ON(err)) { - kfree(ttccs); - return; - } - - ttccs->xttc.clk_rate_change_nb.notifier_call = - xttcps_rate_change_clocksource_cb; - ttccs->xttc.clk_rate_change_nb.next = NULL; - if (clk_notifier_register(ttccs->xttc.clk, - &ttccs->xttc.clk_rate_change_nb)) - pr_warn("Unable to register clock notifier.\n"); - - ttccs->xttc.base_addr = base; - ttccs->cs.name = "xttcps_clocksource"; - ttccs->cs.rating = 200; - ttccs->cs.read = __xttc_clocksource_read; - ttccs->cs.mask = CLOCKSOURCE_MASK(16); - ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; - - /* - * Setup the clock source counter to be an incrementing counter - * with no interrupt and it rolls over at 0xFFFF. Pre-scale - * it by 32 also. Let it start running now. - */ - __raw_writel(0x0, ttccs->xttc.base_addr + XTTCPS_IER_OFFSET); - __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, - ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); - __raw_writel(CNT_CNTRL_RESET, - ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); - - err = clocksource_register_hz(&ttccs->cs, - clk_get_rate(ttccs->xttc.clk) / PRESCALE); - if (WARN_ON(err)) { - kfree(ttccs); - return; - } -} - -static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct clk_notifier_data *ndata = data; - struct xttcps_timer *xttcps = to_xttcps_timer(nb); - struct xttcps_timer_clockevent *xttcce = container_of(xttcps, - struct xttcps_timer_clockevent, xttc); - - switch (event) { - case POST_RATE_CHANGE: - { - unsigned long flags; - - /* - * clockevents_update_freq should be called with IRQ disabled on - * the CPU the timer provides events for. The timer we use is - * common to both CPUs, not sure if we need to run on both - * cores. - */ - local_irq_save(flags); - clockevents_update_freq(&xttcce->ce, - ndata->new_rate / PRESCALE); - local_irq_restore(flags); - - /* fall through */ - } - case PRE_RATE_CHANGE: - case ABORT_RATE_CHANGE: - default: - return NOTIFY_DONE; - } -} - -static void __init xttc_setup_clockevent(struct clk *clk, - void __iomem *base, u32 irq) -{ - struct xttcps_timer_clockevent *ttcce; - int err; - - ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); - if (WARN_ON(!ttcce)) - return; - - ttcce->xttc.clk = clk; - - err = clk_prepare_enable(ttcce->xttc.clk); - if (WARN_ON(err)) { - kfree(ttcce); - return; - } - - ttcce->xttc.clk_rate_change_nb.notifier_call = - xttcps_rate_change_clockevent_cb; - ttcce->xttc.clk_rate_change_nb.next = NULL; - if (clk_notifier_register(ttcce->xttc.clk, - &ttcce->xttc.clk_rate_change_nb)) - pr_warn("Unable to register clock notifier.\n"); - - ttcce->xttc.base_addr = base; - ttcce->ce.name = "xttcps_clockevent"; - ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; - ttcce->ce.set_next_event = xttcps_set_next_event; - ttcce->ce.set_mode = xttcps_set_mode; - ttcce->ce.rating = 200; - ttcce->ce.irq = irq; - ttcce->ce.cpumask = cpu_possible_mask; - - /* - * Setup the clock event timer to be an interval timer which - * is prescaled by 32 using the interval interrupt. Leave it - * disabled for now. - */ - __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); - __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, - ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); - __raw_writel(0x1, ttcce->xttc.base_addr + XTTCPS_IER_OFFSET); - - err = request_irq(irq, xttcps_clock_event_interrupt, - IRQF_DISABLED | IRQF_TIMER, - ttcce->ce.name, ttcce); - if (WARN_ON(err)) { - kfree(ttcce); - return; - } - - clockevents_config_and_register(&ttcce->ce, - clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe); -} - -/** - * xttcps_timer_init - Initialize the timer - * - * Initializes the timer hardware and register the clock source and clock event - * timers with Linux kernal timer framework - */ -static void __init xttcps_timer_init(struct device_node *timer) -{ - unsigned int irq; - void __iomem *timer_baseaddr; - struct clk *clk; - static int initialized; - - if (initialized) - return; - - initialized = 1; - - /* - * Get the 1st Triple Timer Counter (TTC) block from the device tree - * and use it. Note that the event timer uses the interrupt and it's the - * 2nd TTC hence the irq_of_parse_and_map(,1) - */ - timer_baseaddr = of_iomap(timer, 0); - if (!timer_baseaddr) { - pr_err("ERROR: invalid timer base address\n"); - BUG(); - } - - irq = irq_of_parse_and_map(timer, 1); - if (irq <= 0) { - pr_err("ERROR: invalid interrupt number\n"); - BUG(); - } - - clk = of_clk_get_by_name(timer, "cpu_1x"); - if (IS_ERR(clk)) { - pr_err("ERROR: timer input clock not found\n"); - BUG(); - } - - xttc_setup_clocksource(clk, timer_baseaddr); - xttc_setup_clockevent(clk, timer_baseaddr + 4, irq); - - pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); -} - -CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", xttcps_timer_init); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7..3167fda 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -31,6 +31,9 @@ config SUNXI_TIMER config VT8500_TIMER bool +config CADENCE_TTC_TIMER + bool + config CLKSRC_NOMADIK_MTU bool depends on (ARCH_NOMADIK || ARCH_U8500) diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283a..e74c8ce 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o +obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c new file mode 100644 index 0000000..ab5b839 --- /dev/null +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -0,0 +1,436 @@ +/* + * This file contains driver for the Xilinx PS Timer Counter IP. + * + * Copyright (C) 2011-2013 Xilinx + * + * based on arch/mips/kernel/time.c timer driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This driver configures the 2 16-bit count-up timers as follows: + * + * T1: Timer 1, clocksource for generic timekeeping + * T2: Timer 2, clockevent source for hrtimers + * T3: Timer 3, + * + * The input frequency to the timer module for emulation is 2.5MHz which is + * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, + * the timers are clocked at 78.125KHz (12.8 us resolution). + + * The input frequency to the timer module in silicon is configurable and + * obtained from device tree. The pre-scaler of 32 is used. + */ + +/* + * Timer Register Offset Definitions of Timer 1, Increment base address by 4 + * and use same offsets for Timer 2 + */ +#define XTTCPS_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */ +#define XTTCPS_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ +#define XTTCPS_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ +#define XTTCPS_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */ +#define XTTCPS_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ +#define XTTCPS_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ + +#define XTTCPS_CNT_CNTRL_DISABLE_MASK 0x1 + +/* + * Setup the timers to use pre-scaling, using a fixed value for now that will + * work across most input frequency, but it may need to be more dynamic + */ +#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */ +#define PRESCALE 2048 /* The exponent must match this */ +#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) +#define CLK_CNTRL_PRESCALE_EN 1 +#define CNT_CNTRL_RESET (1 << 4) + +/** + * struct xttcps_timer - This definition defines local timer structure + * + * @base_addr: Base address of timer + * @clk: Associated clock source + * @clk_rate_change_nb Notifier block for clock rate changes + */ +struct xttcps_timer { + void __iomem *base_addr; + struct clk *clk; + struct notifier_block clk_rate_change_nb; +}; + +#define to_xttcps_timer(x) \ + container_of(x, struct xttcps_timer, clk_rate_change_nb) + +struct xttcps_timer_clocksource { + struct xttcps_timer xttc; + struct clocksource cs; +}; + +#define to_xttcps_timer_clksrc(x) \ + container_of(x, struct xttcps_timer_clocksource, cs) + +struct xttcps_timer_clockevent { + struct xttcps_timer xttc; + struct clock_event_device ce; +}; + +#define to_xttcps_timer_clkevent(x) \ + container_of(x, struct xttcps_timer_clockevent, ce) + +/** + * xttcps_set_interval - Set the timer interval value + * + * @timer: Pointer to the timer instance + * @cycles: Timer interval ticks + **/ +static void xttcps_set_interval(struct xttcps_timer *timer, + unsigned long cycles) +{ + u32 ctrl_reg; + + /* Disable the counter, set the counter value and re-enable counter */ + ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); + ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); + + __raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET); + + /* + * Reset the counter (0x10) so that it starts from 0, one-shot + * mode makes this needed for timing to be right. + */ + ctrl_reg |= CNT_CNTRL_RESET; + ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); +} + +/** + * xttcps_clock_event_interrupt - Clock event timer interrupt handler + * + * @irq: IRQ number of the Timer + * @dev_id: void pointer to the xttcps_timer instance + * + * returns: Always IRQ_HANDLED - success + **/ +static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id) +{ + struct xttcps_timer_clockevent *xttce = dev_id; + struct xttcps_timer *timer = &xttce->xttc; + + /* Acknowledge the interrupt and call event handler */ + __raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET); + + xttce->ce.event_handler(&xttce->ce); + + return IRQ_HANDLED; +} + +/** + * __xttc_clocksource_read - Reads the timer counter register + * + * returns: Current timer counter register value + **/ +static cycle_t __xttc_clocksource_read(struct clocksource *cs) +{ + struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc; + + return (cycle_t)__raw_readl(timer->base_addr + + XTTCPS_COUNT_VAL_OFFSET); +} + +/** + * xttcps_set_next_event - Sets the time interval for next event + * + * @cycles: Timer interval ticks + * @evt: Address of clock event instance + * + * returns: Always 0 - success + **/ +static int xttcps_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt); + struct xttcps_timer *timer = &xttce->xttc; + + xttcps_set_interval(timer, cycles); + return 0; +} + +/** + * xttcps_set_mode - Sets the mode of timer + * + * @mode: Mode to be set + * @evt: Address of clock event instance + **/ +static void xttcps_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt); + struct xttcps_timer *timer = &xttce->xttc; + u32 ctrl_reg; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + xttcps_set_interval(timer, + DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk), + PRESCALE * HZ)); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + ctrl_reg = __raw_readl(timer->base_addr + + XTTCPS_CNT_CNTRL_OFFSET); + ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, + timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); + break; + case CLOCK_EVT_MODE_RESUME: + ctrl_reg = __raw_readl(timer->base_addr + + XTTCPS_CNT_CNTRL_OFFSET); + ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK; + __raw_writel(ctrl_reg, + timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET); + break; + } +} + +static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct xttcps_timer *xttcps = to_xttcps_timer(nb); + struct xttcps_timer_clocksource *xttccs = container_of(xttcps, + struct xttcps_timer_clocksource, xttc); + + switch (event) { + case POST_RATE_CHANGE: + /* + * Do whatever is necessary to maintain a proper time base + * + * I cannot find a way to adjust the currently used clocksource + * to the new frequency. __clocksource_updatefreq_hz() sounds + * good, but does not work. Not sure what's that missing. + * + * This approach works, but triggers two clocksource switches. + * The first after unregister to clocksource jiffies. And + * another one after the register to the newly registered timer. + * + * Alternatively we could 'waste' another HW timer to ping pong + * between clock sources. That would also use one register and + * one unregister call, but only trigger one clocksource switch + * for the cost of another HW timer used by the OS. + */ + clocksource_unregister(&xttccs->cs); + clocksource_register_hz(&xttccs->cs, + ndata->new_rate / PRESCALE); + /* fall through */ + case PRE_RATE_CHANGE: + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base) +{ + struct xttcps_timer_clocksource *ttccs; + int err; + + ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); + if (WARN_ON(!ttccs)) + return; + + ttccs->xttc.clk = clk; + + err = clk_prepare_enable(ttccs->xttc.clk); + if (WARN_ON(err)) { + kfree(ttccs); + return; + } + + ttccs->xttc.clk_rate_change_nb.notifier_call = + xttcps_rate_change_clocksource_cb; + ttccs->xttc.clk_rate_change_nb.next = NULL; + if (clk_notifier_register(ttccs->xttc.clk, + &ttccs->xttc.clk_rate_change_nb)) + pr_warn("Unable to register clock notifier.\n"); + + ttccs->xttc.base_addr = base; + ttccs->cs.name = "xttcps_clocksource"; + ttccs->cs.rating = 200; + ttccs->cs.read = __xttc_clocksource_read; + ttccs->cs.mask = CLOCKSOURCE_MASK(16); + ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + /* + * Setup the clock source counter to be an incrementing counter + * with no interrupt and it rolls over at 0xFFFF. Pre-scale + * it by 32 also. Let it start running now. + */ + __raw_writel(0x0, ttccs->xttc.base_addr + XTTCPS_IER_OFFSET); + __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, + ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); + __raw_writel(CNT_CNTRL_RESET, + ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); + + err = clocksource_register_hz(&ttccs->cs, + clk_get_rate(ttccs->xttc.clk) / PRESCALE); + if (WARN_ON(err)) { + kfree(ttccs); + return; + } +} + +static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct xttcps_timer *xttcps = to_xttcps_timer(nb); + struct xttcps_timer_clockevent *xttcce = container_of(xttcps, + struct xttcps_timer_clockevent, xttc); + + switch (event) { + case POST_RATE_CHANGE: + { + unsigned long flags; + + /* + * clockevents_update_freq should be called with IRQ disabled on + * the CPU the timer provides events for. The timer we use is + * common to both CPUs, not sure if we need to run on both + * cores. + */ + local_irq_save(flags); + clockevents_update_freq(&xttcce->ce, + ndata->new_rate / PRESCALE); + local_irq_restore(flags); + + /* fall through */ + } + case PRE_RATE_CHANGE: + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +static void __init xttc_setup_clockevent(struct clk *clk, + void __iomem *base, u32 irq) +{ + struct xttcps_timer_clockevent *ttcce; + int err; + + ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); + if (WARN_ON(!ttcce)) + return; + + ttcce->xttc.clk = clk; + + err = clk_prepare_enable(ttcce->xttc.clk); + if (WARN_ON(err)) { + kfree(ttcce); + return; + } + + ttcce->xttc.clk_rate_change_nb.notifier_call = + xttcps_rate_change_clockevent_cb; + ttcce->xttc.clk_rate_change_nb.next = NULL; + if (clk_notifier_register(ttcce->xttc.clk, + &ttcce->xttc.clk_rate_change_nb)) + pr_warn("Unable to register clock notifier.\n"); + + ttcce->xttc.base_addr = base; + ttcce->ce.name = "xttcps_clockevent"; + ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + ttcce->ce.set_next_event = xttcps_set_next_event; + ttcce->ce.set_mode = xttcps_set_mode; + ttcce->ce.rating = 200; + ttcce->ce.irq = irq; + ttcce->ce.cpumask = cpu_possible_mask; + + /* + * Setup the clock event timer to be an interval timer which + * is prescaled by 32 using the interval interrupt. Leave it + * disabled for now. + */ + __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); + __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, + ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); + __raw_writel(0x1, ttcce->xttc.base_addr + XTTCPS_IER_OFFSET); + + err = request_irq(irq, xttcps_clock_event_interrupt, + IRQF_DISABLED | IRQF_TIMER, + ttcce->ce.name, ttcce); + if (WARN_ON(err)) { + kfree(ttcce); + return; + } + + clockevents_config_and_register(&ttcce->ce, + clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe); +} + +/** + * xttcps_timer_init - Initialize the timer + * + * Initializes the timer hardware and register the clock source and clock event + * timers with Linux kernal timer framework + */ +static void __init xttcps_timer_init(struct device_node *timer) +{ + unsigned int irq; + void __iomem *timer_baseaddr; + struct clk *clk; + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * Get the 1st Triple Timer Counter (TTC) block from the device tree + * and use it. Note that the event timer uses the interrupt and it's the + * 2nd TTC hence the irq_of_parse_and_map(,1) + */ + timer_baseaddr = of_iomap(timer, 0); + if (!timer_baseaddr) { + pr_err("ERROR: invalid timer base address\n"); + BUG(); + } + + irq = irq_of_parse_and_map(timer, 1); + if (irq <= 0) { + pr_err("ERROR: invalid interrupt number\n"); + BUG(); + } + + clk = of_clk_get_by_name(timer, "cpu_1x"); + if (IS_ERR(clk)) { + pr_err("ERROR: timer input clock not found\n"); + BUG(); + } + + xttc_setup_clocksource(clk, timer_baseaddr); + xttc_setup_clockevent(clk, timer_baseaddr + 4, irq); + + pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); +} + +CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", xttcps_timer_init);