1a74dfa43SThomas Gleixner // SPDX-License-Identifier: GPL-2.0
267905540SMaxime Ripard /*
367905540SMaxime Ripard * Allwinner SoCs hstimer driver.
467905540SMaxime Ripard *
567905540SMaxime Ripard * Copyright (C) 2013 Maxime Ripard
667905540SMaxime Ripard *
767905540SMaxime Ripard * Maxime Ripard <maxime.ripard@free-electrons.com>
867905540SMaxime Ripard */
967905540SMaxime Ripard
1067905540SMaxime Ripard #include <linux/clk.h>
1167905540SMaxime Ripard #include <linux/clockchips.h>
12459fa246SStephen Rothwell #include <linux/clocksource.h>
1367905540SMaxime Ripard #include <linux/delay.h>
1467905540SMaxime Ripard #include <linux/interrupt.h>
1567905540SMaxime Ripard #include <linux/irq.h>
1667905540SMaxime Ripard #include <linux/irqreturn.h>
17e50a00beSMaxime Ripard #include <linux/reset.h>
184a59058fSMaxime Ripard #include <linux/slab.h>
197e5bac61SMans Rullgard #include <linux/platform_device.h>
2067905540SMaxime Ripard
2167905540SMaxime Ripard #define TIMER_IRQ_EN_REG 0x00
2267905540SMaxime Ripard #define TIMER_IRQ_EN(val) BIT(val)
2367905540SMaxime Ripard #define TIMER_IRQ_ST_REG 0x04
2467905540SMaxime Ripard #define TIMER_CTL_REG(val) (0x20 * (val) + 0x10)
2567905540SMaxime Ripard #define TIMER_CTL_ENABLE BIT(0)
2667905540SMaxime Ripard #define TIMER_CTL_RELOAD BIT(1)
2767905540SMaxime Ripard #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
2867905540SMaxime Ripard #define TIMER_CTL_ONESHOT BIT(7)
2967905540SMaxime Ripard #define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14)
3067905540SMaxime Ripard #define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18)
3167905540SMaxime Ripard #define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c)
3267905540SMaxime Ripard #define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20)
3367905540SMaxime Ripard
3467905540SMaxime Ripard #define TIMER_SYNC_TICKS 3
3567905540SMaxime Ripard
360b38dd17SMans Rullgard struct sun5i_timer {
374a59058fSMaxime Ripard void __iomem *base;
384a59058fSMaxime Ripard struct clk *clk;
393071efa4SMaxime Ripard struct notifier_block clk_rate_cb;
404a59058fSMaxime Ripard u32 ticks_per_jiffy;
414a59058fSMaxime Ripard struct clocksource clksrc;
424a59058fSMaxime Ripard struct clock_event_device clkevt;
434a59058fSMaxime Ripard };
444a59058fSMaxime Ripard
457ded8038SMans Rullgard #define nb_to_sun5i_timer(x) \
460b38dd17SMans Rullgard container_of(x, struct sun5i_timer, clk_rate_cb)
477ded8038SMans Rullgard #define clksrc_to_sun5i_timer(x) \
487ded8038SMans Rullgard container_of(x, struct sun5i_timer, clksrc)
497ded8038SMans Rullgard #define clkevt_to_sun5i_timer(x) \
507ded8038SMans Rullgard container_of(x, struct sun5i_timer, clkevt)
5167905540SMaxime Ripard
5267905540SMaxime Ripard /*
5367905540SMaxime Ripard * When we disable a timer, we need to wait at least for 2 cycles of
5467905540SMaxime Ripard * the timer source clock. We will use for that the clocksource timer
5567905540SMaxime Ripard * that is already setup and runs at the same frequency than the other
5667905540SMaxime Ripard * timers, and we never will be disabled.
5767905540SMaxime Ripard */
sun5i_clkevt_sync(struct sun5i_timer * ce)587ded8038SMans Rullgard static void sun5i_clkevt_sync(struct sun5i_timer *ce)
5967905540SMaxime Ripard {
600b38dd17SMans Rullgard u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1));
6167905540SMaxime Ripard
620b38dd17SMans Rullgard while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS)
6367905540SMaxime Ripard cpu_relax();
6467905540SMaxime Ripard }
6567905540SMaxime Ripard
sun5i_clkevt_time_stop(struct sun5i_timer * ce,u8 timer)667ded8038SMans Rullgard static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer)
6767905540SMaxime Ripard {
680b38dd17SMans Rullgard u32 val = readl(ce->base + TIMER_CTL_REG(timer));
690b38dd17SMans Rullgard writel(val & ~TIMER_CTL_ENABLE, ce->base + TIMER_CTL_REG(timer));
7067905540SMaxime Ripard
714a59058fSMaxime Ripard sun5i_clkevt_sync(ce);
7267905540SMaxime Ripard }
7367905540SMaxime Ripard
sun5i_clkevt_time_setup(struct sun5i_timer * ce,u8 timer,u32 delay)747ded8038SMans Rullgard static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay)
7567905540SMaxime Ripard {
760b38dd17SMans Rullgard writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer));
7767905540SMaxime Ripard }
7867905540SMaxime Ripard
sun5i_clkevt_time_start(struct sun5i_timer * ce,u8 timer,bool periodic)797ded8038SMans Rullgard static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic)
8067905540SMaxime Ripard {
810b38dd17SMans Rullgard u32 val = readl(ce->base + TIMER_CTL_REG(timer));
8267905540SMaxime Ripard
8367905540SMaxime Ripard if (periodic)
8467905540SMaxime Ripard val &= ~TIMER_CTL_ONESHOT;
8567905540SMaxime Ripard else
8667905540SMaxime Ripard val |= TIMER_CTL_ONESHOT;
8767905540SMaxime Ripard
8867905540SMaxime Ripard writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
890b38dd17SMans Rullgard ce->base + TIMER_CTL_REG(timer));
9067905540SMaxime Ripard }
9167905540SMaxime Ripard
sun5i_clkevt_shutdown(struct clock_event_device * clkevt)927486f5adSViresh Kumar static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt)
9367905540SMaxime Ripard {
947ded8038SMans Rullgard struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);
954a59058fSMaxime Ripard
967486f5adSViresh Kumar sun5i_clkevt_time_stop(ce, 0);
977486f5adSViresh Kumar return 0;
987486f5adSViresh Kumar }
997486f5adSViresh Kumar
sun5i_clkevt_set_oneshot(struct clock_event_device * clkevt)1007486f5adSViresh Kumar static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt)
1017486f5adSViresh Kumar {
1027ded8038SMans Rullgard struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);
1037486f5adSViresh Kumar
1047486f5adSViresh Kumar sun5i_clkevt_time_stop(ce, 0);
1057486f5adSViresh Kumar sun5i_clkevt_time_start(ce, 0, false);
1067486f5adSViresh Kumar return 0;
1077486f5adSViresh Kumar }
1087486f5adSViresh Kumar
sun5i_clkevt_set_periodic(struct clock_event_device * clkevt)1097486f5adSViresh Kumar static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt)
1107486f5adSViresh Kumar {
1117ded8038SMans Rullgard struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);
1127486f5adSViresh Kumar
1134a59058fSMaxime Ripard sun5i_clkevt_time_stop(ce, 0);
1140b38dd17SMans Rullgard sun5i_clkevt_time_setup(ce, 0, ce->ticks_per_jiffy);
1154a59058fSMaxime Ripard sun5i_clkevt_time_start(ce, 0, true);
1167486f5adSViresh Kumar return 0;
11767905540SMaxime Ripard }
11867905540SMaxime Ripard
sun5i_clkevt_next_event(unsigned long evt,struct clock_event_device * clkevt)11967905540SMaxime Ripard static int sun5i_clkevt_next_event(unsigned long evt,
1204a59058fSMaxime Ripard struct clock_event_device *clkevt)
12167905540SMaxime Ripard {
1227ded8038SMans Rullgard struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);
1234a59058fSMaxime Ripard
1244a59058fSMaxime Ripard sun5i_clkevt_time_stop(ce, 0);
1254a59058fSMaxime Ripard sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS);
1264a59058fSMaxime Ripard sun5i_clkevt_time_start(ce, 0, false);
12767905540SMaxime Ripard
12867905540SMaxime Ripard return 0;
12967905540SMaxime Ripard }
13067905540SMaxime Ripard
sun5i_timer_interrupt(int irq,void * dev_id)13167905540SMaxime Ripard static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
13267905540SMaxime Ripard {
1337ded8038SMans Rullgard struct sun5i_timer *ce = dev_id;
13467905540SMaxime Ripard
1350b38dd17SMans Rullgard writel(0x1, ce->base + TIMER_IRQ_ST_REG);
1364a59058fSMaxime Ripard ce->clkevt.event_handler(&ce->clkevt);
13767905540SMaxime Ripard
13867905540SMaxime Ripard return IRQ_HANDLED;
13967905540SMaxime Ripard }
14067905540SMaxime Ripard
sun5i_clksrc_read(struct clocksource * clksrc)141a5a1d1c2SThomas Gleixner static u64 sun5i_clksrc_read(struct clocksource *clksrc)
14259387683SChen-Yu Tsai {
1437ded8038SMans Rullgard struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc);
14459387683SChen-Yu Tsai
1450b38dd17SMans Rullgard return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1));
14659387683SChen-Yu Tsai }
14759387683SChen-Yu Tsai
sun5i_rate_cb(struct notifier_block * nb,unsigned long event,void * data)1487ded8038SMans Rullgard static int sun5i_rate_cb(struct notifier_block *nb,
1493071efa4SMaxime Ripard unsigned long event, void *data)
1503071efa4SMaxime Ripard {
1513071efa4SMaxime Ripard struct clk_notifier_data *ndata = data;
1527ded8038SMans Rullgard struct sun5i_timer *cs = nb_to_sun5i_timer(nb);
1533071efa4SMaxime Ripard
1543071efa4SMaxime Ripard switch (event) {
1553071efa4SMaxime Ripard case PRE_RATE_CHANGE:
1563071efa4SMaxime Ripard clocksource_unregister(&cs->clksrc);
1573071efa4SMaxime Ripard break;
1583071efa4SMaxime Ripard
1593071efa4SMaxime Ripard case POST_RATE_CHANGE:
1603071efa4SMaxime Ripard clocksource_register_hz(&cs->clksrc, ndata->new_rate);
1617ded8038SMans Rullgard clockevents_update_freq(&cs->clkevt, ndata->new_rate);
1620b38dd17SMans Rullgard cs->ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ);
1633071efa4SMaxime Ripard break;
1643071efa4SMaxime Ripard
1653071efa4SMaxime Ripard default:
1663071efa4SMaxime Ripard break;
1673071efa4SMaxime Ripard }
1683071efa4SMaxime Ripard
1693071efa4SMaxime Ripard return NOTIFY_DONE;
1703071efa4SMaxime Ripard }
1713071efa4SMaxime Ripard
sun5i_setup_clocksource(struct platform_device * pdev,unsigned long rate)1727e5bac61SMans Rullgard static int sun5i_setup_clocksource(struct platform_device *pdev,
1737ded8038SMans Rullgard unsigned long rate)
1744a59058fSMaxime Ripard {
1757e5bac61SMans Rullgard struct sun5i_timer *cs = platform_get_drvdata(pdev);
1760b38dd17SMans Rullgard void __iomem *base = cs->base;
1774a59058fSMaxime Ripard int ret;
1784a59058fSMaxime Ripard
1794a59058fSMaxime Ripard writel(~0, base + TIMER_INTVAL_LO_REG(1));
1804a59058fSMaxime Ripard writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
1814a59058fSMaxime Ripard base + TIMER_CTL_REG(1));
1824a59058fSMaxime Ripard
1837e5bac61SMans Rullgard cs->clksrc.name = pdev->dev.of_node->name;
18459387683SChen-Yu Tsai cs->clksrc.rating = 340;
18559387683SChen-Yu Tsai cs->clksrc.read = sun5i_clksrc_read;
18659387683SChen-Yu Tsai cs->clksrc.mask = CLOCKSOURCE_MASK(32);
18759387683SChen-Yu Tsai cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
18859387683SChen-Yu Tsai
18959387683SChen-Yu Tsai ret = clocksource_register_hz(&cs->clksrc, rate);
1904a59058fSMaxime Ripard if (ret) {
1917e5bac61SMans Rullgard dev_err(&pdev->dev, "Couldn't register clock source.\n");
1924a59058fSMaxime Ripard return ret;
1934a59058fSMaxime Ripard }
1944a59058fSMaxime Ripard
1957ded8038SMans Rullgard return 0;
1963071efa4SMaxime Ripard }
1973071efa4SMaxime Ripard
sun5i_setup_clockevent(struct platform_device * pdev,unsigned long rate,int irq)1987e5bac61SMans Rullgard static int sun5i_setup_clockevent(struct platform_device *pdev,
1997ded8038SMans Rullgard unsigned long rate, int irq)
2004a59058fSMaxime Ripard {
2017e5bac61SMans Rullgard struct device *dev = &pdev->dev;
2027e5bac61SMans Rullgard struct sun5i_timer *ce = platform_get_drvdata(pdev);
2030b38dd17SMans Rullgard void __iomem *base = ce->base;
2044a59058fSMaxime Ripard int ret;
2054a59058fSMaxime Ripard u32 val;
2064a59058fSMaxime Ripard
2077e5bac61SMans Rullgard ce->clkevt.name = dev->of_node->name;
2084a59058fSMaxime Ripard ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
2094a59058fSMaxime Ripard ce->clkevt.set_next_event = sun5i_clkevt_next_event;
2107486f5adSViresh Kumar ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown;
2117486f5adSViresh Kumar ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic;
2127486f5adSViresh Kumar ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot;
2137486f5adSViresh Kumar ce->clkevt.tick_resume = sun5i_clkevt_shutdown;
2144a59058fSMaxime Ripard ce->clkevt.rating = 340;
2154a59058fSMaxime Ripard ce->clkevt.irq = irq;
2164a59058fSMaxime Ripard ce->clkevt.cpumask = cpu_possible_mask;
2174a59058fSMaxime Ripard
2184a59058fSMaxime Ripard /* Enable timer0 interrupt */
2194a59058fSMaxime Ripard val = readl(base + TIMER_IRQ_EN_REG);
2204a59058fSMaxime Ripard writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG);
2214a59058fSMaxime Ripard
2224a59058fSMaxime Ripard clockevents_config_and_register(&ce->clkevt, rate,
2234a59058fSMaxime Ripard TIMER_SYNC_TICKS, 0xffffffff);
2244a59058fSMaxime Ripard
2257e5bac61SMans Rullgard ret = devm_request_irq(dev, irq, sun5i_timer_interrupt,
2267e5bac61SMans Rullgard IRQF_TIMER | IRQF_IRQPOLL,
2274a59058fSMaxime Ripard "sun5i_timer0", ce);
2284a59058fSMaxime Ripard if (ret) {
2297e5bac61SMans Rullgard dev_err(dev, "Unable to register interrupt\n");
2307ded8038SMans Rullgard return ret;
2314a59058fSMaxime Ripard }
2324a59058fSMaxime Ripard
2334a59058fSMaxime Ripard return 0;
2344a59058fSMaxime Ripard }
2354a59058fSMaxime Ripard
sun5i_timer_probe(struct platform_device * pdev)2367e5bac61SMans Rullgard static int sun5i_timer_probe(struct platform_device *pdev)
23767905540SMaxime Ripard {
2387e5bac61SMans Rullgard struct device *dev = &pdev->dev;
2397ded8038SMans Rullgard struct sun5i_timer *st;
240e50a00beSMaxime Ripard struct reset_control *rstc;
2414a59058fSMaxime Ripard void __iomem *timer_base;
24267905540SMaxime Ripard struct clk *clk;
2437ded8038SMans Rullgard unsigned long rate;
244e4d9f2eeSDaniel Lezcano int irq, ret;
24567905540SMaxime Ripard
2467e5bac61SMans Rullgard st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
2477ded8038SMans Rullgard if (!st)
2487ded8038SMans Rullgard return -ENOMEM;
2497ded8038SMans Rullgard
2507e5bac61SMans Rullgard platform_set_drvdata(pdev, st);
2517e5bac61SMans Rullgard
2527e5bac61SMans Rullgard timer_base = devm_platform_ioremap_resource(pdev, 0);
253e4d9f2eeSDaniel Lezcano if (IS_ERR(timer_base)) {
2547e5bac61SMans Rullgard dev_err(dev, "Can't map registers\n");
255ed7158baSIngo Molnar return PTR_ERR(timer_base);
256e4d9f2eeSDaniel Lezcano }
25767905540SMaxime Ripard
2587e5bac61SMans Rullgard irq = platform_get_irq(pdev, 0);
259*fd73c011SYang Li if (irq < 0)
2607e5bac61SMans Rullgard return irq;
26167905540SMaxime Ripard
2627e5bac61SMans Rullgard clk = devm_clk_get_enabled(dev, NULL);
263e4d9f2eeSDaniel Lezcano if (IS_ERR(clk)) {
2647e5bac61SMans Rullgard dev_err(dev, "Can't get timer clock\n");
265e4d9f2eeSDaniel Lezcano return PTR_ERR(clk);
266e4d9f2eeSDaniel Lezcano }
26767905540SMaxime Ripard
2687ded8038SMans Rullgard rate = clk_get_rate(clk);
2697ded8038SMans Rullgard if (!rate) {
2707e5bac61SMans Rullgard dev_err(dev, "Couldn't get parent clock rate\n");
2717e5bac61SMans Rullgard return -EINVAL;
2727ded8038SMans Rullgard }
2737ded8038SMans Rullgard
2740b38dd17SMans Rullgard st->base = timer_base;
2750b38dd17SMans Rullgard st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
2760b38dd17SMans Rullgard st->clk = clk;
2770b38dd17SMans Rullgard st->clk_rate_cb.notifier_call = sun5i_rate_cb;
2780b38dd17SMans Rullgard st->clk_rate_cb.next = NULL;
2797ded8038SMans Rullgard
2807e5bac61SMans Rullgard ret = devm_clk_notifier_register(dev, clk, &st->clk_rate_cb);
2817ded8038SMans Rullgard if (ret) {
2827e5bac61SMans Rullgard dev_err(dev, "Unable to register clock notifier.\n");
2837ded8038SMans Rullgard return ret;
28467905540SMaxime Ripard }
2857e5bac61SMans Rullgard
2867e5bac61SMans Rullgard rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
2877e5bac61SMans Rullgard if (rstc)
2887e5bac61SMans Rullgard reset_control_deassert(rstc);
2897e5bac61SMans Rullgard
2907e5bac61SMans Rullgard ret = sun5i_setup_clocksource(pdev, rate);
2917e5bac61SMans Rullgard if (ret)
2927e5bac61SMans Rullgard return ret;
2937e5bac61SMans Rullgard
2947e5bac61SMans Rullgard ret = sun5i_setup_clockevent(pdev, rate, irq);
2957e5bac61SMans Rullgard if (ret)
2967e5bac61SMans Rullgard goto err_unreg_clocksource;
2977e5bac61SMans Rullgard
2987e5bac61SMans Rullgard return 0;
2997e5bac61SMans Rullgard
3007e5bac61SMans Rullgard err_unreg_clocksource:
3017e5bac61SMans Rullgard clocksource_unregister(&st->clksrc);
3027e5bac61SMans Rullgard return ret;
3037e5bac61SMans Rullgard }
3047e5bac61SMans Rullgard
sun5i_timer_remove(struct platform_device * pdev)3057e5bac61SMans Rullgard static void sun5i_timer_remove(struct platform_device *pdev)
3067e5bac61SMans Rullgard {
3077e5bac61SMans Rullgard struct sun5i_timer *st = platform_get_drvdata(pdev);
3087e5bac61SMans Rullgard
3097e5bac61SMans Rullgard clocksource_unregister(&st->clksrc);
3107e5bac61SMans Rullgard }
3117e5bac61SMans Rullgard
3127e5bac61SMans Rullgard static const struct of_device_id sun5i_timer_of_match[] = {
3137e5bac61SMans Rullgard { .compatible = "allwinner,sun5i-a13-hstimer" },
3147e5bac61SMans Rullgard { .compatible = "allwinner,sun7i-a20-hstimer" },
3157e5bac61SMans Rullgard {},
3167e5bac61SMans Rullgard };
3177e5bac61SMans Rullgard MODULE_DEVICE_TABLE(of, sun5i_timer_of_match);
3187e5bac61SMans Rullgard
3197e5bac61SMans Rullgard static struct platform_driver sun5i_timer_driver = {
3207e5bac61SMans Rullgard .probe = sun5i_timer_probe,
3217e5bac61SMans Rullgard .remove_new = sun5i_timer_remove,
3227e5bac61SMans Rullgard .driver = {
3237e5bac61SMans Rullgard .name = "sun5i-timer",
3247e5bac61SMans Rullgard .of_match_table = sun5i_timer_of_match,
3257e5bac61SMans Rullgard .suppress_bind_attrs = true,
3267e5bac61SMans Rullgard },
3277e5bac61SMans Rullgard };
3287e5bac61SMans Rullgard module_platform_driver(sun5i_timer_driver);
329