162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Allwinner SoCs hstimer driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Maxime Ripard 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/clockchips.h> 1262306a36Sopenharmony_ci#include <linux/clocksource.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqreturn.h> 1762306a36Sopenharmony_ci#include <linux/reset.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define TIMER_IRQ_EN_REG 0x00 2262306a36Sopenharmony_ci#define TIMER_IRQ_EN(val) BIT(val) 2362306a36Sopenharmony_ci#define TIMER_IRQ_ST_REG 0x04 2462306a36Sopenharmony_ci#define TIMER_CTL_REG(val) (0x20 * (val) + 0x10) 2562306a36Sopenharmony_ci#define TIMER_CTL_ENABLE BIT(0) 2662306a36Sopenharmony_ci#define TIMER_CTL_RELOAD BIT(1) 2762306a36Sopenharmony_ci#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) 2862306a36Sopenharmony_ci#define TIMER_CTL_ONESHOT BIT(7) 2962306a36Sopenharmony_ci#define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14) 3062306a36Sopenharmony_ci#define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18) 3162306a36Sopenharmony_ci#define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c) 3262306a36Sopenharmony_ci#define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define TIMER_SYNC_TICKS 3 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct sun5i_timer { 3762306a36Sopenharmony_ci void __iomem *base; 3862306a36Sopenharmony_ci struct clk *clk; 3962306a36Sopenharmony_ci struct notifier_block clk_rate_cb; 4062306a36Sopenharmony_ci u32 ticks_per_jiffy; 4162306a36Sopenharmony_ci struct clocksource clksrc; 4262306a36Sopenharmony_ci struct clock_event_device clkevt; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define nb_to_sun5i_timer(x) \ 4662306a36Sopenharmony_ci container_of(x, struct sun5i_timer, clk_rate_cb) 4762306a36Sopenharmony_ci#define clksrc_to_sun5i_timer(x) \ 4862306a36Sopenharmony_ci container_of(x, struct sun5i_timer, clksrc) 4962306a36Sopenharmony_ci#define clkevt_to_sun5i_timer(x) \ 5062306a36Sopenharmony_ci container_of(x, struct sun5i_timer, clkevt) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * When we disable a timer, we need to wait at least for 2 cycles of 5462306a36Sopenharmony_ci * the timer source clock. We will use for that the clocksource timer 5562306a36Sopenharmony_ci * that is already setup and runs at the same frequency than the other 5662306a36Sopenharmony_ci * timers, and we never will be disabled. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic void sun5i_clkevt_sync(struct sun5i_timer *ce) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1)); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) 6362306a36Sopenharmony_ci cpu_relax(); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u32 val = readl(ce->base + TIMER_CTL_REG(timer)); 6962306a36Sopenharmony_ci writel(val & ~TIMER_CTL_ENABLE, ce->base + TIMER_CTL_REG(timer)); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci sun5i_clkevt_sync(ce); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer)); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 val = readl(ce->base + TIMER_CTL_REG(timer)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (periodic) 8462306a36Sopenharmony_ci val &= ~TIMER_CTL_ONESHOT; 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci val |= TIMER_CTL_ONESHOT; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 8962306a36Sopenharmony_ci ce->base + TIMER_CTL_REG(timer)); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 10562306a36Sopenharmony_ci sun5i_clkevt_time_start(ce, 0, false); 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 11462306a36Sopenharmony_ci sun5i_clkevt_time_setup(ce, 0, ce->ticks_per_jiffy); 11562306a36Sopenharmony_ci sun5i_clkevt_time_start(ce, 0, true); 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int sun5i_clkevt_next_event(unsigned long evt, 12062306a36Sopenharmony_ci struct clock_event_device *clkevt) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 12562306a36Sopenharmony_ci sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS); 12662306a36Sopenharmony_ci sun5i_clkevt_time_start(ce, 0, false); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct sun5i_timer *ce = dev_id; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci writel(0x1, ce->base + TIMER_IRQ_ST_REG); 13662306a36Sopenharmony_ci ce->clkevt.event_handler(&ce->clkevt); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return IRQ_HANDLED; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic u64 sun5i_clksrc_read(struct clocksource *clksrc) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1)); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int sun5i_rate_cb(struct notifier_block *nb, 14962306a36Sopenharmony_ci unsigned long event, void *data) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct clk_notifier_data *ndata = data; 15262306a36Sopenharmony_ci struct sun5i_timer *cs = nb_to_sun5i_timer(nb); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci switch (event) { 15562306a36Sopenharmony_ci case PRE_RATE_CHANGE: 15662306a36Sopenharmony_ci clocksource_unregister(&cs->clksrc); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci case POST_RATE_CHANGE: 16062306a36Sopenharmony_ci clocksource_register_hz(&cs->clksrc, ndata->new_rate); 16162306a36Sopenharmony_ci clockevents_update_freq(&cs->clkevt, ndata->new_rate); 16262306a36Sopenharmony_ci cs->ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return NOTIFY_DONE; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int sun5i_setup_clocksource(struct platform_device *pdev, 17362306a36Sopenharmony_ci unsigned long rate) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct sun5i_timer *cs = platform_get_drvdata(pdev); 17662306a36Sopenharmony_ci void __iomem *base = cs->base; 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci writel(~0, base + TIMER_INTVAL_LO_REG(1)); 18062306a36Sopenharmony_ci writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 18162306a36Sopenharmony_ci base + TIMER_CTL_REG(1)); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci cs->clksrc.name = pdev->dev.of_node->name; 18462306a36Sopenharmony_ci cs->clksrc.rating = 340; 18562306a36Sopenharmony_ci cs->clksrc.read = sun5i_clksrc_read; 18662306a36Sopenharmony_ci cs->clksrc.mask = CLOCKSOURCE_MASK(32); 18762306a36Sopenharmony_ci cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ret = clocksource_register_hz(&cs->clksrc, rate); 19062306a36Sopenharmony_ci if (ret) { 19162306a36Sopenharmony_ci dev_err(&pdev->dev, "Couldn't register clock source.\n"); 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int sun5i_setup_clockevent(struct platform_device *pdev, 19962306a36Sopenharmony_ci unsigned long rate, int irq) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 20262306a36Sopenharmony_ci struct sun5i_timer *ce = platform_get_drvdata(pdev); 20362306a36Sopenharmony_ci void __iomem *base = ce->base; 20462306a36Sopenharmony_ci int ret; 20562306a36Sopenharmony_ci u32 val; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ce->clkevt.name = dev->of_node->name; 20862306a36Sopenharmony_ci ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 20962306a36Sopenharmony_ci ce->clkevt.set_next_event = sun5i_clkevt_next_event; 21062306a36Sopenharmony_ci ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown; 21162306a36Sopenharmony_ci ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic; 21262306a36Sopenharmony_ci ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot; 21362306a36Sopenharmony_ci ce->clkevt.tick_resume = sun5i_clkevt_shutdown; 21462306a36Sopenharmony_ci ce->clkevt.rating = 340; 21562306a36Sopenharmony_ci ce->clkevt.irq = irq; 21662306a36Sopenharmony_ci ce->clkevt.cpumask = cpu_possible_mask; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Enable timer0 interrupt */ 21962306a36Sopenharmony_ci val = readl(base + TIMER_IRQ_EN_REG); 22062306a36Sopenharmony_ci writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci clockevents_config_and_register(&ce->clkevt, rate, 22362306a36Sopenharmony_ci TIMER_SYNC_TICKS, 0xffffffff); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, sun5i_timer_interrupt, 22662306a36Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, 22762306a36Sopenharmony_ci "sun5i_timer0", ce); 22862306a36Sopenharmony_ci if (ret) { 22962306a36Sopenharmony_ci dev_err(dev, "Unable to register interrupt\n"); 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int sun5i_timer_probe(struct platform_device *pdev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 23962306a36Sopenharmony_ci struct sun5i_timer *st; 24062306a36Sopenharmony_ci struct reset_control *rstc; 24162306a36Sopenharmony_ci void __iomem *timer_base; 24262306a36Sopenharmony_ci struct clk *clk; 24362306a36Sopenharmony_ci unsigned long rate; 24462306a36Sopenharmony_ci int irq, ret; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); 24762306a36Sopenharmony_ci if (!st) 24862306a36Sopenharmony_ci return -ENOMEM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci platform_set_drvdata(pdev, st); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci timer_base = devm_platform_ioremap_resource(pdev, 0); 25362306a36Sopenharmony_ci if (IS_ERR(timer_base)) { 25462306a36Sopenharmony_ci dev_err(dev, "Can't map registers\n"); 25562306a36Sopenharmony_ci return PTR_ERR(timer_base); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 25962306a36Sopenharmony_ci if (irq < 0) { 26062306a36Sopenharmony_ci dev_err(dev, "Can't get IRQ\n"); 26162306a36Sopenharmony_ci return irq; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci clk = devm_clk_get_enabled(dev, NULL); 26562306a36Sopenharmony_ci if (IS_ERR(clk)) { 26662306a36Sopenharmony_ci dev_err(dev, "Can't get timer clock\n"); 26762306a36Sopenharmony_ci return PTR_ERR(clk); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci rate = clk_get_rate(clk); 27162306a36Sopenharmony_ci if (!rate) { 27262306a36Sopenharmony_ci dev_err(dev, "Couldn't get parent clock rate\n"); 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci st->base = timer_base; 27762306a36Sopenharmony_ci st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); 27862306a36Sopenharmony_ci st->clk = clk; 27962306a36Sopenharmony_ci st->clk_rate_cb.notifier_call = sun5i_rate_cb; 28062306a36Sopenharmony_ci st->clk_rate_cb.next = NULL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ret = devm_clk_notifier_register(dev, clk, &st->clk_rate_cb); 28362306a36Sopenharmony_ci if (ret) { 28462306a36Sopenharmony_ci dev_err(dev, "Unable to register clock notifier.\n"); 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci rstc = devm_reset_control_get_optional_exclusive(dev, NULL); 28962306a36Sopenharmony_ci if (rstc) 29062306a36Sopenharmony_ci reset_control_deassert(rstc); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = sun5i_setup_clocksource(pdev, rate); 29362306a36Sopenharmony_ci if (ret) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ret = sun5i_setup_clockevent(pdev, rate, irq); 29762306a36Sopenharmony_ci if (ret) 29862306a36Sopenharmony_ci goto err_unreg_clocksource; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cierr_unreg_clocksource: 30362306a36Sopenharmony_ci clocksource_unregister(&st->clksrc); 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void sun5i_timer_remove(struct platform_device *pdev) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct sun5i_timer *st = platform_get_drvdata(pdev); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci clocksource_unregister(&st->clksrc); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic const struct of_device_id sun5i_timer_of_match[] = { 31562306a36Sopenharmony_ci { .compatible = "allwinner,sun5i-a13-hstimer" }, 31662306a36Sopenharmony_ci { .compatible = "allwinner,sun7i-a20-hstimer" }, 31762306a36Sopenharmony_ci {}, 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun5i_timer_of_match); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct platform_driver sun5i_timer_driver = { 32262306a36Sopenharmony_ci .probe = sun5i_timer_probe, 32362306a36Sopenharmony_ci .remove_new = sun5i_timer_remove, 32462306a36Sopenharmony_ci .driver = { 32562306a36Sopenharmony_ci .name = "sun5i-timer", 32662306a36Sopenharmony_ci .of_match_table = sun5i_timer_of_match, 32762306a36Sopenharmony_ci .suppress_bind_attrs = true, 32862306a36Sopenharmony_ci }, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_cimodule_platform_driver(sun5i_timer_driver); 331