18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/init.h>
38c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
48c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
58c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
68c2ecf20Sopenharmony_ci#include <linux/irq.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/ioport.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
158c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
168c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h>
178c2ecf20Sopenharmony_ci#include <soc/at91/atmel_tcb.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * We're configured to use a specific TC block, one that's not hooked
228c2ecf20Sopenharmony_ci * up to external hardware, to provide a time solution:
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *   - Two channels combine to create a free-running 32 bit counter
258c2ecf20Sopenharmony_ci *     with a base rate of 5+ MHz, packaged as a clocksource (with
268c2ecf20Sopenharmony_ci *     resolution better than 200 nsec).
278c2ecf20Sopenharmony_ci *   - Some chips support 32 bit counter. A single channel is used for
288c2ecf20Sopenharmony_ci *     this 32 bit free-running counter. the second channel is not used.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *   - The third channel may be used to provide a clockevent source, used in
318c2ecf20Sopenharmony_ci *   either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ,
328c2ecf20Sopenharmony_ci *   and can handle delays of up to two seconds. For 32-bit counters, it runs at
338c2ecf20Sopenharmony_ci *   the same rate as the clocksource
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * REVISIT behavior during system suspend states... we should disable
368c2ecf20Sopenharmony_ci * all clocks and save the power.  Easily done for clockevent devices,
378c2ecf20Sopenharmony_ci * but clocksources won't necessarily get the needed notifications.
388c2ecf20Sopenharmony_ci * For deeper system sleep states, this will be mandatory...
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void __iomem *tcaddr;
428c2ecf20Sopenharmony_cistatic struct
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	u32 cmr;
458c2ecf20Sopenharmony_ci	u32 imr;
468c2ecf20Sopenharmony_ci	u32 rc;
478c2ecf20Sopenharmony_ci	bool clken;
488c2ecf20Sopenharmony_ci} tcb_cache[3];
498c2ecf20Sopenharmony_cistatic u32 bmr_cache;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 };
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic u64 tc_get_cycles(struct clocksource *cs)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned long	flags;
568c2ecf20Sopenharmony_ci	u32		lower, upper;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	raw_local_irq_save(flags);
598c2ecf20Sopenharmony_ci	do {
608c2ecf20Sopenharmony_ci		upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV));
618c2ecf20Sopenharmony_ci		lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
628c2ecf20Sopenharmony_ci	} while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)));
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	raw_local_irq_restore(flags);
658c2ecf20Sopenharmony_ci	return (upper << 16) | lower;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic u64 tc_get_cycles32(struct clocksource *cs)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void tc_clksrc_suspend(struct clocksource *cs)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int i;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) {
788c2ecf20Sopenharmony_ci		tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR));
798c2ecf20Sopenharmony_ci		tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR));
808c2ecf20Sopenharmony_ci		tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC));
818c2ecf20Sopenharmony_ci		tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) &
828c2ecf20Sopenharmony_ci					ATMEL_TC_CLKSTA);
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	bmr_cache = readl(tcaddr + ATMEL_TC_BMR);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void tc_clksrc_resume(struct clocksource *cs)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int i;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) {
938c2ecf20Sopenharmony_ci		/* Restore registers for the channel, RA and RB are not used  */
948c2ecf20Sopenharmony_ci		writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR));
958c2ecf20Sopenharmony_ci		writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC));
968c2ecf20Sopenharmony_ci		writel(0, tcaddr + ATMEL_TC_REG(i, RA));
978c2ecf20Sopenharmony_ci		writel(0, tcaddr + ATMEL_TC_REG(i, RB));
988c2ecf20Sopenharmony_ci		/* Disable all the interrupts */
998c2ecf20Sopenharmony_ci		writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR));
1008c2ecf20Sopenharmony_ci		/* Reenable interrupts that were enabled before suspending */
1018c2ecf20Sopenharmony_ci		writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER));
1028c2ecf20Sopenharmony_ci		/* Start the clock if it was used */
1038c2ecf20Sopenharmony_ci		if (tcb_cache[i].clken)
1048c2ecf20Sopenharmony_ci			writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR));
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Dual channel, chain channels */
1088c2ecf20Sopenharmony_ci	writel(bmr_cache, tcaddr + ATMEL_TC_BMR);
1098c2ecf20Sopenharmony_ci	/* Finally, trigger all the channels*/
1108c2ecf20Sopenharmony_ci	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic struct clocksource clksrc = {
1148c2ecf20Sopenharmony_ci	.rating         = 200,
1158c2ecf20Sopenharmony_ci	.read           = tc_get_cycles,
1168c2ecf20Sopenharmony_ci	.mask           = CLOCKSOURCE_MASK(32),
1178c2ecf20Sopenharmony_ci	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
1188c2ecf20Sopenharmony_ci	.suspend	= tc_clksrc_suspend,
1198c2ecf20Sopenharmony_ci	.resume		= tc_clksrc_resume,
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic u64 notrace tc_sched_clock_read(void)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	return tc_get_cycles(&clksrc);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic u64 notrace tc_sched_clock_read32(void)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	return tc_get_cycles32(&clksrc);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic struct delay_timer tc_delay_timer;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic unsigned long tc_delay_timer_read(void)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	return tc_get_cycles(&clksrc);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic unsigned long notrace tc_delay_timer_read32(void)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	return tc_get_cycles32(&clksrc);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_CLOCKEVENTS
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistruct tc_clkevt_device {
1478c2ecf20Sopenharmony_ci	struct clock_event_device	clkevt;
1488c2ecf20Sopenharmony_ci	struct clk			*clk;
1498c2ecf20Sopenharmony_ci	u32				rate;
1508c2ecf20Sopenharmony_ci	void __iomem			*regs;
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return container_of(clkevt, struct tc_clkevt_device, clkevt);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic u32 timer_clock;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int tc_shutdown(struct clock_event_device *d)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct tc_clkevt_device *tcd = to_tc_clkevt(d);
1638c2ecf20Sopenharmony_ci	void __iomem		*regs = tcd->regs;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	writel(0xff, regs + ATMEL_TC_REG(2, IDR));
1668c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
1678c2ecf20Sopenharmony_ci	if (!clockevent_state_detached(d))
1688c2ecf20Sopenharmony_ci		clk_disable(tcd->clk);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return 0;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int tc_set_oneshot(struct clock_event_device *d)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct tc_clkevt_device *tcd = to_tc_clkevt(d);
1768c2ecf20Sopenharmony_ci	void __iomem		*regs = tcd->regs;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
1798c2ecf20Sopenharmony_ci		tc_shutdown(d);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	clk_enable(tcd->clk);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* count up to RC, then irq and stop */
1848c2ecf20Sopenharmony_ci	writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE |
1858c2ecf20Sopenharmony_ci		     ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR));
1868c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* set_next_event() configures and starts the timer */
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int tc_set_periodic(struct clock_event_device *d)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct tc_clkevt_device *tcd = to_tc_clkevt(d);
1958c2ecf20Sopenharmony_ci	void __iomem		*regs = tcd->regs;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
1988c2ecf20Sopenharmony_ci		tc_shutdown(d);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* By not making the gentime core emulate periodic mode on top
2018c2ecf20Sopenharmony_ci	 * of oneshot, we get lower overhead and improved accuracy.
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	clk_enable(tcd->clk);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* count up to RC, then irq and restart */
2068c2ecf20Sopenharmony_ci	writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
2078c2ecf20Sopenharmony_ci		     regs + ATMEL_TC_REG(2, CMR));
2088c2ecf20Sopenharmony_ci	writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* Enable clock and interrupts on RC compare */
2118c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* go go gadget! */
2148c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs +
2158c2ecf20Sopenharmony_ci		     ATMEL_TC_REG(2, CCR));
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int tc_next_event(unsigned long delta, struct clock_event_device *d)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC));
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* go go gadget! */
2248c2ecf20Sopenharmony_ci	writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
2258c2ecf20Sopenharmony_ci			tcaddr + ATMEL_TC_REG(2, CCR));
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic struct tc_clkevt_device clkevt = {
2308c2ecf20Sopenharmony_ci	.clkevt	= {
2318c2ecf20Sopenharmony_ci		.features		= CLOCK_EVT_FEAT_PERIODIC |
2328c2ecf20Sopenharmony_ci					  CLOCK_EVT_FEAT_ONESHOT,
2338c2ecf20Sopenharmony_ci		/* Should be lower than at91rm9200's system timer */
2348c2ecf20Sopenharmony_ci		.rating			= 125,
2358c2ecf20Sopenharmony_ci		.set_next_event		= tc_next_event,
2368c2ecf20Sopenharmony_ci		.set_state_shutdown	= tc_shutdown,
2378c2ecf20Sopenharmony_ci		.set_state_periodic	= tc_set_periodic,
2388c2ecf20Sopenharmony_ci		.set_state_oneshot	= tc_set_oneshot,
2398c2ecf20Sopenharmony_ci	},
2408c2ecf20Sopenharmony_ci};
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic irqreturn_t ch2_irq(int irq, void *handle)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct tc_clkevt_device	*dev = handle;
2458c2ecf20Sopenharmony_ci	unsigned int		sr;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR));
2488c2ecf20Sopenharmony_ci	if (sr & ATMEL_TC_CPCS) {
2498c2ecf20Sopenharmony_ci		dev->clkevt.event_handler(&dev->clkevt);
2508c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return IRQ_NONE;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int ret;
2598c2ecf20Sopenharmony_ci	struct clk *t2_clk = tc->clk[2];
2608c2ecf20Sopenharmony_ci	int irq = tc->irq[2];
2618c2ecf20Sopenharmony_ci	int bits = tc->tcb_config->counter_width;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* try to enable t2 clk to avoid future errors in mode change */
2648c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(t2_clk);
2658c2ecf20Sopenharmony_ci	if (ret)
2668c2ecf20Sopenharmony_ci		return ret;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	clkevt.regs = tc->regs;
2698c2ecf20Sopenharmony_ci	clkevt.clk = t2_clk;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (bits == 32) {
2728c2ecf20Sopenharmony_ci		timer_clock = divisor_idx;
2738c2ecf20Sopenharmony_ci		clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx];
2748c2ecf20Sopenharmony_ci	} else {
2758c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(tc->slow_clk);
2768c2ecf20Sopenharmony_ci		if (ret) {
2778c2ecf20Sopenharmony_ci			clk_disable_unprepare(t2_clk);
2788c2ecf20Sopenharmony_ci			return ret;
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		clkevt.rate = clk_get_rate(tc->slow_clk);
2828c2ecf20Sopenharmony_ci		timer_clock = ATMEL_TC_TIMER_CLOCK5;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	clk_disable(t2_clk);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	clkevt.clkevt.cpumask = cpumask_of(0);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
2908c2ecf20Sopenharmony_ci	if (ret) {
2918c2ecf20Sopenharmony_ci		clk_unprepare(t2_clk);
2928c2ecf20Sopenharmony_ci		if (bits != 32)
2938c2ecf20Sopenharmony_ci			clk_disable_unprepare(tc->slow_clk);
2948c2ecf20Sopenharmony_ci		return ret;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return ret;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci#else /* !CONFIG_GENERIC_CLOCKEVENTS */
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	/* NOTHING */
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci#endif
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	/* channel 0:  waveform mode, input mclk/8, clock TIOA0 on overflow */
3158c2ecf20Sopenharmony_ci	writel(mck_divisor_idx			/* likely divide-by-8 */
3168c2ecf20Sopenharmony_ci			| ATMEL_TC_WAVE
3178c2ecf20Sopenharmony_ci			| ATMEL_TC_WAVESEL_UP		/* free-run */
3188c2ecf20Sopenharmony_ci			| ATMEL_TC_ASWTRG_SET		/* TIOA0 rises at software trigger */
3198c2ecf20Sopenharmony_ci			| ATMEL_TC_ACPA_SET		/* TIOA0 rises at 0 */
3208c2ecf20Sopenharmony_ci			| ATMEL_TC_ACPC_CLEAR,		/* (duty cycle 50%) */
3218c2ecf20Sopenharmony_ci			tcaddr + ATMEL_TC_REG(0, CMR));
3228c2ecf20Sopenharmony_ci	writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
3238c2ecf20Sopenharmony_ci	writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
3248c2ecf20Sopenharmony_ci	writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR));	/* no irqs */
3258c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* channel 1:  waveform mode, input TIOA0 */
3288c2ecf20Sopenharmony_ci	writel(ATMEL_TC_XC1			/* input: TIOA0 */
3298c2ecf20Sopenharmony_ci			| ATMEL_TC_WAVE
3308c2ecf20Sopenharmony_ci			| ATMEL_TC_WAVESEL_UP,		/* free-run */
3318c2ecf20Sopenharmony_ci			tcaddr + ATMEL_TC_REG(1, CMR));
3328c2ecf20Sopenharmony_ci	writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR));	/* no irqs */
3338c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* chain channel 0 to channel 1*/
3368c2ecf20Sopenharmony_ci	writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
3378c2ecf20Sopenharmony_ci	/* then reset all the timers */
3388c2ecf20Sopenharmony_ci	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	/* channel 0:  waveform mode, input mclk/8 */
3448c2ecf20Sopenharmony_ci	writel(mck_divisor_idx			/* likely divide-by-8 */
3458c2ecf20Sopenharmony_ci			| ATMEL_TC_WAVE
3468c2ecf20Sopenharmony_ci			| ATMEL_TC_WAVESEL_UP,		/* free-run */
3478c2ecf20Sopenharmony_ci			tcaddr + ATMEL_TC_REG(0, CMR));
3488c2ecf20Sopenharmony_ci	writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR));	/* no irqs */
3498c2ecf20Sopenharmony_ci	writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* then reset all the timers */
3528c2ecf20Sopenharmony_ci	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic struct atmel_tcb_config tcb_rm9200_config = {
3568c2ecf20Sopenharmony_ci	.counter_width = 16,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic struct atmel_tcb_config tcb_sam9x5_config = {
3608c2ecf20Sopenharmony_ci	.counter_width = 32,
3618c2ecf20Sopenharmony_ci};
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic struct atmel_tcb_config tcb_sama5d2_config = {
3648c2ecf20Sopenharmony_ci	.counter_width = 32,
3658c2ecf20Sopenharmony_ci	.has_gclk = 1,
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_tcb_of_match[] = {
3698c2ecf20Sopenharmony_ci	{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
3708c2ecf20Sopenharmony_ci	{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
3718c2ecf20Sopenharmony_ci	{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
3728c2ecf20Sopenharmony_ci	{ /* sentinel */ }
3738c2ecf20Sopenharmony_ci};
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int __init tcb_clksrc_init(struct device_node *node)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct atmel_tc tc;
3788c2ecf20Sopenharmony_ci	struct clk *t0_clk;
3798c2ecf20Sopenharmony_ci	const struct of_device_id *match;
3808c2ecf20Sopenharmony_ci	u64 (*tc_sched_clock)(void);
3818c2ecf20Sopenharmony_ci	u32 rate, divided_rate = 0;
3828c2ecf20Sopenharmony_ci	int best_divisor_idx = -1;
3838c2ecf20Sopenharmony_ci	int bits;
3848c2ecf20Sopenharmony_ci	int i;
3858c2ecf20Sopenharmony_ci	int ret;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Protect against multiple calls */
3888c2ecf20Sopenharmony_ci	if (tcaddr)
3898c2ecf20Sopenharmony_ci		return 0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	tc.regs = of_iomap(node->parent, 0);
3928c2ecf20Sopenharmony_ci	if (!tc.regs)
3938c2ecf20Sopenharmony_ci		return -ENXIO;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	t0_clk = of_clk_get_by_name(node->parent, "t0_clk");
3968c2ecf20Sopenharmony_ci	if (IS_ERR(t0_clk))
3978c2ecf20Sopenharmony_ci		return PTR_ERR(t0_clk);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk");
4008c2ecf20Sopenharmony_ci	if (IS_ERR(tc.slow_clk))
4018c2ecf20Sopenharmony_ci		return PTR_ERR(tc.slow_clk);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	tc.clk[0] = t0_clk;
4048c2ecf20Sopenharmony_ci	tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk");
4058c2ecf20Sopenharmony_ci	if (IS_ERR(tc.clk[1]))
4068c2ecf20Sopenharmony_ci		tc.clk[1] = t0_clk;
4078c2ecf20Sopenharmony_ci	tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk");
4088c2ecf20Sopenharmony_ci	if (IS_ERR(tc.clk[2]))
4098c2ecf20Sopenharmony_ci		tc.clk[2] = t0_clk;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	tc.irq[2] = of_irq_get(node->parent, 2);
4128c2ecf20Sopenharmony_ci	if (tc.irq[2] <= 0) {
4138c2ecf20Sopenharmony_ci		tc.irq[2] = of_irq_get(node->parent, 0);
4148c2ecf20Sopenharmony_ci		if (tc.irq[2] <= 0)
4158c2ecf20Sopenharmony_ci			return -EINVAL;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	match = of_match_node(atmel_tcb_of_match, node->parent);
4198c2ecf20Sopenharmony_ci	if (!match)
4208c2ecf20Sopenharmony_ci		return -ENODEV;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	tc.tcb_config = match->data;
4238c2ecf20Sopenharmony_ci	bits = tc.tcb_config->counter_width;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tc.irq); i++)
4268c2ecf20Sopenharmony_ci		writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR));
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(t0_clk);
4298c2ecf20Sopenharmony_ci	if (ret) {
4308c2ecf20Sopenharmony_ci		pr_debug("can't enable T0 clk\n");
4318c2ecf20Sopenharmony_ci		return ret;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* How fast will we be counting?  Pick something over 5 MHz.  */
4358c2ecf20Sopenharmony_ci	rate = (u32) clk_get_rate(t0_clk);
4368c2ecf20Sopenharmony_ci	i = 0;
4378c2ecf20Sopenharmony_ci	if (tc.tcb_config->has_gclk)
4388c2ecf20Sopenharmony_ci		i = 1;
4398c2ecf20Sopenharmony_ci	for (; i < ARRAY_SIZE(atmel_tcb_divisors); i++) {
4408c2ecf20Sopenharmony_ci		unsigned divisor = atmel_tcb_divisors[i];
4418c2ecf20Sopenharmony_ci		unsigned tmp;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		tmp = rate / divisor;
4448c2ecf20Sopenharmony_ci		pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
4458c2ecf20Sopenharmony_ci		if ((best_divisor_idx >= 0) && (tmp < 5 * 1000 * 1000))
4468c2ecf20Sopenharmony_ci			break;
4478c2ecf20Sopenharmony_ci		divided_rate = tmp;
4488c2ecf20Sopenharmony_ci		best_divisor_idx = i;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	clksrc.name = kbasename(node->parent->full_name);
4528c2ecf20Sopenharmony_ci	clkevt.clkevt.name = kbasename(node->parent->full_name);
4538c2ecf20Sopenharmony_ci	pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000,
4548c2ecf20Sopenharmony_ci			((divided_rate % 1000000) + 500) / 1000);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	tcaddr = tc.regs;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (bits == 32) {
4598c2ecf20Sopenharmony_ci		/* use apropriate function to read 32 bit counter */
4608c2ecf20Sopenharmony_ci		clksrc.read = tc_get_cycles32;
4618c2ecf20Sopenharmony_ci		/* setup ony channel 0 */
4628c2ecf20Sopenharmony_ci		tcb_setup_single_chan(&tc, best_divisor_idx);
4638c2ecf20Sopenharmony_ci		tc_sched_clock = tc_sched_clock_read32;
4648c2ecf20Sopenharmony_ci		tc_delay_timer.read_current_timer = tc_delay_timer_read32;
4658c2ecf20Sopenharmony_ci	} else {
4668c2ecf20Sopenharmony_ci		/* we have three clocks no matter what the
4678c2ecf20Sopenharmony_ci		 * underlying platform supports.
4688c2ecf20Sopenharmony_ci		 */
4698c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(tc.clk[1]);
4708c2ecf20Sopenharmony_ci		if (ret) {
4718c2ecf20Sopenharmony_ci			pr_debug("can't enable T1 clk\n");
4728c2ecf20Sopenharmony_ci			goto err_disable_t0;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci		/* setup both channel 0 & 1 */
4758c2ecf20Sopenharmony_ci		tcb_setup_dual_chan(&tc, best_divisor_idx);
4768c2ecf20Sopenharmony_ci		tc_sched_clock = tc_sched_clock_read;
4778c2ecf20Sopenharmony_ci		tc_delay_timer.read_current_timer = tc_delay_timer_read;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	/* and away we go! */
4818c2ecf20Sopenharmony_ci	ret = clocksource_register_hz(&clksrc, divided_rate);
4828c2ecf20Sopenharmony_ci	if (ret)
4838c2ecf20Sopenharmony_ci		goto err_disable_t1;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* channel 2:  periodic and oneshot timer support */
4868c2ecf20Sopenharmony_ci	ret = setup_clkevents(&tc, best_divisor_idx);
4878c2ecf20Sopenharmony_ci	if (ret)
4888c2ecf20Sopenharmony_ci		goto err_unregister_clksrc;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	sched_clock_register(tc_sched_clock, 32, divided_rate);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	tc_delay_timer.freq = divided_rate;
4938c2ecf20Sopenharmony_ci	register_current_timer_delay(&tc_delay_timer);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cierr_unregister_clksrc:
4988c2ecf20Sopenharmony_ci	clocksource_unregister(&clksrc);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cierr_disable_t1:
5018c2ecf20Sopenharmony_ci	if (bits != 32)
5028c2ecf20Sopenharmony_ci		clk_disable_unprepare(tc.clk[1]);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cierr_disable_t0:
5058c2ecf20Sopenharmony_ci	clk_disable_unprepare(t0_clk);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	tcaddr = NULL;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return ret;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init);
512