Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2012 Altera Corporation
0004  * Copyright (c) 2011 Picochip Ltd., Jamie Iles
0005  *
0006  * Modified from mach-picoxcell/time.c
0007  */
0008 #include <linux/delay.h>
0009 #include <linux/dw_apb_timer.h>
0010 #include <linux/of.h>
0011 #include <linux/of_address.h>
0012 #include <linux/of_irq.h>
0013 #include <linux/clk.h>
0014 #include <linux/reset.h>
0015 #include <linux/sched_clock.h>
0016 
0017 static int __init timer_get_base_and_rate(struct device_node *np,
0018                     void __iomem **base, u32 *rate)
0019 {
0020     struct clk *timer_clk;
0021     struct clk *pclk;
0022     struct reset_control *rstc;
0023     int ret;
0024 
0025     *base = of_iomap(np, 0);
0026 
0027     if (!*base)
0028         panic("Unable to map regs for %pOFn", np);
0029 
0030     /*
0031      * Reset the timer if the reset control is available, wiping
0032      * out the state the firmware may have left it
0033      */
0034     rstc = of_reset_control_get(np, NULL);
0035     if (!IS_ERR(rstc)) {
0036         reset_control_assert(rstc);
0037         reset_control_deassert(rstc);
0038     }
0039 
0040     /*
0041      * Not all implementations use a peripheral clock, so don't panic
0042      * if it's not present
0043      */
0044     pclk = of_clk_get_by_name(np, "pclk");
0045     if (!IS_ERR(pclk))
0046         if (clk_prepare_enable(pclk))
0047             pr_warn("pclk for %pOFn is present, but could not be activated\n",
0048                 np);
0049 
0050     if (!of_property_read_u32(np, "clock-freq", rate) ||
0051         !of_property_read_u32(np, "clock-frequency", rate))
0052         return 0;
0053 
0054     timer_clk = of_clk_get_by_name(np, "timer");
0055     if (IS_ERR(timer_clk)) {
0056         ret = PTR_ERR(timer_clk);
0057         goto out_pclk_disable;
0058     }
0059 
0060     ret = clk_prepare_enable(timer_clk);
0061     if (ret)
0062         goto out_timer_clk_put;
0063 
0064     *rate = clk_get_rate(timer_clk);
0065     if (!(*rate)) {
0066         ret = -EINVAL;
0067         goto out_timer_clk_disable;
0068     }
0069 
0070     return 0;
0071 
0072 out_timer_clk_disable:
0073     clk_disable_unprepare(timer_clk);
0074 out_timer_clk_put:
0075     clk_put(timer_clk);
0076 out_pclk_disable:
0077     if (!IS_ERR(pclk)) {
0078         clk_disable_unprepare(pclk);
0079         clk_put(pclk);
0080     }
0081     iounmap(*base);
0082     return ret;
0083 }
0084 
0085 static int __init add_clockevent(struct device_node *event_timer)
0086 {
0087     void __iomem *iobase;
0088     struct dw_apb_clock_event_device *ced;
0089     u32 irq, rate;
0090     int ret = 0;
0091 
0092     irq = irq_of_parse_and_map(event_timer, 0);
0093     if (irq == 0)
0094         panic("No IRQ for clock event timer");
0095 
0096     ret = timer_get_base_and_rate(event_timer, &iobase, &rate);
0097     if (ret)
0098         return ret;
0099 
0100     ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,
0101                      rate);
0102     if (!ced)
0103         return -EINVAL;
0104 
0105     dw_apb_clockevent_register(ced);
0106 
0107     return 0;
0108 }
0109 
0110 static void __iomem *sched_io_base;
0111 static u32 sched_rate;
0112 
0113 static int __init add_clocksource(struct device_node *source_timer)
0114 {
0115     void __iomem *iobase;
0116     struct dw_apb_clocksource *cs;
0117     u32 rate;
0118     int ret;
0119 
0120     ret = timer_get_base_and_rate(source_timer, &iobase, &rate);
0121     if (ret)
0122         return ret;
0123 
0124     cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
0125     if (!cs)
0126         return -EINVAL;
0127 
0128     dw_apb_clocksource_start(cs);
0129     dw_apb_clocksource_register(cs);
0130 
0131     /*
0132      * Fallback to use the clocksource as sched_clock if no separate
0133      * timer is found. sched_io_base then points to the current_value
0134      * register of the clocksource timer.
0135      */
0136     sched_io_base = iobase + 0x04;
0137     sched_rate = rate;
0138 
0139     return 0;
0140 }
0141 
0142 static u64 notrace read_sched_clock(void)
0143 {
0144     return ~readl_relaxed(sched_io_base);
0145 }
0146 
0147 static const struct of_device_id sptimer_ids[] __initconst = {
0148     { .compatible = "picochip,pc3x2-rtc" },
0149     { /* Sentinel */ },
0150 };
0151 
0152 static void __init init_sched_clock(void)
0153 {
0154     struct device_node *sched_timer;
0155 
0156     sched_timer = of_find_matching_node(NULL, sptimer_ids);
0157     if (sched_timer) {
0158         timer_get_base_and_rate(sched_timer, &sched_io_base,
0159                     &sched_rate);
0160         of_node_put(sched_timer);
0161     }
0162 
0163     sched_clock_register(read_sched_clock, 32, sched_rate);
0164 }
0165 
0166 #ifdef CONFIG_ARM
0167 static unsigned long dw_apb_delay_timer_read(void)
0168 {
0169     return ~readl_relaxed(sched_io_base);
0170 }
0171 
0172 static struct delay_timer dw_apb_delay_timer = {
0173     .read_current_timer = dw_apb_delay_timer_read,
0174 };
0175 #endif
0176 
0177 static int num_called;
0178 static int __init dw_apb_timer_init(struct device_node *timer)
0179 {
0180     int ret = 0;
0181 
0182     switch (num_called) {
0183     case 1:
0184         pr_debug("%s: found clocksource timer\n", __func__);
0185         ret = add_clocksource(timer);
0186         if (ret)
0187             return ret;
0188         init_sched_clock();
0189 #ifdef CONFIG_ARM
0190         dw_apb_delay_timer.freq = sched_rate;
0191         register_current_timer_delay(&dw_apb_delay_timer);
0192 #endif
0193         break;
0194     default:
0195         pr_debug("%s: found clockevent timer\n", __func__);
0196         ret = add_clockevent(timer);
0197         if (ret)
0198             return ret;
0199         break;
0200     }
0201 
0202     num_called++;
0203 
0204     return 0;
0205 }
0206 TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
0207 TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
0208 TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
0209 TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);