linux/drivers/clocksource/clps711x-timer.c
<<
>>
Prefs
   1/*
   2 *  Cirrus Logic CLPS711X clocksource driver
   3 *
   4 *  Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
   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 as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/clockchips.h>
  14#include <linux/clocksource.h>
  15#include <linux/interrupt.h>
  16#include <linux/io.h>
  17#include <linux/of_address.h>
  18#include <linux/of_irq.h>
  19#include <linux/sched_clock.h>
  20#include <linux/slab.h>
  21
  22enum {
  23        CLPS711X_CLKSRC_CLOCKSOURCE,
  24        CLPS711X_CLKSRC_CLOCKEVENT,
  25};
  26
  27static void __iomem *tcd;
  28
  29static u64 notrace clps711x_sched_clock_read(void)
  30{
  31        return ~readw(tcd);
  32}
  33
  34static int __init _clps711x_clksrc_init(struct clk *clock, void __iomem *base)
  35{
  36        unsigned long rate;
  37
  38        if (!base)
  39                return -ENOMEM;
  40        if (IS_ERR(clock))
  41                return PTR_ERR(clock);
  42
  43        rate = clk_get_rate(clock);
  44
  45        tcd = base;
  46
  47        clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16,
  48                              clocksource_mmio_readw_down);
  49
  50        sched_clock_register(clps711x_sched_clock_read, 16, rate);
  51
  52        return 0;
  53}
  54
  55static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id)
  56{
  57        struct clock_event_device *evt = dev_id;
  58
  59        evt->event_handler(evt);
  60
  61        return IRQ_HANDLED;
  62}
  63
  64static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
  65                                        unsigned int irq)
  66{
  67        struct clock_event_device *clkevt;
  68        unsigned long rate;
  69
  70        if (!irq)
  71                return -EINVAL;
  72        if (!base)
  73                return -ENOMEM;
  74        if (IS_ERR(clock))
  75                return PTR_ERR(clock);
  76
  77        clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
  78        if (!clkevt)
  79                return -ENOMEM;
  80
  81        rate = clk_get_rate(clock);
  82
  83        /* Set Timer prescaler */
  84        writew(DIV_ROUND_CLOSEST(rate, HZ), base);
  85
  86        clkevt->name = "clps711x-clockevent";
  87        clkevt->rating = 300;
  88        clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP;
  89        clkevt->cpumask = cpumask_of(0);
  90        clockevents_config_and_register(clkevt, HZ, 0, 0);
  91
  92        return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER,
  93                           "clps711x-timer", clkevt);
  94}
  95
  96void __init clps711x_clksrc_init(void __iomem *tc1_base, void __iomem *tc2_base,
  97                                 unsigned int irq)
  98{
  99        struct clk *tc1 = clk_get_sys("clps711x-timer.0", NULL);
 100        struct clk *tc2 = clk_get_sys("clps711x-timer.1", NULL);
 101
 102        BUG_ON(_clps711x_clksrc_init(tc1, tc1_base));
 103        BUG_ON(_clps711x_clkevt_init(tc2, tc2_base, irq));
 104}
 105
 106#ifdef CONFIG_TIMER_OF
 107static int __init clps711x_timer_init(struct device_node *np)
 108{
 109        unsigned int irq = irq_of_parse_and_map(np, 0);
 110        struct clk *clock = of_clk_get(np, 0);
 111        void __iomem *base = of_iomap(np, 0);
 112
 113        switch (of_alias_get_id(np, "timer")) {
 114        case CLPS711X_CLKSRC_CLOCKSOURCE:
 115                return _clps711x_clksrc_init(clock, base);
 116        case CLPS711X_CLKSRC_CLOCKEVENT:
 117                return _clps711x_clkevt_init(clock, base, irq);
 118        default:
 119                return -EINVAL;
 120        }
 121}
 122TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);
 123#endif
 124