Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * RDA8810PL SoC timer driver
0004  *
0005  * Copyright RDA Microelectronics Company Limited
0006  * Copyright (c) 2017 Andreas Färber
0007  * Copyright (c) 2018 Manivannan Sadhasivam
0008  *
0009  * RDA8810PL has two independent timers: OSTIMER (56 bit) and HWTIMER (64 bit).
0010  * Each timer provides optional interrupt support. In this driver, OSTIMER is
0011  * used for clockevents and HWTIMER is used for clocksource.
0012  */
0013 
0014 #include <linux/init.h>
0015 #include <linux/interrupt.h>
0016 
0017 #include "timer-of.h"
0018 
0019 #define RDA_OSTIMER_LOADVAL_L   0x000
0020 #define RDA_OSTIMER_CTRL    0x004
0021 #define RDA_HWTIMER_LOCKVAL_L   0x024
0022 #define RDA_HWTIMER_LOCKVAL_H   0x028
0023 #define RDA_TIMER_IRQ_MASK_SET  0x02c
0024 #define RDA_TIMER_IRQ_MASK_CLR  0x030
0025 #define RDA_TIMER_IRQ_CLR   0x034
0026 
0027 #define RDA_OSTIMER_CTRL_ENABLE     BIT(24)
0028 #define RDA_OSTIMER_CTRL_REPEAT     BIT(28)
0029 #define RDA_OSTIMER_CTRL_LOAD       BIT(30)
0030 
0031 #define RDA_TIMER_IRQ_MASK_OSTIMER  BIT(0)
0032 
0033 #define RDA_TIMER_IRQ_CLR_OSTIMER   BIT(0)
0034 
0035 static int rda_ostimer_start(void __iomem *base, bool periodic, u64 cycles)
0036 {
0037     u32 ctrl, load_l;
0038 
0039     load_l = (u32)cycles;
0040     ctrl = ((cycles >> 32) & 0xffffff);
0041     ctrl |= RDA_OSTIMER_CTRL_LOAD | RDA_OSTIMER_CTRL_ENABLE;
0042     if (periodic)
0043         ctrl |= RDA_OSTIMER_CTRL_REPEAT;
0044 
0045     /* Enable ostimer interrupt first */
0046     writel_relaxed(RDA_TIMER_IRQ_MASK_OSTIMER,
0047                base + RDA_TIMER_IRQ_MASK_SET);
0048 
0049     /* Write low 32 bits first, high 24 bits are with ctrl */
0050     writel_relaxed(load_l, base + RDA_OSTIMER_LOADVAL_L);
0051     writel_relaxed(ctrl, base + RDA_OSTIMER_CTRL);
0052 
0053     return 0;
0054 }
0055 
0056 static int rda_ostimer_stop(void __iomem *base)
0057 {
0058     /* Disable ostimer interrupt first */
0059     writel_relaxed(RDA_TIMER_IRQ_MASK_OSTIMER,
0060                base + RDA_TIMER_IRQ_MASK_CLR);
0061 
0062     writel_relaxed(0, base + RDA_OSTIMER_CTRL);
0063 
0064     return 0;
0065 }
0066 
0067 static int rda_ostimer_set_state_shutdown(struct clock_event_device *evt)
0068 {
0069     struct timer_of *to = to_timer_of(evt);
0070 
0071     rda_ostimer_stop(timer_of_base(to));
0072 
0073     return 0;
0074 }
0075 
0076 static int rda_ostimer_set_state_oneshot(struct clock_event_device *evt)
0077 {
0078     struct timer_of *to = to_timer_of(evt);
0079 
0080     rda_ostimer_stop(timer_of_base(to));
0081 
0082     return 0;
0083 }
0084 
0085 static int rda_ostimer_set_state_periodic(struct clock_event_device *evt)
0086 {
0087     struct timer_of *to = to_timer_of(evt);
0088     unsigned long cycles_per_jiffy;
0089 
0090     rda_ostimer_stop(timer_of_base(to));
0091 
0092     cycles_per_jiffy = ((unsigned long long)NSEC_PER_SEC / HZ *
0093                  evt->mult) >> evt->shift;
0094     rda_ostimer_start(timer_of_base(to), true, cycles_per_jiffy);
0095 
0096     return 0;
0097 }
0098 
0099 static int rda_ostimer_tick_resume(struct clock_event_device *evt)
0100 {
0101     return 0;
0102 }
0103 
0104 static int rda_ostimer_set_next_event(unsigned long evt,
0105                       struct clock_event_device *ev)
0106 {
0107     struct timer_of *to = to_timer_of(ev);
0108 
0109     rda_ostimer_start(timer_of_base(to), false, evt);
0110 
0111     return 0;
0112 }
0113 
0114 static irqreturn_t rda_ostimer_interrupt(int irq, void *dev_id)
0115 {
0116     struct clock_event_device *evt = dev_id;
0117     struct timer_of *to = to_timer_of(evt);
0118 
0119     /* clear timer int */
0120     writel_relaxed(RDA_TIMER_IRQ_CLR_OSTIMER,
0121                timer_of_base(to) + RDA_TIMER_IRQ_CLR);
0122 
0123     if (evt->event_handler)
0124         evt->event_handler(evt);
0125 
0126     return IRQ_HANDLED;
0127 }
0128 
0129 static struct timer_of rda_ostimer_of = {
0130     .flags = TIMER_OF_IRQ | TIMER_OF_BASE,
0131 
0132     .clkevt = {
0133         .name = "rda-ostimer",
0134         .rating = 250,
0135         .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
0136                 CLOCK_EVT_FEAT_DYNIRQ,
0137         .set_state_shutdown = rda_ostimer_set_state_shutdown,
0138         .set_state_oneshot = rda_ostimer_set_state_oneshot,
0139         .set_state_periodic = rda_ostimer_set_state_periodic,
0140         .tick_resume = rda_ostimer_tick_resume,
0141         .set_next_event = rda_ostimer_set_next_event,
0142     },
0143 
0144     .of_base = {
0145         .name = "rda-timer",
0146         .index = 0,
0147     },
0148 
0149     .of_irq = {
0150         .name = "ostimer",
0151         .handler = rda_ostimer_interrupt,
0152         .flags = IRQF_TIMER,
0153     },
0154 };
0155 
0156 static u64 rda_hwtimer_read(struct clocksource *cs)
0157 {
0158     void __iomem *base = timer_of_base(&rda_ostimer_of);
0159     u32 lo, hi;
0160 
0161     /* Always read low 32 bits first */
0162     do {
0163         lo = readl_relaxed(base + RDA_HWTIMER_LOCKVAL_L);
0164         hi = readl_relaxed(base + RDA_HWTIMER_LOCKVAL_H);
0165     } while (hi != readl_relaxed(base + RDA_HWTIMER_LOCKVAL_H));
0166 
0167     return ((u64)hi << 32) | lo;
0168 }
0169 
0170 static struct clocksource rda_hwtimer_clocksource = {
0171     .name           = "rda-timer",
0172     .rating         = 400,
0173     .read           = rda_hwtimer_read,
0174     .mask           = CLOCKSOURCE_MASK(64),
0175     .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
0176 };
0177 
0178 static int __init rda_timer_init(struct device_node *np)
0179 {
0180     unsigned long rate = 2000000;
0181     int ret;
0182 
0183     ret = timer_of_init(np, &rda_ostimer_of);
0184     if (ret)
0185         return ret;
0186 
0187     clocksource_register_hz(&rda_hwtimer_clocksource, rate);
0188 
0189     clockevents_config_and_register(&rda_ostimer_of.clkevt, rate,
0190                     0x2, UINT_MAX);
0191 
0192     return 0;
0193 }
0194 
0195 TIMER_OF_DECLARE(rda8810pl, "rda,8810pl-timer", rda_timer_init);