| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Allwinner SoCs hstimer driver. |
| 4 | * |
| 5 | * Copyright (C) 2013 Maxime Ripard |
| 6 | * |
| 7 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
| 8 | */ |
| 9 | |
| 10 | #include <linux/clk.h> |
| 11 | #include <linux/clockchips.h> |
| 12 | #include <linux/clocksource.h> |
| 13 | #include <linux/delay.h> |
| 14 | #include <linux/interrupt.h> |
| 15 | #include <linux/irq.h> |
| 16 | #include <linux/irqreturn.h> |
| 17 | #include <linux/reset.h> |
| 18 | #include <linux/slab.h> |
| 19 | #include <linux/platform_device.h> |
| 20 | |
| 21 | #define TIMER_IRQ_EN_REG 0x00 |
| 22 | #define TIMER_IRQ_EN(val) BIT(val) |
| 23 | #define TIMER_IRQ_ST_REG 0x04 |
| 24 | #define TIMER_CTL_REG(val) (0x20 * (val) + 0x10) |
| 25 | #define TIMER_CTL_ENABLE BIT(0) |
| 26 | #define TIMER_CTL_RELOAD BIT(1) |
| 27 | #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) |
| 28 | #define TIMER_CTL_ONESHOT BIT(7) |
| 29 | #define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14) |
| 30 | #define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18) |
| 31 | #define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c) |
| 32 | #define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20) |
| 33 | |
| 34 | #define TIMER_SYNC_TICKS 3 |
| 35 | |
| 36 | struct sun5i_timer { |
| 37 | void __iomem *base; |
| 38 | struct clk *clk; |
| 39 | struct notifier_block clk_rate_cb; |
| 40 | u32 ticks_per_jiffy; |
| 41 | struct clocksource clksrc; |
| 42 | struct clock_event_device clkevt; |
| 43 | }; |
| 44 | |
| 45 | #define nb_to_sun5i_timer(x) \ |
| 46 | container_of(x, struct sun5i_timer, clk_rate_cb) |
| 47 | #define clksrc_to_sun5i_timer(x) \ |
| 48 | container_of(x, struct sun5i_timer, clksrc) |
| 49 | #define clkevt_to_sun5i_timer(x) \ |
| 50 | container_of(x, struct sun5i_timer, clkevt) |
| 51 | |
| 52 | /* |
| 53 | * When we disable a timer, we need to wait at least for 2 cycles of |
| 54 | * the timer source clock. We will use for that the clocksource timer |
| 55 | * that is already setup and runs at the same frequency than the other |
| 56 | * timers, and we never will be disabled. |
| 57 | */ |
| 58 | static void sun5i_clkevt_sync(struct sun5i_timer *ce) |
| 59 | { |
| 60 | u32 old = readl(addr: ce->base + TIMER_CNTVAL_LO_REG(1)); |
| 61 | |
| 62 | while ((old - readl(addr: ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) |
| 63 | cpu_relax(); |
| 64 | } |
| 65 | |
| 66 | static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer) |
| 67 | { |
| 68 | u32 val = readl(addr: ce->base + TIMER_CTL_REG(timer)); |
| 69 | writel(val: val & ~TIMER_CTL_ENABLE, addr: ce->base + TIMER_CTL_REG(timer)); |
| 70 | |
| 71 | sun5i_clkevt_sync(ce); |
| 72 | } |
| 73 | |
| 74 | static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay) |
| 75 | { |
| 76 | writel(val: delay, addr: ce->base + TIMER_INTVAL_LO_REG(timer)); |
| 77 | } |
| 78 | |
| 79 | static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic) |
| 80 | { |
| 81 | u32 val = readl(addr: ce->base + TIMER_CTL_REG(timer)); |
| 82 | |
| 83 | if (periodic) |
| 84 | val &= ~TIMER_CTL_ONESHOT; |
| 85 | else |
| 86 | val |= TIMER_CTL_ONESHOT; |
| 87 | |
| 88 | writel(val: val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
| 89 | addr: ce->base + TIMER_CTL_REG(timer)); |
| 90 | } |
| 91 | |
| 92 | static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) |
| 93 | { |
| 94 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
| 95 | |
| 96 | sun5i_clkevt_time_stop(ce, timer: 0); |
| 97 | return 0; |
| 98 | } |
| 99 | |
| 100 | static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt) |
| 101 | { |
| 102 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
| 103 | |
| 104 | sun5i_clkevt_time_stop(ce, timer: 0); |
| 105 | sun5i_clkevt_time_start(ce, timer: 0, periodic: false); |
| 106 | return 0; |
| 107 | } |
| 108 | |
| 109 | static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt) |
| 110 | { |
| 111 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
| 112 | |
| 113 | sun5i_clkevt_time_stop(ce, timer: 0); |
| 114 | sun5i_clkevt_time_setup(ce, timer: 0, delay: ce->ticks_per_jiffy); |
| 115 | sun5i_clkevt_time_start(ce, timer: 0, periodic: true); |
| 116 | return 0; |
| 117 | } |
| 118 | |
| 119 | static int sun5i_clkevt_next_event(unsigned long evt, |
| 120 | struct clock_event_device *clkevt) |
| 121 | { |
| 122 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
| 123 | |
| 124 | sun5i_clkevt_time_stop(ce, timer: 0); |
| 125 | sun5i_clkevt_time_setup(ce, timer: 0, delay: evt - TIMER_SYNC_TICKS); |
| 126 | sun5i_clkevt_time_start(ce, timer: 0, periodic: false); |
| 127 | |
| 128 | return 0; |
| 129 | } |
| 130 | |
| 131 | static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) |
| 132 | { |
| 133 | struct sun5i_timer *ce = dev_id; |
| 134 | |
| 135 | writel(val: 0x1, addr: ce->base + TIMER_IRQ_ST_REG); |
| 136 | ce->clkevt.event_handler(&ce->clkevt); |
| 137 | |
| 138 | return IRQ_HANDLED; |
| 139 | } |
| 140 | |
| 141 | static u64 sun5i_clksrc_read(struct clocksource *clksrc) |
| 142 | { |
| 143 | struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc); |
| 144 | |
| 145 | return ~readl(addr: cs->base + TIMER_CNTVAL_LO_REG(1)); |
| 146 | } |
| 147 | |
| 148 | static int sun5i_rate_cb(struct notifier_block *nb, |
| 149 | unsigned long event, void *data) |
| 150 | { |
| 151 | struct clk_notifier_data *ndata = data; |
| 152 | struct sun5i_timer *cs = nb_to_sun5i_timer(nb); |
| 153 | |
| 154 | switch (event) { |
| 155 | case PRE_RATE_CHANGE: |
| 156 | clocksource_unregister(&cs->clksrc); |
| 157 | break; |
| 158 | |
| 159 | case POST_RATE_CHANGE: |
| 160 | clocksource_register_hz(cs: &cs->clksrc, hz: ndata->new_rate); |
| 161 | clockevents_update_freq(ce: &cs->clkevt, freq: ndata->new_rate); |
| 162 | cs->ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); |
| 163 | break; |
| 164 | |
| 165 | default: |
| 166 | break; |
| 167 | } |
| 168 | |
| 169 | return NOTIFY_DONE; |
| 170 | } |
| 171 | |
| 172 | static int sun5i_setup_clocksource(struct platform_device *pdev, |
| 173 | unsigned long rate) |
| 174 | { |
| 175 | struct sun5i_timer *cs = platform_get_drvdata(pdev); |
| 176 | void __iomem *base = cs->base; |
| 177 | int ret; |
| 178 | |
| 179 | writel(val: ~0, addr: base + TIMER_INTVAL_LO_REG(1)); |
| 180 | writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
| 181 | addr: base + TIMER_CTL_REG(1)); |
| 182 | |
| 183 | cs->clksrc.name = pdev->dev.of_node->name; |
| 184 | cs->clksrc.rating = 340; |
| 185 | cs->clksrc.read = sun5i_clksrc_read; |
| 186 | cs->clksrc.mask = CLOCKSOURCE_MASK(32); |
| 187 | cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; |
| 188 | cs->clksrc.owner = THIS_MODULE; |
| 189 | |
| 190 | ret = clocksource_register_hz(cs: &cs->clksrc, hz: rate); |
| 191 | if (ret) { |
| 192 | dev_err(&pdev->dev, "Couldn't register clock source.\n" ); |
| 193 | return ret; |
| 194 | } |
| 195 | |
| 196 | return 0; |
| 197 | } |
| 198 | |
| 199 | static int sun5i_setup_clockevent(struct platform_device *pdev, |
| 200 | unsigned long rate, int irq) |
| 201 | { |
| 202 | struct device *dev = &pdev->dev; |
| 203 | struct sun5i_timer *ce = platform_get_drvdata(pdev); |
| 204 | void __iomem *base = ce->base; |
| 205 | int ret; |
| 206 | u32 val; |
| 207 | |
| 208 | ce->clkevt.name = dev->of_node->name; |
| 209 | ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; |
| 210 | ce->clkevt.set_next_event = sun5i_clkevt_next_event; |
| 211 | ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown; |
| 212 | ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic; |
| 213 | ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot; |
| 214 | ce->clkevt.tick_resume = sun5i_clkevt_shutdown; |
| 215 | ce->clkevt.rating = 340; |
| 216 | ce->clkevt.irq = irq; |
| 217 | ce->clkevt.cpumask = cpu_possible_mask; |
| 218 | ce->clkevt.owner = THIS_MODULE; |
| 219 | |
| 220 | /* Enable timer0 interrupt */ |
| 221 | val = readl(addr: base + TIMER_IRQ_EN_REG); |
| 222 | writel(val: val | TIMER_IRQ_EN(0), addr: base + TIMER_IRQ_EN_REG); |
| 223 | |
| 224 | clockevents_config_and_register(dev: &ce->clkevt, freq: rate, |
| 225 | TIMER_SYNC_TICKS, max_delta: 0xffffffff); |
| 226 | |
| 227 | ret = devm_request_irq(dev, irq, handler: sun5i_timer_interrupt, |
| 228 | IRQF_TIMER | IRQF_IRQPOLL, |
| 229 | devname: "sun5i_timer0" , dev_id: ce); |
| 230 | if (ret) { |
| 231 | dev_err(dev, "Unable to register interrupt\n" ); |
| 232 | return ret; |
| 233 | } |
| 234 | |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | static int sun5i_timer_probe(struct platform_device *pdev) |
| 239 | { |
| 240 | struct device *dev = &pdev->dev; |
| 241 | struct sun5i_timer *st; |
| 242 | struct reset_control *rstc; |
| 243 | void __iomem *timer_base; |
| 244 | struct clk *clk; |
| 245 | unsigned long rate; |
| 246 | int irq, ret; |
| 247 | |
| 248 | st = devm_kzalloc(dev, size: sizeof(*st), GFP_KERNEL); |
| 249 | if (!st) |
| 250 | return -ENOMEM; |
| 251 | |
| 252 | platform_set_drvdata(pdev, data: st); |
| 253 | |
| 254 | timer_base = devm_platform_ioremap_resource(pdev, index: 0); |
| 255 | if (IS_ERR(ptr: timer_base)) { |
| 256 | dev_err(dev, "Can't map registers\n" ); |
| 257 | return PTR_ERR(ptr: timer_base); |
| 258 | } |
| 259 | |
| 260 | irq = platform_get_irq(pdev, 0); |
| 261 | if (irq < 0) |
| 262 | return irq; |
| 263 | |
| 264 | clk = devm_clk_get_enabled(dev, NULL); |
| 265 | if (IS_ERR(ptr: clk)) { |
| 266 | dev_err(dev, "Can't get timer clock\n" ); |
| 267 | return PTR_ERR(ptr: clk); |
| 268 | } |
| 269 | |
| 270 | rate = clk_get_rate(clk); |
| 271 | if (!rate) { |
| 272 | dev_err(dev, "Couldn't get parent clock rate\n" ); |
| 273 | return -EINVAL; |
| 274 | } |
| 275 | |
| 276 | st->base = timer_base; |
| 277 | st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); |
| 278 | st->clk = clk; |
| 279 | st->clk_rate_cb.notifier_call = sun5i_rate_cb; |
| 280 | st->clk_rate_cb.next = NULL; |
| 281 | |
| 282 | ret = devm_clk_notifier_register(dev, clk, nb: &st->clk_rate_cb); |
| 283 | if (ret) { |
| 284 | dev_err(dev, "Unable to register clock notifier.\n" ); |
| 285 | return ret; |
| 286 | } |
| 287 | |
| 288 | rstc = devm_reset_control_get_optional_exclusive(dev, NULL); |
| 289 | if (rstc) |
| 290 | reset_control_deassert(rstc); |
| 291 | |
| 292 | ret = sun5i_setup_clocksource(pdev, rate); |
| 293 | if (ret) |
| 294 | return ret; |
| 295 | |
| 296 | ret = sun5i_setup_clockevent(pdev, rate, irq); |
| 297 | if (ret) |
| 298 | goto err_unreg_clocksource; |
| 299 | |
| 300 | return 0; |
| 301 | |
| 302 | err_unreg_clocksource: |
| 303 | clocksource_unregister(&st->clksrc); |
| 304 | return ret; |
| 305 | } |
| 306 | |
| 307 | static void sun5i_timer_remove(struct platform_device *pdev) |
| 308 | { |
| 309 | struct sun5i_timer *st = platform_get_drvdata(pdev); |
| 310 | |
| 311 | clocksource_unregister(&st->clksrc); |
| 312 | } |
| 313 | |
| 314 | static const struct of_device_id sun5i_timer_of_match[] = { |
| 315 | { .compatible = "allwinner,sun5i-a13-hstimer" }, |
| 316 | { .compatible = "allwinner,sun7i-a20-hstimer" }, |
| 317 | {}, |
| 318 | }; |
| 319 | MODULE_DEVICE_TABLE(of, sun5i_timer_of_match); |
| 320 | |
| 321 | static struct platform_driver sun5i_timer_driver = { |
| 322 | .probe = sun5i_timer_probe, |
| 323 | .remove = sun5i_timer_remove, |
| 324 | .driver = { |
| 325 | .name = "sun5i-timer" , |
| 326 | .of_match_table = sun5i_timer_of_match, |
| 327 | .suppress_bind_attrs = true, |
| 328 | }, |
| 329 | }; |
| 330 | module_platform_driver(sun5i_timer_driver); |
| 331 | |