Manivannan Sadhasivam | 7f83a13 | 2018-12-10 23:05:46 +0530 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * RDA8810PL SoC timer driver |
| 4 | * |
| 5 | * Copyright RDA Microelectronics Company Limited |
| 6 | * Copyright (c) 2017 Andreas Färber |
| 7 | * Copyright (c) 2018 Manivannan Sadhasivam |
| 8 | * |
| 9 | * RDA8810PL has two independent timers: OSTIMER (56 bit) and HWTIMER (64 bit). |
| 10 | * Each timer provides optional interrupt support. In this driver, OSTIMER is |
| 11 | * used for clockevents and HWTIMER is used for clocksource. |
| 12 | */ |
| 13 | |
| 14 | #include <linux/init.h> |
| 15 | #include <linux/interrupt.h> |
| 16 | |
| 17 | #include "timer-of.h" |
| 18 | |
| 19 | #define RDA_OSTIMER_LOADVAL_L 0x000 |
| 20 | #define RDA_OSTIMER_CTRL 0x004 |
| 21 | #define RDA_HWTIMER_LOCKVAL_L 0x024 |
| 22 | #define RDA_HWTIMER_LOCKVAL_H 0x028 |
| 23 | #define RDA_TIMER_IRQ_MASK_SET 0x02c |
| 24 | #define RDA_TIMER_IRQ_MASK_CLR 0x030 |
| 25 | #define RDA_TIMER_IRQ_CLR 0x034 |
| 26 | |
| 27 | #define RDA_OSTIMER_CTRL_ENABLE BIT(24) |
| 28 | #define RDA_OSTIMER_CTRL_REPEAT BIT(28) |
| 29 | #define RDA_OSTIMER_CTRL_LOAD BIT(30) |
| 30 | |
| 31 | #define RDA_TIMER_IRQ_MASK_OSTIMER BIT(0) |
| 32 | |
| 33 | #define RDA_TIMER_IRQ_CLR_OSTIMER BIT(0) |
| 34 | |
| 35 | static int rda_ostimer_start(void __iomem *base, bool periodic, u64 cycles) |
| 36 | { |
| 37 | u32 ctrl, load_l; |
| 38 | |
| 39 | load_l = (u32)cycles; |
| 40 | ctrl = ((cycles >> 32) & 0xffffff); |
| 41 | ctrl |= RDA_OSTIMER_CTRL_LOAD | RDA_OSTIMER_CTRL_ENABLE; |
| 42 | if (periodic) |
| 43 | ctrl |= RDA_OSTIMER_CTRL_REPEAT; |
| 44 | |
| 45 | /* Enable ostimer interrupt first */ |
| 46 | writel_relaxed(RDA_TIMER_IRQ_MASK_OSTIMER, |
| 47 | base + RDA_TIMER_IRQ_MASK_SET); |
| 48 | |
| 49 | /* Write low 32 bits first, high 24 bits are with ctrl */ |
| 50 | writel_relaxed(load_l, base + RDA_OSTIMER_LOADVAL_L); |
| 51 | writel_relaxed(ctrl, base + RDA_OSTIMER_CTRL); |
| 52 | |
| 53 | return 0; |
| 54 | } |
| 55 | |
| 56 | static int rda_ostimer_stop(void __iomem *base) |
| 57 | { |
| 58 | /* Disable ostimer interrupt first */ |
| 59 | writel_relaxed(RDA_TIMER_IRQ_MASK_OSTIMER, |
| 60 | base + RDA_TIMER_IRQ_MASK_CLR); |
| 61 | |
| 62 | writel_relaxed(0, base + RDA_OSTIMER_CTRL); |
| 63 | |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | static int rda_ostimer_set_state_shutdown(struct clock_event_device *evt) |
| 68 | { |
| 69 | struct timer_of *to = to_timer_of(evt); |
| 70 | |
| 71 | rda_ostimer_stop(timer_of_base(to)); |
| 72 | |
| 73 | return 0; |
| 74 | } |
| 75 | |
| 76 | static int rda_ostimer_set_state_oneshot(struct clock_event_device *evt) |
| 77 | { |
| 78 | struct timer_of *to = to_timer_of(evt); |
| 79 | |
| 80 | rda_ostimer_stop(timer_of_base(to)); |
| 81 | |
| 82 | return 0; |
| 83 | } |
| 84 | |
| 85 | static int rda_ostimer_set_state_periodic(struct clock_event_device *evt) |
| 86 | { |
| 87 | struct timer_of *to = to_timer_of(evt); |
| 88 | unsigned long cycles_per_jiffy; |
| 89 | |
| 90 | rda_ostimer_stop(timer_of_base(to)); |
| 91 | |
| 92 | cycles_per_jiffy = ((unsigned long long)NSEC_PER_SEC / HZ * |
| 93 | evt->mult) >> evt->shift; |
| 94 | rda_ostimer_start(timer_of_base(to), true, cycles_per_jiffy); |
| 95 | |
| 96 | return 0; |
| 97 | } |
| 98 | |
| 99 | static int rda_ostimer_tick_resume(struct clock_event_device *evt) |
| 100 | { |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | static int rda_ostimer_set_next_event(unsigned long evt, |
| 105 | struct clock_event_device *ev) |
| 106 | { |
| 107 | struct timer_of *to = to_timer_of(ev); |
| 108 | |
| 109 | rda_ostimer_start(timer_of_base(to), false, evt); |
| 110 | |
| 111 | return 0; |
| 112 | } |
| 113 | |
| 114 | static irqreturn_t rda_ostimer_interrupt(int irq, void *dev_id) |
| 115 | { |
| 116 | struct clock_event_device *evt = dev_id; |
| 117 | struct timer_of *to = to_timer_of(evt); |
| 118 | |
| 119 | /* clear timer int */ |
| 120 | writel_relaxed(RDA_TIMER_IRQ_CLR_OSTIMER, |
| 121 | timer_of_base(to) + RDA_TIMER_IRQ_CLR); |
| 122 | |
| 123 | if (evt->event_handler) |
| 124 | evt->event_handler(evt); |
| 125 | |
| 126 | return IRQ_HANDLED; |
| 127 | } |
| 128 | |
| 129 | static struct timer_of rda_ostimer_of = { |
| 130 | .flags = TIMER_OF_IRQ | TIMER_OF_BASE, |
| 131 | |
| 132 | .clkevt = { |
| 133 | .name = "rda-ostimer", |
| 134 | .rating = 250, |
| 135 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | |
| 136 | CLOCK_EVT_FEAT_DYNIRQ, |
| 137 | .set_state_shutdown = rda_ostimer_set_state_shutdown, |
| 138 | .set_state_oneshot = rda_ostimer_set_state_oneshot, |
| 139 | .set_state_periodic = rda_ostimer_set_state_periodic, |
| 140 | .tick_resume = rda_ostimer_tick_resume, |
| 141 | .set_next_event = rda_ostimer_set_next_event, |
| 142 | }, |
| 143 | |
| 144 | .of_base = { |
| 145 | .name = "rda-timer", |
| 146 | .index = 0, |
| 147 | }, |
| 148 | |
| 149 | .of_irq = { |
| 150 | .name = "ostimer", |
| 151 | .handler = rda_ostimer_interrupt, |
| 152 | .flags = IRQF_TIMER, |
| 153 | }, |
| 154 | }; |
| 155 | |
| 156 | static u64 rda_hwtimer_read(struct clocksource *cs) |
| 157 | { |
| 158 | void __iomem *base = timer_of_base(&rda_ostimer_of); |
| 159 | u32 lo, hi; |
| 160 | |
| 161 | /* Always read low 32 bits first */ |
| 162 | do { |
| 163 | lo = readl_relaxed(base + RDA_HWTIMER_LOCKVAL_L); |
| 164 | hi = readl_relaxed(base + RDA_HWTIMER_LOCKVAL_H); |
| 165 | } while (hi != readl_relaxed(base + RDA_HWTIMER_LOCKVAL_H)); |
| 166 | |
| 167 | return ((u64)hi << 32) | lo; |
| 168 | } |
| 169 | |
| 170 | static struct clocksource rda_hwtimer_clocksource = { |
| 171 | .name = "rda-timer", |
| 172 | .rating = 400, |
| 173 | .read = rda_hwtimer_read, |
| 174 | .mask = CLOCKSOURCE_MASK(64), |
| 175 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 176 | }; |
| 177 | |
| 178 | static int __init rda_timer_init(struct device_node *np) |
| 179 | { |
| 180 | unsigned long rate = 2000000; |
| 181 | int ret; |
| 182 | |
| 183 | ret = timer_of_init(np, &rda_ostimer_of); |
| 184 | if (ret) |
| 185 | return ret; |
| 186 | |
| 187 | clocksource_register_hz(&rda_hwtimer_clocksource, rate); |
| 188 | |
| 189 | clockevents_config_and_register(&rda_ostimer_of.clkevt, rate, |
| 190 | 0x2, UINT_MAX); |
| 191 | |
| 192 | return 0; |
| 193 | } |
| 194 | |
| 195 | TIMER_OF_DECLARE(rda8810pl, "rda,8810pl-timer", rda_timer_init); |