[PATCH 03/10] arm: zynq: Move timer to generic location
Michal Simek
monstr at monstr.eu
Mon Mar 25 12:24:52 EDT 2013
2013/3/25 Steffen Trumtrar <s.trumtrar at pengutronix.de>:
> Hi Michal!
>
> On Mon, Mar 25, 2013 at 02:53:09PM +0100, Michal Simek wrote:
>> Move zynq timer out of mach folder to generic location.
>>
>> Signed-off-by: Michal Simek <michal.simek at xilinx.com>
>> ---
>> Arnd: This is the last patch as we discuss about moving
>> this timer to generic location.
>>
>> It depends on ARCH_ZYNQ as Tegra and others.
>> It should be ok to use it or we can use different name if you like.
>> ---
>
> Moving out of mach-zynq is good, because this is a
> Cadence Rev06 Triple Timer Counter (see Zynq-7000 TRM page 25)
> Which leads me to a proposed rename to: cadence_timer.c or something like that.
> And the fn-names should be changed accordingly while at it.
Agree. Let me do one middle patch which will just remove all zynq reference
before we can move it to generic location.
>> arch/arm/mach-zynq/Makefile | 2 +-
>> arch/arm/mach-zynq/timer.c | 430 --------------------------------------
>> drivers/clocksource/Makefile | 1 +
>> drivers/clocksource/zynq_timer.c | 430 ++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 432 insertions(+), 431 deletions(-)
>> delete mode 100644 arch/arm/mach-zynq/timer.c
>> create mode 100644 drivers/clocksource/zynq_timer.c
>>
>> 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 3b1faed..0000000
>> --- a/arch/arm/mach-zynq/timer.c
>> +++ /dev/null
>> @@ -1,430 +0,0 @@
>> -/*
>> - * This file contains driver for the Xilinx PS Timer Counter IP.
>> - *
>> - * Copyright (C) 2011 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 <linux/clk.h>
>> -#include <linux/interrupt.h>
>> -#include <linux/clockchips.h>
>> -#include <linux/of_address.h>
>> -#include <linux/of_irq.h>
>> -#include <linux/slab.h>
>> -#include <linux/clk-provider.h>
>> -
>> -/*
>> - * 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, <unused>
>> - *
>> - * 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 necessare 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 zynq_ttc_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))
>> - 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))
>> - 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 zynq_ttc_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))
>> - 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))
>> - 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();
>> - }
>> -
>> - zynq_ttc_setup_clocksource(clk, timer_baseaddr);
>> - zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
>> -
>> - pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
>> -}
>> -
>> -CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
>> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
>> index 4d8283a..665af7f 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_ARCH_ZYNQ) += zynq_timer.o
>>
>> obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
>> obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
>> diff --git a/drivers/clocksource/zynq_timer.c b/drivers/clocksource/zynq_timer.c
>> new file mode 100644
>> index 0000000..3b1faed
>> --- /dev/null
>> +++ b/drivers/clocksource/zynq_timer.c
>> @@ -0,0 +1,430 @@
>> +/*
>> + * This file contains driver for the Xilinx PS Timer Counter IP.
>> + *
>> + * Copyright (C) 2011 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 <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/clockchips.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/slab.h>
>> +#include <linux/clk-provider.h>
>> +
>> +/*
>> + * 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, <unused>
>> + *
>> + * 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 necessare to maintain a proper time base
> ------------------------------------------^ little typo
>
>> + *
>> + * 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 zynq_ttc_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))
>> + return;
>
> I think you are losing memory here...
right. Let's free that memory. I have pinged Soren to look if we can handle
clock better.
Till now this is only one timer in the system we have used.
Look below.
>
>> +
>> + 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))
>> + return;
> ... and here and below in zynq_ttc_setup_clockevent the same.
yep
>> +
>> +}
>> +
>> +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 zynq_ttc_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))
>> + 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))
>> + 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;
>> +
> ??? This looks very strange ???
> I think this should be handled in a different manner.
Feel free to propose better solution. Because there are two ttc in the
system and the first
is taken, second shouldn't be initialized as clocksource or clockevent.
>> + /*
>> + * 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();
>> + }
>> +
>> + zynq_ttc_setup_clocksource(clk, timer_baseaddr);
>> + zynq_ttc_setup_clockevent(clk, timer_baseaddr + 4, irq);
>> +
>> + pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
>> +}
>> +
>> +CLOCKSOURCE_OF_DECLARE(zynq, "xlnx,ttc", xttcps_timer_init);
>
> What if do not want to use the ttc as clocksource and therefore don't want it
> enabled? I still have the smp_twd which I might prefere.
Renaming this to cadence name we will have to change dt name to more generic
one cdns,ttc? Also we should probably add it to Kconfig.
IRC: If there is not compatible string kernel will try to find out
different one.
If you find some of them the more precise one is used. It is quite long time
I was playing with it.
Thanks,
Michal
--
Michal Simek, Ing. (M.Eng)
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform
More information about the linux-arm-kernel
mailing list