Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (C) 2012 Broadcom Corporation
0003 
0004 #include <linux/init.h>
0005 #include <linux/irq.h>
0006 #include <linux/interrupt.h>
0007 #include <linux/jiffies.h>
0008 #include <linux/clockchips.h>
0009 #include <linux/types.h>
0010 #include <linux/clk.h>
0011 
0012 #include <linux/io.h>
0013 
0014 #include <linux/of.h>
0015 #include <linux/of_address.h>
0016 #include <linux/of_irq.h>
0017 
0018 
0019 #define KONA_GPTIMER_STCS_OFFSET            0x00000000
0020 #define KONA_GPTIMER_STCLO_OFFSET           0x00000004
0021 #define KONA_GPTIMER_STCHI_OFFSET           0x00000008
0022 #define KONA_GPTIMER_STCM0_OFFSET           0x0000000C
0023 
0024 #define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT     0
0025 #define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT      4
0026 
0027 struct kona_bcm_timers {
0028     int tmr_irq;
0029     void __iomem *tmr_regs;
0030 };
0031 
0032 static struct kona_bcm_timers timers;
0033 
0034 static u32 arch_timer_rate;
0035 
0036 /*
0037  * We use the peripheral timers for system tick, the cpu global timer for
0038  * profile tick
0039  */
0040 static void kona_timer_disable_and_clear(void __iomem *base)
0041 {
0042     uint32_t reg;
0043 
0044     /*
0045      * clear and disable interrupts
0046      * We are using compare/match register 0 for our system interrupts
0047      */
0048     reg = readl(base + KONA_GPTIMER_STCS_OFFSET);
0049 
0050     /* Clear compare (0) interrupt */
0051     reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT;
0052     /* disable compare */
0053     reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
0054 
0055     writel(reg, base + KONA_GPTIMER_STCS_OFFSET);
0056 
0057 }
0058 
0059 static int
0060 kona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw)
0061 {
0062     int loop_limit = 3;
0063 
0064     /*
0065      * Read 64-bit free running counter
0066      * 1. Read hi-word
0067      * 2. Read low-word
0068      * 3. Read hi-word again
0069      * 4.1
0070      *      if new hi-word is not equal to previously read hi-word, then
0071      *      start from #1
0072      * 4.2
0073      *      if new hi-word is equal to previously read hi-word then stop.
0074      */
0075 
0076     do {
0077         *msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET);
0078         *lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET);
0079         if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET))
0080             break;
0081     } while (--loop_limit);
0082     if (!loop_limit) {
0083         pr_err("bcm_kona_timer: getting counter failed.\n");
0084         pr_err(" Timer will be impacted\n");
0085         return -ETIMEDOUT;
0086     }
0087 
0088     return 0;
0089 }
0090 
0091 static int kona_timer_set_next_event(unsigned long clc,
0092                   struct clock_event_device *unused)
0093 {
0094     /*
0095      * timer (0) is disabled by the timer interrupt already
0096      * so, here we reload the next event value and re-enable
0097      * the timer.
0098      *
0099      * This way, we are potentially losing the time between
0100      * timer-interrupt->set_next_event. CPU local timers, when
0101      * they come in should get rid of skew.
0102      */
0103 
0104     uint32_t lsw, msw;
0105     uint32_t reg;
0106     int ret;
0107 
0108     ret = kona_timer_get_counter(timers.tmr_regs, &msw, &lsw);
0109     if (ret)
0110         return ret;
0111 
0112     /* Load the "next" event tick value */
0113     writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET);
0114 
0115     /* Enable compare */
0116     reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
0117     reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
0118     writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
0119 
0120     return 0;
0121 }
0122 
0123 static int kona_timer_shutdown(struct clock_event_device *evt)
0124 {
0125     kona_timer_disable_and_clear(timers.tmr_regs);
0126     return 0;
0127 }
0128 
0129 static struct clock_event_device kona_clockevent_timer = {
0130     .name = "timer 1",
0131     .features = CLOCK_EVT_FEAT_ONESHOT,
0132     .set_next_event = kona_timer_set_next_event,
0133     .set_state_shutdown = kona_timer_shutdown,
0134     .tick_resume = kona_timer_shutdown,
0135 };
0136 
0137 static void __init kona_timer_clockevents_init(void)
0138 {
0139     kona_clockevent_timer.cpumask = cpumask_of(0);
0140     clockevents_config_and_register(&kona_clockevent_timer,
0141         arch_timer_rate, 6, 0xffffffff);
0142 }
0143 
0144 static irqreturn_t kona_timer_interrupt(int irq, void *dev_id)
0145 {
0146     struct clock_event_device *evt = &kona_clockevent_timer;
0147 
0148     kona_timer_disable_and_clear(timers.tmr_regs);
0149     evt->event_handler(evt);
0150     return IRQ_HANDLED;
0151 }
0152 
0153 static int __init kona_timer_init(struct device_node *node)
0154 {
0155     u32 freq;
0156     struct clk *external_clk;
0157 
0158     external_clk = of_clk_get_by_name(node, NULL);
0159 
0160     if (!IS_ERR(external_clk)) {
0161         arch_timer_rate = clk_get_rate(external_clk);
0162         clk_prepare_enable(external_clk);
0163     } else if (!of_property_read_u32(node, "clock-frequency", &freq)) {
0164         arch_timer_rate = freq;
0165     } else {
0166         pr_err("Kona Timer v1 unable to determine clock-frequency\n");
0167         return -EINVAL;
0168     }
0169 
0170     /* Setup IRQ numbers */
0171     timers.tmr_irq = irq_of_parse_and_map(node, 0);
0172 
0173     /* Setup IO addresses */
0174     timers.tmr_regs = of_iomap(node, 0);
0175 
0176     kona_timer_disable_and_clear(timers.tmr_regs);
0177 
0178     kona_timer_clockevents_init();
0179     if (request_irq(timers.tmr_irq, kona_timer_interrupt, IRQF_TIMER,
0180             "Kona Timer Tick", NULL))
0181         pr_err("%s: request_irq() failed\n", "Kona Timer Tick");
0182     kona_timer_set_next_event((arch_timer_rate / HZ), NULL);
0183 
0184     return 0;
0185 }
0186 
0187 TIMER_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
0188 /*
0189  * bcm,kona-timer is deprecated by brcm,kona-timer
0190  * being kept here for driver compatibility
0191  */
0192 TIMER_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);