linux/drivers/clocksource/mps2-timer.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 ARM Limited
   3 *
   4 * Author: Vladimir Murzin <vladimir.murzin@arm.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 */
  11
  12#define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
  13
  14#include <linux/clk.h>
  15#include <linux/clockchips.h>
  16#include <linux/clocksource.h>
  17#include <linux/err.h>
  18#include <linux/interrupt.h>
  19#include <linux/io.h>
  20#include <linux/irq.h>
  21#include <linux/of_address.h>
  22#include <linux/of.h>
  23#include <linux/of_irq.h>
  24#include <linux/sched_clock.h>
  25#include <linux/slab.h>
  26
  27#define TIMER_CTRL              0x0
  28#define TIMER_CTRL_ENABLE       BIT(0)
  29#define TIMER_CTRL_IE           BIT(3)
  30
  31#define TIMER_VALUE             0x4
  32#define TIMER_RELOAD            0x8
  33#define TIMER_INT               0xc
  34
  35struct clockevent_mps2 {
  36        void __iomem *reg;
  37        u32 clock_count_per_tick;
  38        struct clock_event_device clkevt;
  39};
  40
  41static void __iomem *sched_clock_base;
  42
  43static u64 notrace mps2_sched_read(void)
  44{
  45        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
  46}
  47
  48static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
  49{
  50        return container_of(c, struct clockevent_mps2, clkevt);
  51}
  52
  53static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
  54{
  55        writel_relaxed(val, to_mps2_clkevt(c)->reg + offset);
  56}
  57
  58static int mps2_timer_shutdown(struct clock_event_device *ce)
  59{
  60        clockevent_mps2_writel(0, ce, TIMER_RELOAD);
  61        clockevent_mps2_writel(0, ce, TIMER_CTRL);
  62
  63        return 0;
  64}
  65
  66static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
  67{
  68        clockevent_mps2_writel(next, ce, TIMER_VALUE);
  69        clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
  70
  71        return 0;
  72}
  73
  74static int mps2_timer_set_periodic(struct clock_event_device *ce)
  75{
  76        u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
  77
  78        clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
  79        clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
  80        clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
  81
  82        return 0;
  83}
  84
  85static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
  86{
  87        struct clockevent_mps2 *ce = dev_id;
  88        u32 status = readl_relaxed(ce->reg + TIMER_INT);
  89
  90        if (!status) {
  91                pr_warn("spurious interrupt\n");
  92                return IRQ_NONE;
  93        }
  94
  95        writel_relaxed(1, ce->reg + TIMER_INT);
  96
  97        ce->clkevt.event_handler(&ce->clkevt);
  98
  99        return IRQ_HANDLED;
 100}
 101
 102static int __init mps2_clockevent_init(struct device_node *np)
 103{
 104        void __iomem *base;
 105        struct clk *clk = NULL;
 106        struct clockevent_mps2 *ce;
 107        u32 rate;
 108        int irq, ret;
 109        const char *name = "mps2-clkevt";
 110
 111        ret = of_property_read_u32(np, "clock-frequency", &rate);
 112        if (ret) {
 113                clk = of_clk_get(np, 0);
 114                if (IS_ERR(clk)) {
 115                        ret = PTR_ERR(clk);
 116                        pr_err("failed to get clock for clockevent: %d\n", ret);
 117                        goto out;
 118                }
 119
 120                ret = clk_prepare_enable(clk);
 121                if (ret) {
 122                        pr_err("failed to enable clock for clockevent: %d\n", ret);
 123                        goto out_clk_put;
 124                }
 125
 126                rate = clk_get_rate(clk);
 127        }
 128
 129        base = of_iomap(np, 0);
 130        if (!base) {
 131                ret = -EADDRNOTAVAIL;
 132                pr_err("failed to map register for clockevent: %d\n", ret);
 133                goto out_clk_disable;
 134        }
 135
 136        irq = irq_of_parse_and_map(np, 0);
 137        if (!irq) {
 138                ret = -ENOENT;
 139                pr_err("failed to get irq for clockevent: %d\n", ret);
 140                goto out_iounmap;
 141        }
 142
 143        ce = kzalloc(sizeof(*ce), GFP_KERNEL);
 144        if (!ce) {
 145                ret = -ENOMEM;
 146                goto out_iounmap;
 147        }
 148
 149        ce->reg = base;
 150        ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
 151        ce->clkevt.irq = irq;
 152        ce->clkevt.name = name;
 153        ce->clkevt.rating = 200;
 154        ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
 155        ce->clkevt.cpumask = cpu_possible_mask;
 156        ce->clkevt.set_state_shutdown   = mps2_timer_shutdown,
 157        ce->clkevt.set_state_periodic   = mps2_timer_set_periodic,
 158        ce->clkevt.set_state_oneshot    = mps2_timer_shutdown,
 159        ce->clkevt.set_next_event       = mps2_timer_set_next_event;
 160
 161        /* Ensure timer is disabled */
 162        writel_relaxed(0, base + TIMER_CTRL);
 163
 164        ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
 165        if (ret) {
 166                pr_err("failed to request irq for clockevent: %d\n", ret);
 167                goto out_kfree;
 168        }
 169
 170        clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
 171
 172        return 0;
 173
 174out_kfree:
 175        kfree(ce);
 176out_iounmap:
 177        iounmap(base);
 178out_clk_disable:
 179        /* clk_{disable, unprepare, put}() can handle NULL as a parameter */
 180        clk_disable_unprepare(clk);
 181out_clk_put:
 182        clk_put(clk);
 183out:
 184        return ret;
 185}
 186
 187static int __init mps2_clocksource_init(struct device_node *np)
 188{
 189        void __iomem *base;
 190        struct clk *clk = NULL;
 191        u32 rate;
 192        int ret;
 193        const char *name = "mps2-clksrc";
 194
 195        ret = of_property_read_u32(np, "clock-frequency", &rate);
 196        if (ret) {
 197                clk = of_clk_get(np, 0);
 198                if (IS_ERR(clk)) {
 199                        ret = PTR_ERR(clk);
 200                        pr_err("failed to get clock for clocksource: %d\n", ret);
 201                        goto out;
 202                }
 203
 204                ret = clk_prepare_enable(clk);
 205                if (ret) {
 206                        pr_err("failed to enable clock for clocksource: %d\n", ret);
 207                        goto out_clk_put;
 208                }
 209
 210                rate = clk_get_rate(clk);
 211        }
 212
 213        base = of_iomap(np, 0);
 214        if (!base) {
 215                ret = -EADDRNOTAVAIL;
 216                pr_err("failed to map register for clocksource: %d\n", ret);
 217                goto out_clk_disable;
 218        }
 219
 220        /* Ensure timer is disabled */
 221        writel_relaxed(0, base + TIMER_CTRL);
 222
 223        /* ... and set it up as free-running clocksource */
 224        writel_relaxed(0xffffffff, base + TIMER_VALUE);
 225        writel_relaxed(0xffffffff, base + TIMER_RELOAD);
 226
 227        writel_relaxed(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
 228
 229        ret = clocksource_mmio_init(base + TIMER_VALUE, name,
 230                                    rate, 200, 32,
 231                                    clocksource_mmio_readl_down);
 232        if (ret) {
 233                pr_err("failed to init clocksource: %d\n", ret);
 234                goto out_iounmap;
 235        }
 236
 237        sched_clock_base = base;
 238        sched_clock_register(mps2_sched_read, 32, rate);
 239
 240        return 0;
 241
 242out_iounmap:
 243        iounmap(base);
 244out_clk_disable:
 245        /* clk_{disable, unprepare, put}() can handle NULL as a parameter */
 246        clk_disable_unprepare(clk);
 247out_clk_put:
 248        clk_put(clk);
 249out:
 250        return ret;
 251}
 252
 253static int __init mps2_timer_init(struct device_node *np)
 254{
 255        static int has_clocksource, has_clockevent;
 256        int ret;
 257
 258        if (!has_clocksource) {
 259                ret = mps2_clocksource_init(np);
 260                if (!ret) {
 261                        has_clocksource = 1;
 262                        return 0;
 263                }
 264        }
 265
 266        if (!has_clockevent) {
 267                ret = mps2_clockevent_init(np);
 268                if (!ret) {
 269                        has_clockevent = 1;
 270                        return 0;
 271                }
 272        }
 273
 274        return 0;
 275}
 276
 277CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
 278