linux/drivers/clocksource/timer-meson6.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Amlogic Meson6 SoCs timer handling.
   4 *
   5 * Copyright (C) 2014 Carlo Caione <carlo@caione.org>
   6 *
   7 * Based on code from Amlogic, Inc
   8 */
   9
  10#include <linux/bitfield.h>
  11#include <linux/bitops.h>
  12#include <linux/clk.h>
  13#include <linux/clockchips.h>
  14#include <linux/interrupt.h>
  15#include <linux/irq.h>
  16#include <linux/irqreturn.h>
  17#include <linux/sched_clock.h>
  18#include <linux/of.h>
  19#include <linux/of_address.h>
  20#include <linux/of_irq.h>
  21
  22#ifdef CONFIG_ARM
  23#include <linux/delay.h>
  24#endif
  25
  26#define MESON_ISA_TIMER_MUX                                     0x00
  27#define MESON_ISA_TIMER_MUX_TIMERD_EN                           BIT(19)
  28#define MESON_ISA_TIMER_MUX_TIMERC_EN                           BIT(18)
  29#define MESON_ISA_TIMER_MUX_TIMERB_EN                           BIT(17)
  30#define MESON_ISA_TIMER_MUX_TIMERA_EN                           BIT(16)
  31#define MESON_ISA_TIMER_MUX_TIMERD_MODE                         BIT(15)
  32#define MESON_ISA_TIMER_MUX_TIMERC_MODE                         BIT(14)
  33#define MESON_ISA_TIMER_MUX_TIMERB_MODE                         BIT(13)
  34#define MESON_ISA_TIMER_MUX_TIMERA_MODE                         BIT(12)
  35#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK             GENMASK(10, 8)
  36#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_SYSTEM_CLOCK     0x0
  37#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US              0x1
  38#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_10US             0x2
  39#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_100US            0x3
  40#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1MS              0x4
  41#define MESON_ISA_TIMER_MUX_TIMERD_INPUT_CLOCK_MASK             GENMASK(7, 6)
  42#define MESON_ISA_TIMER_MUX_TIMERC_INPUT_CLOCK_MASK             GENMASK(5, 4)
  43#define MESON_ISA_TIMER_MUX_TIMERB_INPUT_CLOCK_MASK             GENMASK(3, 2)
  44#define MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK             GENMASK(1, 0)
  45#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US           0x0
  46#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_10US          0x1
  47#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_100US         0x0
  48#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1MS           0x3
  49
  50#define MESON_ISA_TIMERA                                        0x04
  51#define MESON_ISA_TIMERB                                        0x08
  52#define MESON_ISA_TIMERC                                        0x0c
  53#define MESON_ISA_TIMERD                                        0x10
  54#define MESON_ISA_TIMERE                                        0x14
  55
  56static void __iomem *timer_base;
  57
  58#ifdef CONFIG_ARM
  59static unsigned long meson6_read_current_timer(void)
  60{
  61        return readl_relaxed(timer_base + MESON_ISA_TIMERE);
  62}
  63
  64static struct delay_timer meson6_delay_timer = {
  65        .read_current_timer = meson6_read_current_timer,
  66        .freq = 1000 * 1000,
  67};
  68#endif
  69
  70static u64 notrace meson6_timer_sched_read(void)
  71{
  72        return (u64)readl(timer_base + MESON_ISA_TIMERE);
  73}
  74
  75static void meson6_clkevt_time_stop(void)
  76{
  77        u32 val = readl(timer_base + MESON_ISA_TIMER_MUX);
  78
  79        writel(val & ~MESON_ISA_TIMER_MUX_TIMERA_EN,
  80               timer_base + MESON_ISA_TIMER_MUX);
  81}
  82
  83static void meson6_clkevt_time_setup(unsigned long delay)
  84{
  85        writel(delay, timer_base + MESON_ISA_TIMERA);
  86}
  87
  88static void meson6_clkevt_time_start(bool periodic)
  89{
  90        u32 val = readl(timer_base + MESON_ISA_TIMER_MUX);
  91
  92        if (periodic)
  93                val |= MESON_ISA_TIMER_MUX_TIMERA_MODE;
  94        else
  95                val &= ~MESON_ISA_TIMER_MUX_TIMERA_MODE;
  96
  97        writel(val | MESON_ISA_TIMER_MUX_TIMERA_EN,
  98               timer_base + MESON_ISA_TIMER_MUX);
  99}
 100
 101static int meson6_shutdown(struct clock_event_device *evt)
 102{
 103        meson6_clkevt_time_stop();
 104        return 0;
 105}
 106
 107static int meson6_set_oneshot(struct clock_event_device *evt)
 108{
 109        meson6_clkevt_time_stop();
 110        meson6_clkevt_time_start(false);
 111        return 0;
 112}
 113
 114static int meson6_set_periodic(struct clock_event_device *evt)
 115{
 116        meson6_clkevt_time_stop();
 117        meson6_clkevt_time_setup(USEC_PER_SEC / HZ - 1);
 118        meson6_clkevt_time_start(true);
 119        return 0;
 120}
 121
 122static int meson6_clkevt_next_event(unsigned long evt,
 123                                    struct clock_event_device *unused)
 124{
 125        meson6_clkevt_time_stop();
 126        meson6_clkevt_time_setup(evt);
 127        meson6_clkevt_time_start(false);
 128
 129        return 0;
 130}
 131
 132static struct clock_event_device meson6_clockevent = {
 133        .name                   = "meson6_tick",
 134        .rating                 = 400,
 135        .features               = CLOCK_EVT_FEAT_PERIODIC |
 136                                  CLOCK_EVT_FEAT_ONESHOT,
 137        .set_state_shutdown     = meson6_shutdown,
 138        .set_state_periodic     = meson6_set_periodic,
 139        .set_state_oneshot      = meson6_set_oneshot,
 140        .tick_resume            = meson6_shutdown,
 141        .set_next_event         = meson6_clkevt_next_event,
 142};
 143
 144static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id)
 145{
 146        struct clock_event_device *evt = (struct clock_event_device *)dev_id;
 147
 148        evt->event_handler(evt);
 149
 150        return IRQ_HANDLED;
 151}
 152
 153static int __init meson6_timer_init(struct device_node *node)
 154{
 155        u32 val;
 156        int ret, irq;
 157
 158        timer_base = of_io_request_and_map(node, 0, "meson6-timer");
 159        if (IS_ERR(timer_base)) {
 160                pr_err("Can't map registers\n");
 161                return -ENXIO;
 162        }
 163
 164        irq = irq_of_parse_and_map(node, 0);
 165        if (irq <= 0) {
 166                pr_err("Can't parse IRQ\n");
 167                return -EINVAL;
 168        }
 169
 170        /* Set 1us for timer E */
 171        val = readl(timer_base + MESON_ISA_TIMER_MUX);
 172        val &= ~MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK;
 173        val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK,
 174                          MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US);
 175        writel(val, timer_base + MESON_ISA_TIMER_MUX);
 176
 177        sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC);
 178        clocksource_mmio_init(timer_base + MESON_ISA_TIMERE, node->name,
 179                              1000 * 1000, 300, 32, clocksource_mmio_readl_up);
 180
 181        /* Timer A base 1us */
 182        val &= ~MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK;
 183        val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK,
 184                          MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US);
 185        writel(val, timer_base + MESON_ISA_TIMER_MUX);
 186
 187        /* Stop the timer A */
 188        meson6_clkevt_time_stop();
 189
 190        ret = request_irq(irq, meson6_timer_interrupt,
 191                          IRQF_TIMER | IRQF_IRQPOLL, "meson6_timer",
 192                          &meson6_clockevent);
 193        if (ret) {
 194                pr_warn("failed to setup irq %d\n", irq);
 195                return ret;
 196        }
 197
 198        meson6_clockevent.cpumask = cpu_possible_mask;
 199        meson6_clockevent.irq = irq;
 200
 201        clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC,
 202                                        1, 0xfffe);
 203
 204#ifdef CONFIG_ARM
 205        /* Also use MESON_ISA_TIMERE for delays */
 206        register_current_timer_delay(&meson6_delay_timer);
 207#endif
 208
 209        return 0;
 210}
 211TIMER_OF_DECLARE(meson6, "amlogic,meson6-timer",
 212                       meson6_timer_init);
 213