/* * 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 #include #include #include #include #include #include #include #include #include #include "common.h" #define IRQ_TIMERCOUNTER0 42 /* * This driver configures the 2 16-bit count-up timers as follows: * * T1: Timer 1, clocksource for generic timekeeping * T2: Timer 2, clockevent source for hrtimers * T3: Timer 3, * * The input frequency to the timer module for emulation is 2.5MHz which is * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, * the timers are clocked at 78.125KHz (12.8 us resolution). * * The input frequency to the timer module in silicon will be 200MHz. With the * pre-scaler of 32, the timers are clocked at 6.25MHz (160ns resolution). */ #define XTTCPSS_CLOCKSOURCE 0 /* Timer 1 as a generic timekeeping */ #define XTTCPSS_CLOCKEVENT 1 /* Timer 2 as a clock event */ #define XTTCPSS_TIMER_BASE TTC0_BASE #define XTTCPCC_EVENT_TIMER_IRQ (IRQ_TIMERCOUNTER0 + 1) /* * Timer Register Offset Definitions of Timer 1, Increment base address by 4 * and use same offsets for Timer 2 */ #define XTTCPSS_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */ #define XTTCPSS_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ #define XTTCPSS_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ #define XTTCPSS_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */ #define XTTCPSS_MATCH_1_OFFSET 0x30 /* Match 1 Value Reg, RW */ #define XTTCPSS_MATCH_2_OFFSET 0x3C /* Match 2 Value Reg, RW */ #define XTTCPSS_MATCH_3_OFFSET 0x48 /* Match 3 Value Reg, RW */ #define XTTCPSS_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ #define XTTCPSS_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ #define XTTCPSS_CNT_CNTRL_DISABLE_MASK 0x1 /* Setup the timers to use pre-scaling */ #define TIMER_RATE (PERIPHERAL_CLOCK_RATE / 32) /** * struct xttcpss_timer - This definition defines local timer structure * * @base_addr: Base address of timer **/ struct xttcpss_timer { void __iomem *base_addr; }; static struct xttcpss_timer timers[2]; static struct clock_event_device xttcpss_clockevent; /** * xttcpss_set_interval - Set the timer interval value * * @timer: Pointer to the timer instance * @cycles: Timer interval ticks **/ static void xttcpss_set_interval(struct xttcpss_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 + XTTCPSS_CNT_CNTRL_OFFSET); ctrl_reg |= XTTCPSS_CNT_CNTRL_DISABLE_MASK; __raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); __raw_writel(cycles, timer->base_addr + XTTCPSS_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 |= 0x10; ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK; __raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); } /** * xttcpss_clock_event_interrupt - Clock event timer interrupt handler * * @irq: IRQ number of the Timer * @dev_id: void pointer to the xttcpss_timer instance * * returns: Always IRQ_HANDLED - success **/ static irqreturn_t xttcpss_clock_event_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = &xttcpss_clockevent; struct xttcpss_timer *timer = dev_id; /* Acknowledge the interrupt and call event handler */ __raw_writel(__raw_readl(timer->base_addr + XTTCPSS_ISR_OFFSET), timer->base_addr + XTTCPSS_ISR_OFFSET); evt->event_handler(evt); return IRQ_HANDLED; } static struct irqaction event_timer_irq = { .name = "xttcpss clockevent", .flags = IRQF_DISABLED | IRQF_TIMER, .handler = xttcpss_clock_event_interrupt, }; /** * xttcpss_timer_hardware_init - Initialize the timer hardware * * Initialize the hardware to start the clock source, get the clock * event timer ready to use, and hook up the interrupt. **/ static void __init xttcpss_timer_hardware_init(void) { /* 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. */ timers[XTTCPSS_CLOCKSOURCE].base_addr = XTTCPSS_TIMER_BASE; __raw_writel(0x0, timers[XTTCPSS_CLOCKSOURCE].base_addr + XTTCPSS_IER_OFFSET); __raw_writel(0x9, timers[XTTCPSS_CLOCKSOURCE].base_addr + XTTCPSS_CLK_CNTRL_OFFSET); __raw_writel(0x10, timers[XTTCPSS_CLOCKSOURCE].base_addr + XTTCPSS_CNT_CNTRL_OFFSET); /* Setup the clock event timer to be an interval timer which * is prescaled by 32 using the interval interrupt. Leave it * disabled for now. */ timers[XTTCPSS_CLOCKEVENT].base_addr = XTTCPSS_TIMER_BASE + 4; __raw_writel(0x23, timers[XTTCPSS_CLOCKEVENT].base_addr + XTTCPSS_CNT_CNTRL_OFFSET); __raw_writel(0x9, timers[XTTCPSS_CLOCKEVENT].base_addr + XTTCPSS_CLK_CNTRL_OFFSET); __raw_writel(0x1, timers[XTTCPSS_CLOCKEVENT].base_addr + XTTCPSS_IER_OFFSET); /* Setup IRQ the clock event timer */ event_timer_irq.dev_id = &timers[XTTCPSS_CLOCKEVENT]; setup_irq(XTTCPCC_EVENT_TIMER_IRQ, &event_timer_irq); } /** * __raw_readl_cycles - Reads the timer counter register * * returns: Current timer counter register value **/ static cycle_t __raw_readl_cycles(struct clocksource *cs) { struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKSOURCE]; return (cycle_t)__raw_readl(timer->base_addr + XTTCPSS_COUNT_VAL_OFFSET); } /* * Instantiate and initialize the clock source structure */ static struct clocksource clocksource_xttcpss = { .name = "xttcpss_timer1", .rating = 200, /* Reasonable clock source */ .read = __raw_readl_cycles, .mask = CLOCKSOURCE_MASK(16), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; /** * xttcpss_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 xttcpss_set_next_event(unsigned long cycles, struct clock_event_device *evt) { struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT]; xttcpss_set_interval(timer, cycles); return 0; } /** * xttcpss_set_mode - Sets the mode of timer * * @mode: Mode to be set * @evt: Address of clock event instance **/ static void xttcpss_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT]; u32 ctrl_reg; switch (mode) { case CLOCK_EVT_MODE_PERIODIC: xttcpss_set_interval(timer, TIMER_RATE / HZ); break; case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: ctrl_reg = __raw_readl(timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); ctrl_reg |= XTTCPSS_CNT_CNTRL_DISABLE_MASK; __raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); break; case CLOCK_EVT_MODE_RESUME: ctrl_reg = __raw_readl(timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK; __raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); break; } } /* * Instantiate and initialize the clock event structure */ static struct clock_event_device xttcpss_clockevent = { .name = "xttcpss_timer2", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .set_next_event = xttcpss_set_next_event, .set_mode = xttcpss_set_mode, .rating = 200, }; /** * xttcpss_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 xttcpss_timer_init(void) { xttcpss_timer_hardware_init(); clocksource_register_hz(&clocksource_xttcpss, TIMER_RATE); /* Calculate the parameters to allow the clockevent to operate using integer math */ clockevents_calc_mult_shift(&xttcpss_clockevent, TIMER_RATE, 4); xttcpss_clockevent.max_delta_ns = clockevent_delta2ns(0xfffe, &xttcpss_clockevent); xttcpss_clockevent.min_delta_ns = clockevent_delta2ns(1, &xttcpss_clockevent); /* Indicate that clock event is on 1st CPU as SMP boot needs it */ xttcpss_clockevent.cpumask = cpumask_of(0); clockevents_register_device(&xttcpss_clockevent); } /* * Instantiate and initialize the system timer structure */ struct sys_timer xttcpss_sys_timer = { .init = xttcpss_timer_init, };