123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /*
- * Keystone broadcast clock-event
- *
- * Copyright 2013 Texas Instruments, Inc.
- *
- * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
- #include <linux/clk.h>
- #include <linux/clockchips.h>
- #include <linux/clocksource.h>
- #include <linux/interrupt.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #define TIMER_NAME "timer-keystone"
- /* Timer register offsets */
- #define TIM12 0x10
- #define TIM34 0x14
- #define PRD12 0x18
- #define PRD34 0x1c
- #define TCR 0x20
- #define TGCR 0x24
- #define INTCTLSTAT 0x44
- /* Timer register bitfields */
- #define TCR_ENAMODE_MASK 0xC0
- #define TCR_ENAMODE_ONESHOT_MASK 0x40
- #define TCR_ENAMODE_PERIODIC_MASK 0x80
- #define TGCR_TIM_UNRESET_MASK 0x03
- #define INTCTLSTAT_ENINT_MASK 0x01
- /**
- * struct keystone_timer: holds timer's data
- * @base: timer memory base address
- * @hz_period: cycles per HZ period
- * @event_dev: event device based on timer
- */
- static struct keystone_timer {
- void __iomem *base;
- unsigned long hz_period;
- struct clock_event_device event_dev;
- } timer;
- static inline u32 keystone_timer_readl(unsigned long rg)
- {
- return readl_relaxed(timer.base + rg);
- }
- static inline void keystone_timer_writel(u32 val, unsigned long rg)
- {
- writel_relaxed(val, timer.base + rg);
- }
- /**
- * keystone_timer_barrier: write memory barrier
- * use explicit barrier to avoid using readl/writel non relaxed function
- * variants, because in our case non relaxed variants hide the true places
- * where barrier is needed.
- */
- static inline void keystone_timer_barrier(void)
- {
- __iowmb();
- }
- /**
- * keystone_timer_config: configures timer to work in oneshot/periodic modes.
- * @ mask: mask of the mode to configure
- * @ period: cycles number to configure for
- */
- static int keystone_timer_config(u64 period, int mask)
- {
- u32 tcr;
- u32 off;
- tcr = keystone_timer_readl(TCR);
- off = tcr & ~(TCR_ENAMODE_MASK);
- /* set enable mode */
- tcr |= mask;
- /* disable timer */
- keystone_timer_writel(off, TCR);
- /* here we have to be sure the timer has been disabled */
- keystone_timer_barrier();
- /* reset counter to zero, set new period */
- keystone_timer_writel(0, TIM12);
- keystone_timer_writel(0, TIM34);
- keystone_timer_writel(period & 0xffffffff, PRD12);
- keystone_timer_writel(period >> 32, PRD34);
- /*
- * enable timer
- * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers
- * have been written.
- */
- keystone_timer_barrier();
- keystone_timer_writel(tcr, TCR);
- return 0;
- }
- static void keystone_timer_disable(void)
- {
- u32 tcr;
- tcr = keystone_timer_readl(TCR);
- /* disable timer */
- tcr &= ~(TCR_ENAMODE_MASK);
- keystone_timer_writel(tcr, TCR);
- }
- static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id)
- {
- struct clock_event_device *evt = dev_id;
- evt->event_handler(evt);
- return IRQ_HANDLED;
- }
- static int keystone_set_next_event(unsigned long cycles,
- struct clock_event_device *evt)
- {
- return keystone_timer_config(cycles, TCR_ENAMODE_ONESHOT_MASK);
- }
- static int keystone_shutdown(struct clock_event_device *evt)
- {
- keystone_timer_disable();
- return 0;
- }
- static int keystone_set_periodic(struct clock_event_device *evt)
- {
- keystone_timer_config(timer.hz_period, TCR_ENAMODE_PERIODIC_MASK);
- return 0;
- }
- static int __init keystone_timer_init(struct device_node *np)
- {
- struct clock_event_device *event_dev = &timer.event_dev;
- unsigned long rate;
- struct clk *clk;
- int irq, error;
- irq = irq_of_parse_and_map(np, 0);
- if (!irq) {
- pr_err("%s: failed to map interrupts\n", __func__);
- return -EINVAL;
- }
- timer.base = of_iomap(np, 0);
- if (!timer.base) {
- pr_err("%s: failed to map registers\n", __func__);
- return -ENXIO;
- }
- clk = of_clk_get(np, 0);
- if (IS_ERR(clk)) {
- pr_err("%s: failed to get clock\n", __func__);
- iounmap(timer.base);
- return PTR_ERR(clk);
- }
- error = clk_prepare_enable(clk);
- if (error) {
- pr_err("%s: failed to enable clock\n", __func__);
- goto err;
- }
- rate = clk_get_rate(clk);
- /* disable, use internal clock source */
- keystone_timer_writel(0, TCR);
- /* here we have to be sure the timer has been disabled */
- keystone_timer_barrier();
- /* reset timer as 64-bit, no pre-scaler, plus features are disabled */
- keystone_timer_writel(0, TGCR);
- /* unreset timer */
- keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR);
- /* init counter to zero */
- keystone_timer_writel(0, TIM12);
- keystone_timer_writel(0, TIM34);
- timer.hz_period = DIV_ROUND_UP(rate, HZ);
- /* enable timer interrupts */
- keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT);
- error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER,
- TIMER_NAME, event_dev);
- if (error) {
- pr_err("%s: failed to setup irq\n", __func__);
- goto err;
- }
- /* setup clockevent */
- event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
- event_dev->set_next_event = keystone_set_next_event;
- event_dev->set_state_shutdown = keystone_shutdown;
- event_dev->set_state_periodic = keystone_set_periodic;
- event_dev->set_state_oneshot = keystone_shutdown;
- event_dev->cpumask = cpu_all_mask;
- event_dev->owner = THIS_MODULE;
- event_dev->name = TIMER_NAME;
- event_dev->irq = irq;
- clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX);
- pr_info("keystone timer clock @%lu Hz\n", rate);
- return 0;
- err:
- clk_put(clk);
- iounmap(timer.base);
- return error;
- }
- CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer",
- keystone_timer_init);
|