linux/drivers/clocksource/dw_apb_timer_of.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 Altera Corporation
   4 * Copyright (c) 2011 Picochip Ltd., Jamie Iles
   5 *
   6 * Modified from mach-picoxcell/time.c
   7 */
   8#include <linux/delay.h>
   9#include <linux/dw_apb_timer.h>
  10#include <linux/of.h>
  11#include <linux/of_address.h>
  12#include <linux/of_irq.h>
  13#include <linux/clk.h>
  14#include <linux/reset.h>
  15#include <linux/sched_clock.h>
  16
  17static int __init timer_get_base_and_rate(struct device_node *np,
  18                                    void __iomem **base, u32 *rate)
  19{
  20        struct clk *timer_clk;
  21        struct clk *pclk;
  22        struct reset_control *rstc;
  23        int ret;
  24
  25        *base = of_iomap(np, 0);
  26
  27        if (!*base)
  28                panic("Unable to map regs for %pOFn", np);
  29
  30        /*
  31         * Reset the timer if the reset control is available, wiping
  32         * out the state the firmware may have left it
  33         */
  34        rstc = of_reset_control_get(np, NULL);
  35        if (!IS_ERR(rstc)) {
  36                reset_control_assert(rstc);
  37                reset_control_deassert(rstc);
  38        }
  39
  40        /*
  41         * Not all implementations use a peripheral clock, so don't panic
  42         * if it's not present
  43         */
  44        pclk = of_clk_get_by_name(np, "pclk");
  45        if (!IS_ERR(pclk))
  46                if (clk_prepare_enable(pclk))
  47                        pr_warn("pclk for %pOFn is present, but could not be activated\n",
  48                                np);
  49
  50        if (!of_property_read_u32(np, "clock-freq", rate) &&
  51            !of_property_read_u32(np, "clock-frequency", rate))
  52                return 0;
  53
  54        timer_clk = of_clk_get_by_name(np, "timer");
  55        if (IS_ERR(timer_clk)) {
  56                ret = PTR_ERR(timer_clk);
  57                goto out_pclk_disable;
  58        }
  59
  60        ret = clk_prepare_enable(timer_clk);
  61        if (ret)
  62                goto out_timer_clk_put;
  63
  64        *rate = clk_get_rate(timer_clk);
  65        if (!(*rate)) {
  66                ret = -EINVAL;
  67                goto out_timer_clk_disable;
  68        }
  69
  70        return 0;
  71
  72out_timer_clk_disable:
  73        clk_disable_unprepare(timer_clk);
  74out_timer_clk_put:
  75        clk_put(timer_clk);
  76out_pclk_disable:
  77        if (!IS_ERR(pclk)) {
  78                clk_disable_unprepare(pclk);
  79                clk_put(pclk);
  80        }
  81        iounmap(*base);
  82        return ret;
  83}
  84
  85static int __init add_clockevent(struct device_node *event_timer)
  86{
  87        void __iomem *iobase;
  88        struct dw_apb_clock_event_device *ced;
  89        u32 irq, rate;
  90        int ret = 0;
  91
  92        irq = irq_of_parse_and_map(event_timer, 0);
  93        if (irq == 0)
  94                panic("No IRQ for clock event timer");
  95
  96        ret = timer_get_base_and_rate(event_timer, &iobase, &rate);
  97        if (ret)
  98                return ret;
  99
 100        ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,
 101                                     rate);
 102        if (!ced)
 103                return -EINVAL;
 104
 105        dw_apb_clockevent_register(ced);
 106
 107        return 0;
 108}
 109
 110static void __iomem *sched_io_base;
 111static u32 sched_rate;
 112
 113static int __init add_clocksource(struct device_node *source_timer)
 114{
 115        void __iomem *iobase;
 116        struct dw_apb_clocksource *cs;
 117        u32 rate;
 118        int ret;
 119
 120        ret = timer_get_base_and_rate(source_timer, &iobase, &rate);
 121        if (ret)
 122                return ret;
 123
 124        cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
 125        if (!cs)
 126                return -EINVAL;
 127
 128        dw_apb_clocksource_start(cs);
 129        dw_apb_clocksource_register(cs);
 130
 131        /*
 132         * Fallback to use the clocksource as sched_clock if no separate
 133         * timer is found. sched_io_base then points to the current_value
 134         * register of the clocksource timer.
 135         */
 136        sched_io_base = iobase + 0x04;
 137        sched_rate = rate;
 138
 139        return 0;
 140}
 141
 142static u64 notrace read_sched_clock(void)
 143{
 144        return ~readl_relaxed(sched_io_base);
 145}
 146
 147static const struct of_device_id sptimer_ids[] __initconst = {
 148        { .compatible = "picochip,pc3x2-rtc" },
 149        { /* Sentinel */ },
 150};
 151
 152static void __init init_sched_clock(void)
 153{
 154        struct device_node *sched_timer;
 155
 156        sched_timer = of_find_matching_node(NULL, sptimer_ids);
 157        if (sched_timer) {
 158                timer_get_base_and_rate(sched_timer, &sched_io_base,
 159                                        &sched_rate);
 160                of_node_put(sched_timer);
 161        }
 162
 163        sched_clock_register(read_sched_clock, 32, sched_rate);
 164}
 165
 166#ifdef CONFIG_ARM
 167static unsigned long dw_apb_delay_timer_read(void)
 168{
 169        return ~readl_relaxed(sched_io_base);
 170}
 171
 172static struct delay_timer dw_apb_delay_timer = {
 173        .read_current_timer     = dw_apb_delay_timer_read,
 174};
 175#endif
 176
 177static int num_called;
 178static int __init dw_apb_timer_init(struct device_node *timer)
 179{
 180        int ret = 0;
 181
 182        switch (num_called) {
 183        case 1:
 184                pr_debug("%s: found clocksource timer\n", __func__);
 185                ret = add_clocksource(timer);
 186                if (ret)
 187                        return ret;
 188                init_sched_clock();
 189#ifdef CONFIG_ARM
 190                dw_apb_delay_timer.freq = sched_rate;
 191                register_current_timer_delay(&dw_apb_delay_timer);
 192#endif
 193                break;
 194        default:
 195                pr_debug("%s: found clockevent timer\n", __func__);
 196                ret = add_clockevent(timer);
 197                if (ret)
 198                        return ret;
 199                break;
 200        }
 201
 202        num_called++;
 203
 204        return 0;
 205}
 206TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
 207TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
 208TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
 209TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
 210