18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci//  Copyright (C) 2000-2001 Deep Blue Solutions
48c2ecf20Sopenharmony_ci//  Copyright (C) 2002 Shane Nay (shane@minirl.com)
58c2ecf20Sopenharmony_ci//  Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com)
68c2ecf20Sopenharmony_ci//  Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/irq.h>
108c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
198c2ecf20Sopenharmony_ci#include <soc/imx/timer.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * There are 4 versions of the timer hardware on Freescale MXC hardware.
238c2ecf20Sopenharmony_ci *  - MX1/MXL
248c2ecf20Sopenharmony_ci *  - MX21, MX27.
258c2ecf20Sopenharmony_ci *  - MX25, MX31, MX35, MX37, MX51, MX6Q(rev1.0)
268c2ecf20Sopenharmony_ci *  - MX6DL, MX6SX, MX6Q(rev1.1+)
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* defines common for all i.MX */
308c2ecf20Sopenharmony_ci#define MXC_TCTL		0x00
318c2ecf20Sopenharmony_ci#define MXC_TCTL_TEN		(1 << 0) /* Enable module */
328c2ecf20Sopenharmony_ci#define MXC_TPRER		0x04
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* MX1, MX21, MX27 */
358c2ecf20Sopenharmony_ci#define MX1_2_TCTL_CLK_PCLK1	(1 << 1)
368c2ecf20Sopenharmony_ci#define MX1_2_TCTL_IRQEN	(1 << 4)
378c2ecf20Sopenharmony_ci#define MX1_2_TCTL_FRR		(1 << 8)
388c2ecf20Sopenharmony_ci#define MX1_2_TCMP		0x08
398c2ecf20Sopenharmony_ci#define MX1_2_TCN		0x10
408c2ecf20Sopenharmony_ci#define MX1_2_TSTAT		0x14
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* MX21, MX27 */
438c2ecf20Sopenharmony_ci#define MX2_TSTAT_CAPT		(1 << 1)
448c2ecf20Sopenharmony_ci#define MX2_TSTAT_COMP		(1 << 0)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* MX31, MX35, MX25, MX5, MX6 */
478c2ecf20Sopenharmony_ci#define V2_TCTL_WAITEN		(1 << 3) /* Wait enable mode */
488c2ecf20Sopenharmony_ci#define V2_TCTL_CLK_IPG		(1 << 6)
498c2ecf20Sopenharmony_ci#define V2_TCTL_CLK_PER		(2 << 6)
508c2ecf20Sopenharmony_ci#define V2_TCTL_CLK_OSC_DIV8	(5 << 6)
518c2ecf20Sopenharmony_ci#define V2_TCTL_FRR		(1 << 9)
528c2ecf20Sopenharmony_ci#define V2_TCTL_24MEN		(1 << 10)
538c2ecf20Sopenharmony_ci#define V2_TPRER_PRE24M		12
548c2ecf20Sopenharmony_ci#define V2_IR			0x0c
558c2ecf20Sopenharmony_ci#define V2_TSTAT		0x08
568c2ecf20Sopenharmony_ci#define V2_TSTAT_OF1		(1 << 0)
578c2ecf20Sopenharmony_ci#define V2_TCN			0x24
588c2ecf20Sopenharmony_ci#define V2_TCMP			0x10
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define V2_TIMER_RATE_OSC_DIV8	3000000
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct imx_timer {
638c2ecf20Sopenharmony_ci	enum imx_gpt_type type;
648c2ecf20Sopenharmony_ci	void __iomem *base;
658c2ecf20Sopenharmony_ci	int irq;
668c2ecf20Sopenharmony_ci	struct clk *clk_per;
678c2ecf20Sopenharmony_ci	struct clk *clk_ipg;
688c2ecf20Sopenharmony_ci	const struct imx_gpt_data *gpt;
698c2ecf20Sopenharmony_ci	struct clock_event_device ced;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct imx_gpt_data {
738c2ecf20Sopenharmony_ci	int reg_tstat;
748c2ecf20Sopenharmony_ci	int reg_tcn;
758c2ecf20Sopenharmony_ci	int reg_tcmp;
768c2ecf20Sopenharmony_ci	void (*gpt_setup_tctl)(struct imx_timer *imxtm);
778c2ecf20Sopenharmony_ci	void (*gpt_irq_enable)(struct imx_timer *imxtm);
788c2ecf20Sopenharmony_ci	void (*gpt_irq_disable)(struct imx_timer *imxtm);
798c2ecf20Sopenharmony_ci	void (*gpt_irq_acknowledge)(struct imx_timer *imxtm);
808c2ecf20Sopenharmony_ci	int (*set_next_event)(unsigned long evt,
818c2ecf20Sopenharmony_ci			      struct clock_event_device *ced);
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	return container_of(ced, struct imx_timer, ced);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void imx1_gpt_irq_disable(struct imx_timer *imxtm)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned int tmp;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
948c2ecf20Sopenharmony_ci	writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci#define imx21_gpt_irq_disable imx1_gpt_irq_disable
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void imx31_gpt_irq_disable(struct imx_timer *imxtm)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	writel_relaxed(0, imxtm->base + V2_IR);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci#define imx6dl_gpt_irq_disable imx31_gpt_irq_disable
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void imx1_gpt_irq_enable(struct imx_timer *imxtm)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	unsigned int tmp;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
1098c2ecf20Sopenharmony_ci	writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci#define imx21_gpt_irq_enable imx1_gpt_irq_enable
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void imx31_gpt_irq_enable(struct imx_timer *imxtm)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	writel_relaxed(1<<0, imxtm->base + V2_IR);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci#define imx6dl_gpt_irq_enable imx31_gpt_irq_enable
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	writel_relaxed(0, imxtm->base + MX1_2_TSTAT);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void imx21_gpt_irq_acknowledge(struct imx_timer *imxtm)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	writel_relaxed(MX2_TSTAT_CAPT | MX2_TSTAT_COMP,
1278c2ecf20Sopenharmony_ci				imxtm->base + MX1_2_TSTAT);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci#define imx6dl_gpt_irq_acknowledge imx31_gpt_irq_acknowledge
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void __iomem *sched_clock_reg;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic u64 notrace mxc_read_sched_clock(void)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return sched_clock_reg ? readl_relaxed(sched_clock_reg) : 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM)
1448c2ecf20Sopenharmony_cistatic struct delay_timer imx_delay_timer;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic unsigned long imx_read_current_timer(void)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return readl_relaxed(sched_clock_reg);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci#endif
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int __init mxc_clocksource_init(struct imx_timer *imxtm)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned int c = clk_get_rate(imxtm->clk_per);
1558c2ecf20Sopenharmony_ci	void __iomem *reg = imxtm->base + imxtm->gpt->reg_tcn;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM)
1588c2ecf20Sopenharmony_ci	imx_delay_timer.read_current_timer = &imx_read_current_timer;
1598c2ecf20Sopenharmony_ci	imx_delay_timer.freq = c;
1608c2ecf20Sopenharmony_ci	register_current_timer_delay(&imx_delay_timer);
1618c2ecf20Sopenharmony_ci#endif
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	sched_clock_reg = reg;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	sched_clock_register(mxc_read_sched_clock, 32, c);
1668c2ecf20Sopenharmony_ci	return clocksource_mmio_init(reg, "mxc_timer1", c, 200, 32,
1678c2ecf20Sopenharmony_ci			clocksource_mmio_readl_up);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/* clock event */
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int mx1_2_set_next_event(unsigned long evt,
1738c2ecf20Sopenharmony_ci			      struct clock_event_device *ced)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct imx_timer *imxtm = to_imx_timer(ced);
1768c2ecf20Sopenharmony_ci	unsigned long tcmp;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	tcmp = readl_relaxed(imxtm->base + MX1_2_TCN) + evt;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	writel_relaxed(tcmp, imxtm->base + MX1_2_TCMP);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return (int)(tcmp - readl_relaxed(imxtm->base + MX1_2_TCN)) < 0 ?
1838c2ecf20Sopenharmony_ci				-ETIME : 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int v2_set_next_event(unsigned long evt,
1878c2ecf20Sopenharmony_ci			      struct clock_event_device *ced)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct imx_timer *imxtm = to_imx_timer(ced);
1908c2ecf20Sopenharmony_ci	unsigned long tcmp;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	tcmp = readl_relaxed(imxtm->base + V2_TCN) + evt;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	writel_relaxed(tcmp, imxtm->base + V2_TCMP);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return evt < 0x7fffffff &&
1978c2ecf20Sopenharmony_ci		(int)(tcmp - readl_relaxed(imxtm->base + V2_TCN)) < 0 ?
1988c2ecf20Sopenharmony_ci				-ETIME : 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int mxc_shutdown(struct clock_event_device *ced)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct imx_timer *imxtm = to_imx_timer(ced);
2048c2ecf20Sopenharmony_ci	u32 tcn;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Disable interrupt in GPT module */
2078c2ecf20Sopenharmony_ci	imxtm->gpt->gpt_irq_disable(imxtm);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
2108c2ecf20Sopenharmony_ci	/* Set event time into far-far future */
2118c2ecf20Sopenharmony_ci	writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* Clear pending interrupt */
2148c2ecf20Sopenharmony_ci	imxtm->gpt->gpt_irq_acknowledge(imxtm);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci#ifdef DEBUG
2178c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: changing mode\n", __func__);
2188c2ecf20Sopenharmony_ci#endif /* DEBUG */
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int mxc_set_oneshot(struct clock_event_device *ced)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct imx_timer *imxtm = to_imx_timer(ced);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* Disable interrupt in GPT module */
2288c2ecf20Sopenharmony_ci	imxtm->gpt->gpt_irq_disable(imxtm);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (!clockevent_state_oneshot(ced)) {
2318c2ecf20Sopenharmony_ci		u32 tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
2328c2ecf20Sopenharmony_ci		/* Set event time into far-far future */
2338c2ecf20Sopenharmony_ci		writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		/* Clear pending interrupt */
2368c2ecf20Sopenharmony_ci		imxtm->gpt->gpt_irq_acknowledge(imxtm);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci#ifdef DEBUG
2408c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: changing mode\n", __func__);
2418c2ecf20Sopenharmony_ci#endif /* DEBUG */
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/*
2448c2ecf20Sopenharmony_ci	 * Do not put overhead of interrupt enable/disable into
2458c2ecf20Sopenharmony_ci	 * mxc_set_next_event(), the core has about 4 minutes
2468c2ecf20Sopenharmony_ci	 * to call mxc_set_next_event() or shutdown clock after
2478c2ecf20Sopenharmony_ci	 * mode switching
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	imxtm->gpt->gpt_irq_enable(imxtm);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return 0;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci * IRQ handler for the timer
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_cistatic irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct clock_event_device *ced = dev_id;
2608c2ecf20Sopenharmony_ci	struct imx_timer *imxtm = to_imx_timer(ced);
2618c2ecf20Sopenharmony_ci	uint32_t tstat;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	imxtm->gpt->gpt_irq_acknowledge(imxtm);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ced->event_handler(ced);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int __init mxc_clockevent_init(struct imx_timer *imxtm)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct clock_event_device *ced = &imxtm->ced;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	ced->name = "mxc_timer1";
2778c2ecf20Sopenharmony_ci	ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
2788c2ecf20Sopenharmony_ci	ced->set_state_shutdown = mxc_shutdown;
2798c2ecf20Sopenharmony_ci	ced->set_state_oneshot = mxc_set_oneshot;
2808c2ecf20Sopenharmony_ci	ced->tick_resume = mxc_shutdown;
2818c2ecf20Sopenharmony_ci	ced->set_next_event = imxtm->gpt->set_next_event;
2828c2ecf20Sopenharmony_ci	ced->rating = 200;
2838c2ecf20Sopenharmony_ci	ced->cpumask = cpumask_of(0);
2848c2ecf20Sopenharmony_ci	ced->irq = imxtm->irq;
2858c2ecf20Sopenharmony_ci	clockevents_config_and_register(ced, clk_get_rate(imxtm->clk_per),
2868c2ecf20Sopenharmony_ci					0xff, 0xfffffffe);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return request_irq(imxtm->irq, mxc_timer_interrupt,
2898c2ecf20Sopenharmony_ci			   IRQF_TIMER | IRQF_IRQPOLL, "i.MX Timer Tick", ced);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void imx1_gpt_setup_tctl(struct imx_timer *imxtm)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	u32 tctl_val;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
2978c2ecf20Sopenharmony_ci	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci#define imx21_gpt_setup_tctl imx1_gpt_setup_tctl
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	u32 tctl_val;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	tctl_val = V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN;
3068c2ecf20Sopenharmony_ci	if (clk_get_rate(imxtm->clk_per) == V2_TIMER_RATE_OSC_DIV8)
3078c2ecf20Sopenharmony_ci		tctl_val |= V2_TCTL_CLK_OSC_DIV8;
3088c2ecf20Sopenharmony_ci	else
3098c2ecf20Sopenharmony_ci		tctl_val |= V2_TCTL_CLK_PER;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic void imx6dl_gpt_setup_tctl(struct imx_timer *imxtm)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	u32 tctl_val;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	tctl_val = V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN;
3198c2ecf20Sopenharmony_ci	if (clk_get_rate(imxtm->clk_per) == V2_TIMER_RATE_OSC_DIV8) {
3208c2ecf20Sopenharmony_ci		tctl_val |= V2_TCTL_CLK_OSC_DIV8;
3218c2ecf20Sopenharmony_ci		/* 24 / 8 = 3 MHz */
3228c2ecf20Sopenharmony_ci		writel_relaxed(7 << V2_TPRER_PRE24M, imxtm->base + MXC_TPRER);
3238c2ecf20Sopenharmony_ci		tctl_val |= V2_TCTL_24MEN;
3248c2ecf20Sopenharmony_ci	} else {
3258c2ecf20Sopenharmony_ci		tctl_val |= V2_TCTL_CLK_PER;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic const struct imx_gpt_data imx1_gpt_data = {
3328c2ecf20Sopenharmony_ci	.reg_tstat = MX1_2_TSTAT,
3338c2ecf20Sopenharmony_ci	.reg_tcn = MX1_2_TCN,
3348c2ecf20Sopenharmony_ci	.reg_tcmp = MX1_2_TCMP,
3358c2ecf20Sopenharmony_ci	.gpt_irq_enable = imx1_gpt_irq_enable,
3368c2ecf20Sopenharmony_ci	.gpt_irq_disable = imx1_gpt_irq_disable,
3378c2ecf20Sopenharmony_ci	.gpt_irq_acknowledge = imx1_gpt_irq_acknowledge,
3388c2ecf20Sopenharmony_ci	.gpt_setup_tctl = imx1_gpt_setup_tctl,
3398c2ecf20Sopenharmony_ci	.set_next_event = mx1_2_set_next_event,
3408c2ecf20Sopenharmony_ci};
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic const struct imx_gpt_data imx21_gpt_data = {
3438c2ecf20Sopenharmony_ci	.reg_tstat = MX1_2_TSTAT,
3448c2ecf20Sopenharmony_ci	.reg_tcn = MX1_2_TCN,
3458c2ecf20Sopenharmony_ci	.reg_tcmp = MX1_2_TCMP,
3468c2ecf20Sopenharmony_ci	.gpt_irq_enable = imx21_gpt_irq_enable,
3478c2ecf20Sopenharmony_ci	.gpt_irq_disable = imx21_gpt_irq_disable,
3488c2ecf20Sopenharmony_ci	.gpt_irq_acknowledge = imx21_gpt_irq_acknowledge,
3498c2ecf20Sopenharmony_ci	.gpt_setup_tctl = imx21_gpt_setup_tctl,
3508c2ecf20Sopenharmony_ci	.set_next_event = mx1_2_set_next_event,
3518c2ecf20Sopenharmony_ci};
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic const struct imx_gpt_data imx31_gpt_data = {
3548c2ecf20Sopenharmony_ci	.reg_tstat = V2_TSTAT,
3558c2ecf20Sopenharmony_ci	.reg_tcn = V2_TCN,
3568c2ecf20Sopenharmony_ci	.reg_tcmp = V2_TCMP,
3578c2ecf20Sopenharmony_ci	.gpt_irq_enable = imx31_gpt_irq_enable,
3588c2ecf20Sopenharmony_ci	.gpt_irq_disable = imx31_gpt_irq_disable,
3598c2ecf20Sopenharmony_ci	.gpt_irq_acknowledge = imx31_gpt_irq_acknowledge,
3608c2ecf20Sopenharmony_ci	.gpt_setup_tctl = imx31_gpt_setup_tctl,
3618c2ecf20Sopenharmony_ci	.set_next_event = v2_set_next_event,
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic const struct imx_gpt_data imx6dl_gpt_data = {
3658c2ecf20Sopenharmony_ci	.reg_tstat = V2_TSTAT,
3668c2ecf20Sopenharmony_ci	.reg_tcn = V2_TCN,
3678c2ecf20Sopenharmony_ci	.reg_tcmp = V2_TCMP,
3688c2ecf20Sopenharmony_ci	.gpt_irq_enable = imx6dl_gpt_irq_enable,
3698c2ecf20Sopenharmony_ci	.gpt_irq_disable = imx6dl_gpt_irq_disable,
3708c2ecf20Sopenharmony_ci	.gpt_irq_acknowledge = imx6dl_gpt_irq_acknowledge,
3718c2ecf20Sopenharmony_ci	.gpt_setup_tctl = imx6dl_gpt_setup_tctl,
3728c2ecf20Sopenharmony_ci	.set_next_event = v2_set_next_event,
3738c2ecf20Sopenharmony_ci};
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int __init _mxc_timer_init(struct imx_timer *imxtm)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	int ret;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	switch (imxtm->type) {
3808c2ecf20Sopenharmony_ci	case GPT_TYPE_IMX1:
3818c2ecf20Sopenharmony_ci		imxtm->gpt = &imx1_gpt_data;
3828c2ecf20Sopenharmony_ci		break;
3838c2ecf20Sopenharmony_ci	case GPT_TYPE_IMX21:
3848c2ecf20Sopenharmony_ci		imxtm->gpt = &imx21_gpt_data;
3858c2ecf20Sopenharmony_ci		break;
3868c2ecf20Sopenharmony_ci	case GPT_TYPE_IMX31:
3878c2ecf20Sopenharmony_ci		imxtm->gpt = &imx31_gpt_data;
3888c2ecf20Sopenharmony_ci		break;
3898c2ecf20Sopenharmony_ci	case GPT_TYPE_IMX6DL:
3908c2ecf20Sopenharmony_ci		imxtm->gpt = &imx6dl_gpt_data;
3918c2ecf20Sopenharmony_ci		break;
3928c2ecf20Sopenharmony_ci	default:
3938c2ecf20Sopenharmony_ci		return -EINVAL;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (IS_ERR(imxtm->clk_per)) {
3978c2ecf20Sopenharmony_ci		pr_err("i.MX timer: unable to get clk\n");
3988c2ecf20Sopenharmony_ci		return PTR_ERR(imxtm->clk_per);
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (!IS_ERR(imxtm->clk_ipg))
4028c2ecf20Sopenharmony_ci		clk_prepare_enable(imxtm->clk_ipg);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	clk_prepare_enable(imxtm->clk_per);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/*
4078c2ecf20Sopenharmony_ci	 * Initialise to a known state (all timers off, and timing reset)
4088c2ecf20Sopenharmony_ci	 */
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	writel_relaxed(0, imxtm->base + MXC_TCTL);
4118c2ecf20Sopenharmony_ci	writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	imxtm->gpt->gpt_setup_tctl(imxtm);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* init and register the timer to the framework */
4168c2ecf20Sopenharmony_ci	ret = mxc_clocksource_init(imxtm);
4178c2ecf20Sopenharmony_ci	if (ret)
4188c2ecf20Sopenharmony_ci		return ret;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return mxc_clockevent_init(imxtm);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_civoid __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct imx_timer *imxtm;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL);
4288c2ecf20Sopenharmony_ci	BUG_ON(!imxtm);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	imxtm->clk_per = clk_get_sys("imx-gpt.0", "per");
4318c2ecf20Sopenharmony_ci	imxtm->clk_ipg = clk_get_sys("imx-gpt.0", "ipg");
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	imxtm->base = ioremap(pbase, SZ_4K);
4348c2ecf20Sopenharmony_ci	BUG_ON(!imxtm->base);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	imxtm->type = type;
4378c2ecf20Sopenharmony_ci	imxtm->irq = irq;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	_mxc_timer_init(imxtm);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int __init mxc_timer_init_dt(struct device_node *np,  enum imx_gpt_type type)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct imx_timer *imxtm;
4458c2ecf20Sopenharmony_ci	static int initialized;
4468c2ecf20Sopenharmony_ci	int ret;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Support one instance only */
4498c2ecf20Sopenharmony_ci	if (initialized)
4508c2ecf20Sopenharmony_ci		return 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL);
4538c2ecf20Sopenharmony_ci	if (!imxtm)
4548c2ecf20Sopenharmony_ci		return -ENOMEM;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	imxtm->base = of_iomap(np, 0);
4578c2ecf20Sopenharmony_ci	if (!imxtm->base) {
4588c2ecf20Sopenharmony_ci		ret = -ENXIO;
4598c2ecf20Sopenharmony_ci		goto err_kfree;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	imxtm->irq = irq_of_parse_and_map(np, 0);
4638c2ecf20Sopenharmony_ci	if (imxtm->irq <= 0) {
4648c2ecf20Sopenharmony_ci		ret = -EINVAL;
4658c2ecf20Sopenharmony_ci		goto err_kfree;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	imxtm->clk_ipg = of_clk_get_by_name(np, "ipg");
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* Try osc_per first, and fall back to per otherwise */
4718c2ecf20Sopenharmony_ci	imxtm->clk_per = of_clk_get_by_name(np, "osc_per");
4728c2ecf20Sopenharmony_ci	if (IS_ERR(imxtm->clk_per))
4738c2ecf20Sopenharmony_ci		imxtm->clk_per = of_clk_get_by_name(np, "per");
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	imxtm->type = type;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	ret = _mxc_timer_init(imxtm);
4788c2ecf20Sopenharmony_ci	if (ret)
4798c2ecf20Sopenharmony_ci		goto err_kfree;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	initialized = 1;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	return 0;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cierr_kfree:
4868c2ecf20Sopenharmony_ci	kfree(imxtm);
4878c2ecf20Sopenharmony_ci	return ret;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic int __init imx1_timer_init_dt(struct device_node *np)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	return mxc_timer_init_dt(np, GPT_TYPE_IMX1);
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic int __init imx21_timer_init_dt(struct device_node *np)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	return mxc_timer_init_dt(np, GPT_TYPE_IMX21);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int __init imx31_timer_init_dt(struct device_node *np)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	enum imx_gpt_type type = GPT_TYPE_IMX31;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/*
5058c2ecf20Sopenharmony_ci	 * We were using the same compatible string for i.MX6Q/D and i.MX6DL/S
5068c2ecf20Sopenharmony_ci	 * GPT device, while they actually have different programming model.
5078c2ecf20Sopenharmony_ci	 * This is a workaround to keep the existing i.MX6DL/S DTBs continue
5088c2ecf20Sopenharmony_ci	 * working with the new kernel.
5098c2ecf20Sopenharmony_ci	 */
5108c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("fsl,imx6dl"))
5118c2ecf20Sopenharmony_ci		type = GPT_TYPE_IMX6DL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return mxc_timer_init_dt(np, type);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int __init imx6dl_timer_init_dt(struct device_node *np)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL);
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
5228c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
5238c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
5248c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
5258c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
5268c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
5278c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
5288c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
5298c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
5308c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
5318c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
5328c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
533