linux/drivers/clocksource/nomadik-mtu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2008 STMicroelectronics
   4 * Copyright (C) 2010 Alessandro Rubini
   5 * Copyright (C) 2010 Linus Walleij for ST-Ericsson
   6 */
   7#include <linux/init.h>
   8#include <linux/interrupt.h>
   9#include <linux/irq.h>
  10#include <linux/io.h>
  11#include <linux/clockchips.h>
  12#include <linux/clocksource.h>
  13#include <linux/of_address.h>
  14#include <linux/of_irq.h>
  15#include <linux/of_platform.h>
  16#include <linux/clk.h>
  17#include <linux/jiffies.h>
  18#include <linux/delay.h>
  19#include <linux/err.h>
  20#include <linux/sched_clock.h>
  21#include <asm/mach/time.h>
  22
  23/*
  24 * The MTU device hosts four different counters, with 4 set of
  25 * registers. These are register names.
  26 */
  27
  28#define MTU_IMSC        0x00    /* Interrupt mask set/clear */
  29#define MTU_RIS         0x04    /* Raw interrupt status */
  30#define MTU_MIS         0x08    /* Masked interrupt status */
  31#define MTU_ICR         0x0C    /* Interrupt clear register */
  32
  33/* per-timer registers take 0..3 as argument */
  34#define MTU_LR(x)       (0x10 + 0x10 * (x) + 0x00)      /* Load value */
  35#define MTU_VAL(x)      (0x10 + 0x10 * (x) + 0x04)      /* Current value */
  36#define MTU_CR(x)       (0x10 + 0x10 * (x) + 0x08)      /* Control reg */
  37#define MTU_BGLR(x)     (0x10 + 0x10 * (x) + 0x0c)      /* At next overflow */
  38
  39/* bits for the control register */
  40#define MTU_CRn_ENA             0x80
  41#define MTU_CRn_PERIODIC        0x40    /* if 0 = free-running */
  42#define MTU_CRn_PRESCALE_MASK   0x0c
  43#define MTU_CRn_PRESCALE_1              0x00
  44#define MTU_CRn_PRESCALE_16             0x04
  45#define MTU_CRn_PRESCALE_256            0x08
  46#define MTU_CRn_32BITS          0x02
  47#define MTU_CRn_ONESHOT         0x01    /* if 0 = wraps reloading from BGLR*/
  48
  49/* Other registers are usual amba/primecell registers, currently not used */
  50#define MTU_ITCR        0xff0
  51#define MTU_ITOP        0xff4
  52
  53#define MTU_PERIPH_ID0  0xfe0
  54#define MTU_PERIPH_ID1  0xfe4
  55#define MTU_PERIPH_ID2  0xfe8
  56#define MTU_PERIPH_ID3  0xfeC
  57
  58#define MTU_PCELL0      0xff0
  59#define MTU_PCELL1      0xff4
  60#define MTU_PCELL2      0xff8
  61#define MTU_PCELL3      0xffC
  62
  63static void __iomem *mtu_base;
  64static bool clkevt_periodic;
  65static u32 clk_prescale;
  66static u32 nmdk_cycle;          /* write-once */
  67static struct delay_timer mtu_delay_timer;
  68
  69/*
  70 * Override the global weak sched_clock symbol with this
  71 * local implementation which uses the clocksource to get some
  72 * better resolution when scheduling the kernel.
  73 */
  74static u64 notrace nomadik_read_sched_clock(void)
  75{
  76        if (unlikely(!mtu_base))
  77                return 0;
  78
  79        return -readl(mtu_base + MTU_VAL(0));
  80}
  81
  82static unsigned long nmdk_timer_read_current_timer(void)
  83{
  84        return ~readl_relaxed(mtu_base + MTU_VAL(0));
  85}
  86
  87/* Clockevent device: use one-shot mode */
  88static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
  89{
  90        writel(1 << 1, mtu_base + MTU_IMSC);
  91        writel(evt, mtu_base + MTU_LR(1));
  92        /* Load highest value, enable device, enable interrupts */
  93        writel(MTU_CRn_ONESHOT | clk_prescale |
  94               MTU_CRn_32BITS | MTU_CRn_ENA,
  95               mtu_base + MTU_CR(1));
  96
  97        return 0;
  98}
  99
 100static void nmdk_clkevt_reset(void)
 101{
 102        if (clkevt_periodic) {
 103                /* Timer: configure load and background-load, and fire it up */
 104                writel(nmdk_cycle, mtu_base + MTU_LR(1));
 105                writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
 106
 107                writel(MTU_CRn_PERIODIC | clk_prescale |
 108                       MTU_CRn_32BITS | MTU_CRn_ENA,
 109                       mtu_base + MTU_CR(1));
 110                writel(1 << 1, mtu_base + MTU_IMSC);
 111        } else {
 112                /* Generate an interrupt to start the clockevent again */
 113                (void) nmdk_clkevt_next(nmdk_cycle, NULL);
 114        }
 115}
 116
 117static int nmdk_clkevt_shutdown(struct clock_event_device *evt)
 118{
 119        writel(0, mtu_base + MTU_IMSC);
 120        /* disable timer */
 121        writel(0, mtu_base + MTU_CR(1));
 122        /* load some high default value */
 123        writel(0xffffffff, mtu_base + MTU_LR(1));
 124        return 0;
 125}
 126
 127static int nmdk_clkevt_set_oneshot(struct clock_event_device *evt)
 128{
 129        clkevt_periodic = false;
 130        return 0;
 131}
 132
 133static int nmdk_clkevt_set_periodic(struct clock_event_device *evt)
 134{
 135        clkevt_periodic = true;
 136        nmdk_clkevt_reset();
 137        return 0;
 138}
 139
 140static void nmdk_clksrc_reset(void)
 141{
 142        /* Disable */
 143        writel(0, mtu_base + MTU_CR(0));
 144
 145        /* ClockSource: configure load and background-load, and fire it up */
 146        writel(nmdk_cycle, mtu_base + MTU_LR(0));
 147        writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
 148
 149        writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
 150               mtu_base + MTU_CR(0));
 151}
 152
 153static void nmdk_clkevt_resume(struct clock_event_device *cedev)
 154{
 155        nmdk_clkevt_reset();
 156        nmdk_clksrc_reset();
 157}
 158
 159static struct clock_event_device nmdk_clkevt = {
 160        .name                   = "mtu_1",
 161        .features               = CLOCK_EVT_FEAT_ONESHOT |
 162                                  CLOCK_EVT_FEAT_PERIODIC |
 163                                  CLOCK_EVT_FEAT_DYNIRQ,
 164        .rating                 = 200,
 165        .set_state_shutdown     = nmdk_clkevt_shutdown,
 166        .set_state_periodic     = nmdk_clkevt_set_periodic,
 167        .set_state_oneshot      = nmdk_clkevt_set_oneshot,
 168        .set_next_event         = nmdk_clkevt_next,
 169        .resume                 = nmdk_clkevt_resume,
 170};
 171
 172/*
 173 * IRQ Handler for timer 1 of the MTU block.
 174 */
 175static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
 176{
 177        struct clock_event_device *evdev = dev_id;
 178
 179        writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
 180        evdev->event_handler(evdev);
 181        return IRQ_HANDLED;
 182}
 183
 184static int __init nmdk_timer_init(void __iomem *base, int irq,
 185                                   struct clk *pclk, struct clk *clk)
 186{
 187        unsigned long rate;
 188        int ret;
 189        int min_ticks;
 190
 191        mtu_base = base;
 192
 193        BUG_ON(clk_prepare_enable(pclk));
 194        BUG_ON(clk_prepare_enable(clk));
 195
 196        /*
 197         * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
 198         * for ux500, and in one specific Ux500 case 32768 Hz.
 199         *
 200         * Use a divide-by-16 counter if the tick rate is more than 32MHz.
 201         * At 32 MHz, the timer (with 32 bit counter) can be programmed
 202         * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
 203         * with 16 gives too low timer resolution.
 204         */
 205        rate = clk_get_rate(clk);
 206        if (rate > 32000000) {
 207                rate /= 16;
 208                clk_prescale = MTU_CRn_PRESCALE_16;
 209        } else {
 210                clk_prescale = MTU_CRn_PRESCALE_1;
 211        }
 212
 213        /* Cycles for periodic mode */
 214        nmdk_cycle = DIV_ROUND_CLOSEST(rate, HZ);
 215
 216
 217        /* Timer 0 is the free running clocksource */
 218        nmdk_clksrc_reset();
 219
 220        ret = clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
 221                                    rate, 200, 32, clocksource_mmio_readl_down);
 222        if (ret) {
 223                pr_err("timer: failed to initialize clock source %s\n", "mtu_0");
 224                return ret;
 225        }
 226
 227        sched_clock_register(nomadik_read_sched_clock, 32, rate);
 228
 229        /* Timer 1 is used for events, register irq and clockevents */
 230        if (request_irq(irq, nmdk_timer_interrupt, IRQF_TIMER,
 231                        "Nomadik Timer Tick", &nmdk_clkevt))
 232                pr_err("%s: request_irq() failed\n", "Nomadik Timer Tick");
 233        nmdk_clkevt.cpumask = cpumask_of(0);
 234        nmdk_clkevt.irq = irq;
 235        if (rate < 100000)
 236                min_ticks = 5;
 237        else
 238                min_ticks = 2;
 239        clockevents_config_and_register(&nmdk_clkevt, rate, min_ticks,
 240                                        0xffffffffU);
 241
 242        mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer;
 243        mtu_delay_timer.freq = rate;
 244        register_current_timer_delay(&mtu_delay_timer);
 245
 246        return 0;
 247}
 248
 249static int __init nmdk_timer_of_init(struct device_node *node)
 250{
 251        struct clk *pclk;
 252        struct clk *clk;
 253        void __iomem *base;
 254        int irq;
 255
 256        base = of_iomap(node, 0);
 257        if (!base) {
 258                pr_err("Can't remap registers\n");
 259                return -ENXIO;
 260        }
 261
 262        pclk = of_clk_get_by_name(node, "apb_pclk");
 263        if (IS_ERR(pclk)) {
 264                pr_err("could not get apb_pclk\n");
 265                return PTR_ERR(pclk);
 266        }
 267
 268        clk = of_clk_get_by_name(node, "timclk");
 269        if (IS_ERR(clk)) {
 270                pr_err("could not get timclk\n");
 271                return PTR_ERR(clk);
 272        }
 273
 274        irq = irq_of_parse_and_map(node, 0);
 275        if (irq <= 0) {
 276                pr_err("Can't parse IRQ\n");
 277                return -EINVAL;
 278        }
 279
 280        return nmdk_timer_init(base, irq, pclk, clk);
 281}
 282TIMER_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
 283                       nmdk_timer_of_init);
 284