linux/drivers/clocksource/timer-sun5i.c
<<
>>
Prefs
   1/*
   2 * Allwinner SoCs hstimer driver.
   3 *
   4 * Copyright (C) 2013 Maxime Ripard
   5 *
   6 * Maxime Ripard <maxime.ripard@free-electrons.com>
   7 *
   8 * This file is licensed under the terms of the GNU General Public
   9 * License version 2.  This program is licensed "as is" without any
  10 * warranty of any kind, whether express or implied.
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/clockchips.h>
  15#include <linux/delay.h>
  16#include <linux/interrupt.h>
  17#include <linux/irq.h>
  18#include <linux/irqreturn.h>
  19#include <linux/reset.h>
  20#include <linux/slab.h>
  21#include <linux/of.h>
  22#include <linux/of_address.h>
  23#include <linux/of_irq.h>
  24
  25#define TIMER_IRQ_EN_REG                0x00
  26#define TIMER_IRQ_EN(val)                       BIT(val)
  27#define TIMER_IRQ_ST_REG                0x04
  28#define TIMER_CTL_REG(val)              (0x20 * (val) + 0x10)
  29#define TIMER_CTL_ENABLE                        BIT(0)
  30#define TIMER_CTL_RELOAD                        BIT(1)
  31#define TIMER_CTL_CLK_PRES(val)                 (((val) & 0x7) << 4)
  32#define TIMER_CTL_ONESHOT                       BIT(7)
  33#define TIMER_INTVAL_LO_REG(val)        (0x20 * (val) + 0x14)
  34#define TIMER_INTVAL_HI_REG(val)        (0x20 * (val) + 0x18)
  35#define TIMER_CNTVAL_LO_REG(val)        (0x20 * (val) + 0x1c)
  36#define TIMER_CNTVAL_HI_REG(val)        (0x20 * (val) + 0x20)
  37
  38#define TIMER_SYNC_TICKS        3
  39
  40struct sun5i_timer {
  41        void __iomem            *base;
  42        struct clk              *clk;
  43        struct notifier_block   clk_rate_cb;
  44        u32                     ticks_per_jiffy;
  45};
  46
  47#define to_sun5i_timer(x) \
  48        container_of(x, struct sun5i_timer, clk_rate_cb)
  49
  50struct sun5i_timer_clksrc {
  51        struct sun5i_timer      timer;
  52        struct clocksource      clksrc;
  53};
  54
  55#define to_sun5i_timer_clksrc(x) \
  56        container_of(x, struct sun5i_timer_clksrc, clksrc)
  57
  58struct sun5i_timer_clkevt {
  59        struct sun5i_timer              timer;
  60        struct clock_event_device       clkevt;
  61};
  62
  63#define to_sun5i_timer_clkevt(x) \
  64        container_of(x, struct sun5i_timer_clkevt, clkevt)
  65
  66/*
  67 * When we disable a timer, we need to wait at least for 2 cycles of
  68 * the timer source clock. We will use for that the clocksource timer
  69 * that is already setup and runs at the same frequency than the other
  70 * timers, and we never will be disabled.
  71 */
  72static void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce)
  73{
  74        u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1));
  75
  76        while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS)
  77                cpu_relax();
  78}
  79
  80static void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer)
  81{
  82        u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
  83        writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer));
  84
  85        sun5i_clkevt_sync(ce);
  86}
  87
  88static void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay)
  89{
  90        writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer));
  91}
  92
  93static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic)
  94{
  95        u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
  96
  97        if (periodic)
  98                val &= ~TIMER_CTL_ONESHOT;
  99        else
 100                val |= TIMER_CTL_ONESHOT;
 101
 102        writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
 103               ce->timer.base + TIMER_CTL_REG(timer));
 104}
 105
 106static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt)
 107{
 108        struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
 109
 110        sun5i_clkevt_time_stop(ce, 0);
 111        return 0;
 112}
 113
 114static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt)
 115{
 116        struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
 117
 118        sun5i_clkevt_time_stop(ce, 0);
 119        sun5i_clkevt_time_start(ce, 0, false);
 120        return 0;
 121}
 122
 123static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt)
 124{
 125        struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
 126
 127        sun5i_clkevt_time_stop(ce, 0);
 128        sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy);
 129        sun5i_clkevt_time_start(ce, 0, true);
 130        return 0;
 131}
 132
 133static int sun5i_clkevt_next_event(unsigned long evt,
 134                                   struct clock_event_device *clkevt)
 135{
 136        struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
 137
 138        sun5i_clkevt_time_stop(ce, 0);
 139        sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS);
 140        sun5i_clkevt_time_start(ce, 0, false);
 141
 142        return 0;
 143}
 144
 145static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
 146{
 147        struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id;
 148
 149        writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG);
 150        ce->clkevt.event_handler(&ce->clkevt);
 151
 152        return IRQ_HANDLED;
 153}
 154
 155static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
 156                                unsigned long event, void *data)
 157{
 158        struct clk_notifier_data *ndata = data;
 159        struct sun5i_timer *timer = to_sun5i_timer(nb);
 160        struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer);
 161
 162        switch (event) {
 163        case PRE_RATE_CHANGE:
 164                clocksource_unregister(&cs->clksrc);
 165                break;
 166
 167        case POST_RATE_CHANGE:
 168                clocksource_register_hz(&cs->clksrc, ndata->new_rate);
 169                break;
 170
 171        default:
 172                break;
 173        }
 174
 175        return NOTIFY_DONE;
 176}
 177
 178static int __init sun5i_setup_clocksource(struct device_node *node,
 179                                          void __iomem *base,
 180                                          struct clk *clk, int irq)
 181{
 182        struct sun5i_timer_clksrc *cs;
 183        unsigned long rate;
 184        int ret;
 185
 186        cs = kzalloc(sizeof(*cs), GFP_KERNEL);
 187        if (!cs)
 188                return -ENOMEM;
 189
 190        ret = clk_prepare_enable(clk);
 191        if (ret) {
 192                pr_err("Couldn't enable parent clock\n");
 193                goto err_free;
 194        }
 195
 196        rate = clk_get_rate(clk);
 197
 198        cs->timer.base = base;
 199        cs->timer.clk = clk;
 200        cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc;
 201        cs->timer.clk_rate_cb.next = NULL;
 202
 203        ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb);
 204        if (ret) {
 205                pr_err("Unable to register clock notifier.\n");
 206                goto err_disable_clk;
 207        }
 208
 209        writel(~0, base + TIMER_INTVAL_LO_REG(1));
 210        writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
 211               base + TIMER_CTL_REG(1));
 212
 213        ret = clocksource_mmio_init(base + TIMER_CNTVAL_LO_REG(1), node->name,
 214                                    rate, 340, 32, clocksource_mmio_readl_down);
 215        if (ret) {
 216                pr_err("Couldn't register clock source.\n");
 217                goto err_remove_notifier;
 218        }
 219
 220        return 0;
 221
 222err_remove_notifier:
 223        clk_notifier_unregister(clk, &cs->timer.clk_rate_cb);
 224err_disable_clk:
 225        clk_disable_unprepare(clk);
 226err_free:
 227        kfree(cs);
 228        return ret;
 229}
 230
 231static int sun5i_rate_cb_clkevt(struct notifier_block *nb,
 232                                unsigned long event, void *data)
 233{
 234        struct clk_notifier_data *ndata = data;
 235        struct sun5i_timer *timer = to_sun5i_timer(nb);
 236        struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer);
 237
 238        if (event == POST_RATE_CHANGE) {
 239                clockevents_update_freq(&ce->clkevt, ndata->new_rate);
 240                ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ);
 241        }
 242
 243        return NOTIFY_DONE;
 244}
 245
 246static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base,
 247                                         struct clk *clk, int irq)
 248{
 249        struct sun5i_timer_clkevt *ce;
 250        unsigned long rate;
 251        int ret;
 252        u32 val;
 253
 254        ce = kzalloc(sizeof(*ce), GFP_KERNEL);
 255        if (!ce)
 256                return -ENOMEM;
 257
 258        ret = clk_prepare_enable(clk);
 259        if (ret) {
 260                pr_err("Couldn't enable parent clock\n");
 261                goto err_free;
 262        }
 263
 264        rate = clk_get_rate(clk);
 265
 266        ce->timer.base = base;
 267        ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
 268        ce->timer.clk = clk;
 269        ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt;
 270        ce->timer.clk_rate_cb.next = NULL;
 271
 272        ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb);
 273        if (ret) {
 274                pr_err("Unable to register clock notifier.\n");
 275                goto err_disable_clk;
 276        }
 277
 278        ce->clkevt.name = node->name;
 279        ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
 280        ce->clkevt.set_next_event = sun5i_clkevt_next_event;
 281        ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown;
 282        ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic;
 283        ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot;
 284        ce->clkevt.tick_resume = sun5i_clkevt_shutdown;
 285        ce->clkevt.rating = 340;
 286        ce->clkevt.irq = irq;
 287        ce->clkevt.cpumask = cpu_possible_mask;
 288
 289        /* Enable timer0 interrupt */
 290        val = readl(base + TIMER_IRQ_EN_REG);
 291        writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG);
 292
 293        clockevents_config_and_register(&ce->clkevt, rate,
 294                                        TIMER_SYNC_TICKS, 0xffffffff);
 295
 296        ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
 297                          "sun5i_timer0", ce);
 298        if (ret) {
 299                pr_err("Unable to register interrupt\n");
 300                goto err_remove_notifier;
 301        }
 302
 303        return 0;
 304
 305err_remove_notifier:
 306        clk_notifier_unregister(clk, &ce->timer.clk_rate_cb);
 307err_disable_clk:
 308        clk_disable_unprepare(clk);
 309err_free:
 310        kfree(ce);
 311        return ret;
 312}
 313
 314static void __init sun5i_timer_init(struct device_node *node)
 315{
 316        struct reset_control *rstc;
 317        void __iomem *timer_base;
 318        struct clk *clk;
 319        int irq;
 320
 321        timer_base = of_io_request_and_map(node, 0, of_node_full_name(node));
 322        if (IS_ERR(timer_base))
 323                panic("Can't map registers");
 324
 325        irq = irq_of_parse_and_map(node, 0);
 326        if (irq <= 0)
 327                panic("Can't parse IRQ");
 328
 329        clk = of_clk_get(node, 0);
 330        if (IS_ERR(clk))
 331                panic("Can't get timer clock");
 332
 333        rstc = of_reset_control_get(node, NULL);
 334        if (!IS_ERR(rstc))
 335                reset_control_deassert(rstc);
 336
 337        sun5i_setup_clocksource(node, timer_base, clk, irq);
 338        sun5i_setup_clockevent(node, timer_base, clk, irq);
 339}
 340CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
 341                       sun5i_timer_init);
 342CLOCKSOURCE_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
 343                       sun5i_timer_init);
 344