linux/drivers/clocksource/timer-digicolor.c
<<
>>
Prefs
   1/*
   2 * Conexant Digicolor timer driver
   3 *
   4 * Author: Baruch Siach <baruch@tkos.co.il>
   5 *
   6 * Copyright (C) 2014 Paradox Innovation Ltd.
   7 *
   8 * Based on:
   9 *      Allwinner SoCs hstimer driver
  10 *
  11 * Copyright (C) 2013 Maxime Ripard
  12 *
  13 * Maxime Ripard <maxime.ripard@free-electrons.com>
  14 *
  15 * This file is licensed under the terms of the GNU General Public
  16 * License version 2.  This program is licensed "as is" without any
  17 * warranty of any kind, whether express or implied.
  18 */
  19
  20/*
  21 * Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to
  22 * "Timer H". Timer A is the only one with watchdog support, so it is dedicated
  23 * to the watchdog driver. This driver uses Timer B for sched_clock(), and
  24 * Timer C for clockevents.
  25 */
  26
  27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  28
  29#include <linux/clk.h>
  30#include <linux/clockchips.h>
  31#include <linux/interrupt.h>
  32#include <linux/irq.h>
  33#include <linux/irqreturn.h>
  34#include <linux/sched/clock.h>
  35#include <linux/sched_clock.h>
  36#include <linux/of.h>
  37#include <linux/of_address.h>
  38#include <linux/of_irq.h>
  39
  40enum {
  41        TIMER_A,
  42        TIMER_B,
  43        TIMER_C,
  44        TIMER_D,
  45        TIMER_E,
  46        TIMER_F,
  47        TIMER_G,
  48        TIMER_H,
  49};
  50
  51#define CONTROL(t)      ((t)*8)
  52#define COUNT(t)        ((t)*8 + 4)
  53
  54#define CONTROL_DISABLE         0
  55#define CONTROL_ENABLE          BIT(0)
  56#define CONTROL_MODE(m)         ((m) << 4)
  57#define CONTROL_MODE_ONESHOT    CONTROL_MODE(1)
  58#define CONTROL_MODE_PERIODIC   CONTROL_MODE(2)
  59
  60struct digicolor_timer {
  61        struct clock_event_device ce;
  62        void __iomem *base;
  63        u32 ticks_per_jiffy;
  64        int timer_id; /* one of TIMER_* */
  65};
  66
  67static struct digicolor_timer *dc_timer(struct clock_event_device *ce)
  68{
  69        return container_of(ce, struct digicolor_timer, ce);
  70}
  71
  72static inline void dc_timer_disable(struct clock_event_device *ce)
  73{
  74        struct digicolor_timer *dt = dc_timer(ce);
  75        writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id));
  76}
  77
  78static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode)
  79{
  80        struct digicolor_timer *dt = dc_timer(ce);
  81        writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id));
  82}
  83
  84static inline void dc_timer_set_count(struct clock_event_device *ce,
  85                                      unsigned long count)
  86{
  87        struct digicolor_timer *dt = dc_timer(ce);
  88        writel(count, dt->base + COUNT(dt->timer_id));
  89}
  90
  91static int digicolor_clkevt_shutdown(struct clock_event_device *ce)
  92{
  93        dc_timer_disable(ce);
  94        return 0;
  95}
  96
  97static int digicolor_clkevt_set_oneshot(struct clock_event_device *ce)
  98{
  99        dc_timer_disable(ce);
 100        dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
 101        return 0;
 102}
 103
 104static int digicolor_clkevt_set_periodic(struct clock_event_device *ce)
 105{
 106        struct digicolor_timer *dt = dc_timer(ce);
 107
 108        dc_timer_disable(ce);
 109        dc_timer_set_count(ce, dt->ticks_per_jiffy);
 110        dc_timer_enable(ce, CONTROL_MODE_PERIODIC);
 111        return 0;
 112}
 113
 114static int digicolor_clkevt_next_event(unsigned long evt,
 115                                       struct clock_event_device *ce)
 116{
 117        dc_timer_disable(ce);
 118        dc_timer_set_count(ce, evt);
 119        dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
 120
 121        return 0;
 122}
 123
 124static struct digicolor_timer dc_timer_dev = {
 125        .ce = {
 126                .name = "digicolor_tick",
 127                .rating = 340,
 128                .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 129                .set_state_shutdown = digicolor_clkevt_shutdown,
 130                .set_state_periodic = digicolor_clkevt_set_periodic,
 131                .set_state_oneshot = digicolor_clkevt_set_oneshot,
 132                .tick_resume = digicolor_clkevt_shutdown,
 133                .set_next_event = digicolor_clkevt_next_event,
 134        },
 135        .timer_id = TIMER_C,
 136};
 137
 138static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id)
 139{
 140        struct clock_event_device *evt = dev_id;
 141
 142        evt->event_handler(evt);
 143
 144        return IRQ_HANDLED;
 145}
 146
 147static u64 notrace digicolor_timer_sched_read(void)
 148{
 149        return ~readl(dc_timer_dev.base + COUNT(TIMER_B));
 150}
 151
 152static int __init digicolor_timer_init(struct device_node *node)
 153{
 154        unsigned long rate;
 155        struct clk *clk;
 156        int ret, irq;
 157
 158        /*
 159         * timer registers are shared with the watchdog timer;
 160         * don't map exclusively
 161         */
 162        dc_timer_dev.base = of_iomap(node, 0);
 163        if (!dc_timer_dev.base) {
 164                pr_err("Can't map registers\n");
 165                return -ENXIO;
 166        }
 167
 168        irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id);
 169        if (irq <= 0) {
 170                pr_err("Can't parse IRQ\n");
 171                return -EINVAL;
 172        }
 173
 174        clk = of_clk_get(node, 0);
 175        if (IS_ERR(clk)) {
 176                pr_err("Can't get timer clock\n");
 177                return PTR_ERR(clk);
 178        }
 179        clk_prepare_enable(clk);
 180        rate = clk_get_rate(clk);
 181        dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
 182
 183        writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B));
 184        writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B));
 185        writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B));
 186
 187        sched_clock_register(digicolor_timer_sched_read, 32, rate);
 188        clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name,
 189                              rate, 340, 32, clocksource_mmio_readl_down);
 190
 191        ret = request_irq(irq, digicolor_timer_interrupt,
 192                          IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC",
 193                          &dc_timer_dev.ce);
 194        if (ret) {
 195                pr_warn("request of timer irq %d failed (%d)\n", irq, ret);
 196                return ret;
 197        }
 198
 199        dc_timer_dev.ce.cpumask = cpu_possible_mask;
 200        dc_timer_dev.ce.irq = irq;
 201
 202        clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff);
 203
 204        return 0;
 205}
 206TIMER_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
 207                       digicolor_timer_init);
 208