Message ID | 1364219596-4954-3-git-send-email-michal.simek@xilinx.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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@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. > 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... > + > + 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. > + > +} > + > +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. > + /* > + * 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. Regards, Steffen
2013/3/25 Steffen Trumtrar <s.trumtrar@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@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
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 + * + * 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);
Move zynq timer out of mach folder to generic location. Signed-off-by: Michal Simek <michal.simek@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. --- 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