162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale FlexTimer Module (FTM) timer driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2014 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/clockchips.h>
1062306a36Sopenharmony_ci#include <linux/clocksource.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/of_address.h>
1562306a36Sopenharmony_ci#include <linux/of_irq.h>
1662306a36Sopenharmony_ci#include <linux/sched_clock.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/fsl/ftm.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define FTM_SC_CLK(c)	((c) << FTM_SC_CLK_MASK_SHIFT)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct ftm_clock_device {
2362306a36Sopenharmony_ci	void __iomem *clksrc_base;
2462306a36Sopenharmony_ci	void __iomem *clkevt_base;
2562306a36Sopenharmony_ci	unsigned long periodic_cyc;
2662306a36Sopenharmony_ci	unsigned long ps;
2762306a36Sopenharmony_ci	bool big_endian;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic struct ftm_clock_device *priv;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic inline u32 ftm_readl(void __iomem *addr)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	if (priv->big_endian)
3562306a36Sopenharmony_ci		return ioread32be(addr);
3662306a36Sopenharmony_ci	else
3762306a36Sopenharmony_ci		return ioread32(addr);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline void ftm_writel(u32 val, void __iomem *addr)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	if (priv->big_endian)
4362306a36Sopenharmony_ci		iowrite32be(val, addr);
4462306a36Sopenharmony_ci	else
4562306a36Sopenharmony_ci		iowrite32(val, addr);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline void ftm_counter_enable(void __iomem *base)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u32 val;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* select and enable counter clock source */
5362306a36Sopenharmony_ci	val = ftm_readl(base + FTM_SC);
5462306a36Sopenharmony_ci	val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
5562306a36Sopenharmony_ci	val |= priv->ps | FTM_SC_CLK(1);
5662306a36Sopenharmony_ci	ftm_writel(val, base + FTM_SC);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline void ftm_counter_disable(void __iomem *base)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	u32 val;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* disable counter clock source */
6462306a36Sopenharmony_ci	val = ftm_readl(base + FTM_SC);
6562306a36Sopenharmony_ci	val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
6662306a36Sopenharmony_ci	ftm_writel(val, base + FTM_SC);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic inline void ftm_irq_acknowledge(void __iomem *base)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	u32 val;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	val = ftm_readl(base + FTM_SC);
7462306a36Sopenharmony_ci	val &= ~FTM_SC_TOF;
7562306a36Sopenharmony_ci	ftm_writel(val, base + FTM_SC);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic inline void ftm_irq_enable(void __iomem *base)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	u32 val;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	val = ftm_readl(base + FTM_SC);
8362306a36Sopenharmony_ci	val |= FTM_SC_TOIE;
8462306a36Sopenharmony_ci	ftm_writel(val, base + FTM_SC);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline void ftm_irq_disable(void __iomem *base)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	u32 val;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	val = ftm_readl(base + FTM_SC);
9262306a36Sopenharmony_ci	val &= ~FTM_SC_TOIE;
9362306a36Sopenharmony_ci	ftm_writel(val, base + FTM_SC);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic inline void ftm_reset_counter(void __iomem *base)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	/*
9962306a36Sopenharmony_ci	 * The CNT register contains the FTM counter value.
10062306a36Sopenharmony_ci	 * Reset clears the CNT register. Writing any value to COUNT
10162306a36Sopenharmony_ci	 * updates the counter with its initial value, CNTIN.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	ftm_writel(0x00, base + FTM_CNT);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic u64 notrace ftm_read_sched_clock(void)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	return ftm_readl(priv->clksrc_base + FTM_CNT);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int ftm_set_next_event(unsigned long delta,
11262306a36Sopenharmony_ci				struct clock_event_device *unused)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * The CNNIN and MOD are all double buffer registers, writing
11662306a36Sopenharmony_ci	 * to the MOD register latches the value into a buffer. The MOD
11762306a36Sopenharmony_ci	 * register is updated with the value of its write buffer with
11862306a36Sopenharmony_ci	 * the following scenario:
11962306a36Sopenharmony_ci	 * a, the counter source clock is disabled.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	ftm_counter_disable(priv->clkevt_base);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Force the value of CNTIN to be loaded into the FTM counter */
12462306a36Sopenharmony_ci	ftm_reset_counter(priv->clkevt_base);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * The counter increments until the value of MOD is reached,
12862306a36Sopenharmony_ci	 * at which point the counter is reloaded with the value of CNTIN.
12962306a36Sopenharmony_ci	 * The TOF (the overflow flag) bit is set when the FTM counter
13062306a36Sopenharmony_ci	 * changes from MOD to CNTIN. So we should using the delta - 1.
13162306a36Sopenharmony_ci	 */
13262306a36Sopenharmony_ci	ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ftm_counter_enable(priv->clkevt_base);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ftm_irq_enable(priv->clkevt_base);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int ftm_set_oneshot(struct clock_event_device *evt)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	ftm_counter_disable(priv->clkevt_base);
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int ftm_set_periodic(struct clock_event_device *evt)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	ftm_set_next_event(priv->periodic_cyc, evt);
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct clock_event_device *evt = dev_id;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	ftm_irq_acknowledge(priv->clkevt_base);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (likely(clockevent_state_oneshot(evt))) {
16062306a36Sopenharmony_ci		ftm_irq_disable(priv->clkevt_base);
16162306a36Sopenharmony_ci		ftm_counter_disable(priv->clkevt_base);
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	evt->event_handler(evt);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return IRQ_HANDLED;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct clock_event_device ftm_clockevent = {
17062306a36Sopenharmony_ci	.name			= "Freescale ftm timer",
17162306a36Sopenharmony_ci	.features		= CLOCK_EVT_FEAT_PERIODIC |
17262306a36Sopenharmony_ci				  CLOCK_EVT_FEAT_ONESHOT,
17362306a36Sopenharmony_ci	.set_state_periodic	= ftm_set_periodic,
17462306a36Sopenharmony_ci	.set_state_oneshot	= ftm_set_oneshot,
17562306a36Sopenharmony_ci	.set_next_event		= ftm_set_next_event,
17662306a36Sopenharmony_ci	.rating			= 300,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int __init ftm_clockevent_init(unsigned long freq, int irq)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int err;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
18462306a36Sopenharmony_ci	ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ftm_reset_counter(priv->clkevt_base);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
18962306a36Sopenharmony_ci			  "Freescale ftm timer", &ftm_clockevent);
19062306a36Sopenharmony_ci	if (err) {
19162306a36Sopenharmony_ci		pr_err("ftm: setup irq failed: %d\n", err);
19262306a36Sopenharmony_ci		return err;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ftm_clockevent.cpumask = cpumask_of(0);
19662306a36Sopenharmony_ci	ftm_clockevent.irq = irq;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	clockevents_config_and_register(&ftm_clockevent,
19962306a36Sopenharmony_ci					freq / (1 << priv->ps),
20062306a36Sopenharmony_ci					1, 0xffff);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	ftm_counter_enable(priv->clkevt_base);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int __init ftm_clocksource_init(unsigned long freq)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int err;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
21262306a36Sopenharmony_ci	ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ftm_reset_counter(priv->clksrc_base);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
21762306a36Sopenharmony_ci	err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
21862306a36Sopenharmony_ci				    freq / (1 << priv->ps), 300, 16,
21962306a36Sopenharmony_ci				    clocksource_mmio_readl_up);
22062306a36Sopenharmony_ci	if (err) {
22162306a36Sopenharmony_ci		pr_err("ftm: init clock source mmio failed: %d\n", err);
22262306a36Sopenharmony_ci		return err;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ftm_counter_enable(priv->clksrc_base);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
23162306a36Sopenharmony_ci				 char *ftm_name)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct clk *clk;
23462306a36Sopenharmony_ci	int err;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	clk = of_clk_get_by_name(np, cnt_name);
23762306a36Sopenharmony_ci	if (IS_ERR(clk)) {
23862306a36Sopenharmony_ci		pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
23962306a36Sopenharmony_ci		return PTR_ERR(clk);
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	err = clk_prepare_enable(clk);
24262306a36Sopenharmony_ci	if (err) {
24362306a36Sopenharmony_ci		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
24462306a36Sopenharmony_ci			cnt_name, err);
24562306a36Sopenharmony_ci		return err;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	clk = of_clk_get_by_name(np, ftm_name);
24962306a36Sopenharmony_ci	if (IS_ERR(clk)) {
25062306a36Sopenharmony_ci		pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
25162306a36Sopenharmony_ci		return PTR_ERR(clk);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	err = clk_prepare_enable(clk);
25462306a36Sopenharmony_ci	if (err)
25562306a36Sopenharmony_ci		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
25662306a36Sopenharmony_ci			ftm_name, err);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return clk_get_rate(clk);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic unsigned long __init ftm_clk_init(struct device_node *np)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	long freq;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
26662306a36Sopenharmony_ci	if (freq <= 0)
26762306a36Sopenharmony_ci		return 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
27062306a36Sopenharmony_ci	if (freq <= 0)
27162306a36Sopenharmony_ci		return 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return freq;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int __init ftm_calc_closest_round_cyc(unsigned long freq)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	priv->ps = 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* The counter register is only using the lower 16 bits, and
28162306a36Sopenharmony_ci	 * if the 'freq' value is to big here, then the periodic_cyc
28262306a36Sopenharmony_ci	 * may exceed 0xFFFF.
28362306a36Sopenharmony_ci	 */
28462306a36Sopenharmony_ci	do {
28562306a36Sopenharmony_ci		priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
28662306a36Sopenharmony_ci						HZ * (1 << priv->ps++));
28762306a36Sopenharmony_ci	} while (priv->periodic_cyc > 0xFFFF);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (priv->ps > FTM_PS_MAX) {
29062306a36Sopenharmony_ci		pr_err("ftm: the prescaler is %lu > %d\n",
29162306a36Sopenharmony_ci				priv->ps, FTM_PS_MAX);
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return 0;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int __init ftm_timer_init(struct device_node *np)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	unsigned long freq;
30162306a36Sopenharmony_ci	int ret, irq;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
30462306a36Sopenharmony_ci	if (!priv)
30562306a36Sopenharmony_ci		return -ENOMEM;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	ret = -ENXIO;
30862306a36Sopenharmony_ci	priv->clkevt_base = of_iomap(np, 0);
30962306a36Sopenharmony_ci	if (!priv->clkevt_base) {
31062306a36Sopenharmony_ci		pr_err("ftm: unable to map event timer registers\n");
31162306a36Sopenharmony_ci		goto err_clkevt;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	priv->clksrc_base = of_iomap(np, 1);
31562306a36Sopenharmony_ci	if (!priv->clksrc_base) {
31662306a36Sopenharmony_ci		pr_err("ftm: unable to map source timer registers\n");
31762306a36Sopenharmony_ci		goto err_clksrc;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	ret = -EINVAL;
32162306a36Sopenharmony_ci	irq = irq_of_parse_and_map(np, 0);
32262306a36Sopenharmony_ci	if (irq <= 0) {
32362306a36Sopenharmony_ci		pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
32462306a36Sopenharmony_ci		goto err;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	priv->big_endian = of_property_read_bool(np, "big-endian");
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	freq = ftm_clk_init(np);
33062306a36Sopenharmony_ci	if (!freq)
33162306a36Sopenharmony_ci		goto err;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ret = ftm_calc_closest_round_cyc(freq);
33462306a36Sopenharmony_ci	if (ret)
33562306a36Sopenharmony_ci		goto err;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ret = ftm_clocksource_init(freq);
33862306a36Sopenharmony_ci	if (ret)
33962306a36Sopenharmony_ci		goto err;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ret = ftm_clockevent_init(freq, irq);
34262306a36Sopenharmony_ci	if (ret)
34362306a36Sopenharmony_ci		goto err;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cierr:
34862306a36Sopenharmony_ci	iounmap(priv->clksrc_base);
34962306a36Sopenharmony_cierr_clksrc:
35062306a36Sopenharmony_ci	iounmap(priv->clkevt_base);
35162306a36Sopenharmony_cierr_clkevt:
35262306a36Sopenharmony_ci	kfree(priv);
35362306a36Sopenharmony_ci	return ret;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ciTIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
356