root/drivers/clocksource/timer-fsl-ftm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftm_readl
  2. ftm_writel
  3. ftm_counter_enable
  4. ftm_counter_disable
  5. ftm_irq_acknowledge
  6. ftm_irq_enable
  7. ftm_irq_disable
  8. ftm_reset_counter
  9. ftm_read_sched_clock
  10. ftm_set_next_event
  11. ftm_set_oneshot
  12. ftm_set_periodic
  13. ftm_evt_interrupt
  14. ftm_clockevent_init
  15. ftm_clocksource_init
  16. __ftm_clk_init
  17. ftm_clk_init
  18. ftm_calc_closest_round_cyc
  19. ftm_timer_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Freescale FlexTimer Module (FTM) timer driver.
   4  *
   5  * Copyright 2014 Freescale Semiconductor, Inc.
   6  */
   7 
   8 #include <linux/clk.h>
   9 #include <linux/clockchips.h>
  10 #include <linux/clocksource.h>
  11 #include <linux/err.h>
  12 #include <linux/interrupt.h>
  13 #include <linux/io.h>
  14 #include <linux/of_address.h>
  15 #include <linux/of_irq.h>
  16 #include <linux/sched_clock.h>
  17 #include <linux/slab.h>
  18 #include <linux/fsl/ftm.h>
  19 
  20 #define FTM_SC_CLK(c)   ((c) << FTM_SC_CLK_MASK_SHIFT)
  21 
  22 struct ftm_clock_device {
  23         void __iomem *clksrc_base;
  24         void __iomem *clkevt_base;
  25         unsigned long periodic_cyc;
  26         unsigned long ps;
  27         bool big_endian;
  28 };
  29 
  30 static struct ftm_clock_device *priv;
  31 
  32 static inline u32 ftm_readl(void __iomem *addr)
  33 {
  34         if (priv->big_endian)
  35                 return ioread32be(addr);
  36         else
  37                 return ioread32(addr);
  38 }
  39 
  40 static inline void ftm_writel(u32 val, void __iomem *addr)
  41 {
  42         if (priv->big_endian)
  43                 iowrite32be(val, addr);
  44         else
  45                 iowrite32(val, addr);
  46 }
  47 
  48 static inline void ftm_counter_enable(void __iomem *base)
  49 {
  50         u32 val;
  51 
  52         /* select and enable counter clock source */
  53         val = ftm_readl(base + FTM_SC);
  54         val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  55         val |= priv->ps | FTM_SC_CLK(1);
  56         ftm_writel(val, base + FTM_SC);
  57 }
  58 
  59 static inline void ftm_counter_disable(void __iomem *base)
  60 {
  61         u32 val;
  62 
  63         /* disable counter clock source */
  64         val = ftm_readl(base + FTM_SC);
  65         val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  66         ftm_writel(val, base + FTM_SC);
  67 }
  68 
  69 static inline void ftm_irq_acknowledge(void __iomem *base)
  70 {
  71         u32 val;
  72 
  73         val = ftm_readl(base + FTM_SC);
  74         val &= ~FTM_SC_TOF;
  75         ftm_writel(val, base + FTM_SC);
  76 }
  77 
  78 static inline void ftm_irq_enable(void __iomem *base)
  79 {
  80         u32 val;
  81 
  82         val = ftm_readl(base + FTM_SC);
  83         val |= FTM_SC_TOIE;
  84         ftm_writel(val, base + FTM_SC);
  85 }
  86 
  87 static inline void ftm_irq_disable(void __iomem *base)
  88 {
  89         u32 val;
  90 
  91         val = ftm_readl(base + FTM_SC);
  92         val &= ~FTM_SC_TOIE;
  93         ftm_writel(val, base + FTM_SC);
  94 }
  95 
  96 static inline void ftm_reset_counter(void __iomem *base)
  97 {
  98         /*
  99          * The CNT register contains the FTM counter value.
 100          * Reset clears the CNT register. Writing any value to COUNT
 101          * updates the counter with its initial value, CNTIN.
 102          */
 103         ftm_writel(0x00, base + FTM_CNT);
 104 }
 105 
 106 static u64 notrace ftm_read_sched_clock(void)
 107 {
 108         return ftm_readl(priv->clksrc_base + FTM_CNT);
 109 }
 110 
 111 static int ftm_set_next_event(unsigned long delta,
 112                                 struct clock_event_device *unused)
 113 {
 114         /*
 115          * The CNNIN and MOD are all double buffer registers, writing
 116          * to the MOD register latches the value into a buffer. The MOD
 117          * register is updated with the value of its write buffer with
 118          * the following scenario:
 119          * a, the counter source clock is diabled.
 120          */
 121         ftm_counter_disable(priv->clkevt_base);
 122 
 123         /* Force the value of CNTIN to be loaded into the FTM counter */
 124         ftm_reset_counter(priv->clkevt_base);
 125 
 126         /*
 127          * The counter increments until the value of MOD is reached,
 128          * at which point the counter is reloaded with the value of CNTIN.
 129          * The TOF (the overflow flag) bit is set when the FTM counter
 130          * changes from MOD to CNTIN. So we should using the delta - 1.
 131          */
 132         ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
 133 
 134         ftm_counter_enable(priv->clkevt_base);
 135 
 136         ftm_irq_enable(priv->clkevt_base);
 137 
 138         return 0;
 139 }
 140 
 141 static int ftm_set_oneshot(struct clock_event_device *evt)
 142 {
 143         ftm_counter_disable(priv->clkevt_base);
 144         return 0;
 145 }
 146 
 147 static int ftm_set_periodic(struct clock_event_device *evt)
 148 {
 149         ftm_set_next_event(priv->periodic_cyc, evt);
 150         return 0;
 151 }
 152 
 153 static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
 154 {
 155         struct clock_event_device *evt = dev_id;
 156 
 157         ftm_irq_acknowledge(priv->clkevt_base);
 158 
 159         if (likely(clockevent_state_oneshot(evt))) {
 160                 ftm_irq_disable(priv->clkevt_base);
 161                 ftm_counter_disable(priv->clkevt_base);
 162         }
 163 
 164         evt->event_handler(evt);
 165 
 166         return IRQ_HANDLED;
 167 }
 168 
 169 static struct clock_event_device ftm_clockevent = {
 170         .name                   = "Freescale ftm timer",
 171         .features               = CLOCK_EVT_FEAT_PERIODIC |
 172                                   CLOCK_EVT_FEAT_ONESHOT,
 173         .set_state_periodic     = ftm_set_periodic,
 174         .set_state_oneshot      = ftm_set_oneshot,
 175         .set_next_event         = ftm_set_next_event,
 176         .rating                 = 300,
 177 };
 178 
 179 static struct irqaction ftm_timer_irq = {
 180         .name           = "Freescale ftm timer",
 181         .flags          = IRQF_TIMER | IRQF_IRQPOLL,
 182         .handler        = ftm_evt_interrupt,
 183         .dev_id         = &ftm_clockevent,
 184 };
 185 
 186 static int __init ftm_clockevent_init(unsigned long freq, int irq)
 187 {
 188         int err;
 189 
 190         ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
 191         ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
 192 
 193         ftm_reset_counter(priv->clkevt_base);
 194 
 195         err = setup_irq(irq, &ftm_timer_irq);
 196         if (err) {
 197                 pr_err("ftm: setup irq failed: %d\n", err);
 198                 return err;
 199         }
 200 
 201         ftm_clockevent.cpumask = cpumask_of(0);
 202         ftm_clockevent.irq = irq;
 203 
 204         clockevents_config_and_register(&ftm_clockevent,
 205                                         freq / (1 << priv->ps),
 206                                         1, 0xffff);
 207 
 208         ftm_counter_enable(priv->clkevt_base);
 209 
 210         return 0;
 211 }
 212 
 213 static int __init ftm_clocksource_init(unsigned long freq)
 214 {
 215         int err;
 216 
 217         ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
 218         ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
 219 
 220         ftm_reset_counter(priv->clksrc_base);
 221 
 222         sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
 223         err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
 224                                     freq / (1 << priv->ps), 300, 16,
 225                                     clocksource_mmio_readl_up);
 226         if (err) {
 227                 pr_err("ftm: init clock source mmio failed: %d\n", err);
 228                 return err;
 229         }
 230 
 231         ftm_counter_enable(priv->clksrc_base);
 232 
 233         return 0;
 234 }
 235 
 236 static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
 237                                  char *ftm_name)
 238 {
 239         struct clk *clk;
 240         int err;
 241 
 242         clk = of_clk_get_by_name(np, cnt_name);
 243         if (IS_ERR(clk)) {
 244                 pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
 245                 return PTR_ERR(clk);
 246         }
 247         err = clk_prepare_enable(clk);
 248         if (err) {
 249                 pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
 250                         cnt_name, err);
 251                 return err;
 252         }
 253 
 254         clk = of_clk_get_by_name(np, ftm_name);
 255         if (IS_ERR(clk)) {
 256                 pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
 257                 return PTR_ERR(clk);
 258         }
 259         err = clk_prepare_enable(clk);
 260         if (err)
 261                 pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
 262                         ftm_name, err);
 263 
 264         return clk_get_rate(clk);
 265 }
 266 
 267 static unsigned long __init ftm_clk_init(struct device_node *np)
 268 {
 269         long freq;
 270 
 271         freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
 272         if (freq <= 0)
 273                 return 0;
 274 
 275         freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
 276         if (freq <= 0)
 277                 return 0;
 278 
 279         return freq;
 280 }
 281 
 282 static int __init ftm_calc_closest_round_cyc(unsigned long freq)
 283 {
 284         priv->ps = 0;
 285 
 286         /* The counter register is only using the lower 16 bits, and
 287          * if the 'freq' value is to big here, then the periodic_cyc
 288          * may exceed 0xFFFF.
 289          */
 290         do {
 291                 priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
 292                                                 HZ * (1 << priv->ps++));
 293         } while (priv->periodic_cyc > 0xFFFF);
 294 
 295         if (priv->ps > FTM_PS_MAX) {
 296                 pr_err("ftm: the prescaler is %lu > %d\n",
 297                                 priv->ps, FTM_PS_MAX);
 298                 return -EINVAL;
 299         }
 300 
 301         return 0;
 302 }
 303 
 304 static int __init ftm_timer_init(struct device_node *np)
 305 {
 306         unsigned long freq;
 307         int ret, irq;
 308 
 309         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 310         if (!priv)
 311                 return -ENOMEM;
 312 
 313         ret = -ENXIO;
 314         priv->clkevt_base = of_iomap(np, 0);
 315         if (!priv->clkevt_base) {
 316                 pr_err("ftm: unable to map event timer registers\n");
 317                 goto err_clkevt;
 318         }
 319 
 320         priv->clksrc_base = of_iomap(np, 1);
 321         if (!priv->clksrc_base) {
 322                 pr_err("ftm: unable to map source timer registers\n");
 323                 goto err_clksrc;
 324         }
 325 
 326         ret = -EINVAL;
 327         irq = irq_of_parse_and_map(np, 0);
 328         if (irq <= 0) {
 329                 pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
 330                 goto err;
 331         }
 332 
 333         priv->big_endian = of_property_read_bool(np, "big-endian");
 334 
 335         freq = ftm_clk_init(np);
 336         if (!freq)
 337                 goto err;
 338 
 339         ret = ftm_calc_closest_round_cyc(freq);
 340         if (ret)
 341                 goto err;
 342 
 343         ret = ftm_clocksource_init(freq);
 344         if (ret)
 345                 goto err;
 346 
 347         ret = ftm_clockevent_init(freq, irq);
 348         if (ret)
 349                 goto err;
 350 
 351         return 0;
 352 
 353 err:
 354         iounmap(priv->clksrc_base);
 355 err_clksrc:
 356         iounmap(priv->clkevt_base);
 357 err_clkevt:
 358         kfree(priv);
 359         return ret;
 360 }
 361 TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);

/* [<][>][^][v][top][bottom][index][help] */