162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2000-2001 Deep Blue Solutions 462306a36Sopenharmony_ci// Copyright (C) 2002 Shane Nay (shane@minirl.com) 562306a36Sopenharmony_ci// Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com) 662306a36Sopenharmony_ci// Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/irq.h> 1062306a36Sopenharmony_ci#include <linux/clockchips.h> 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/sched_clock.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci#include <linux/of_irq.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * There are 4 versions of the timer hardware on Freescale MXC hardware. 2262306a36Sopenharmony_ci * - MX1/MXL 2362306a36Sopenharmony_ci * - MX21, MX27. 2462306a36Sopenharmony_ci * - MX25, MX31, MX35, MX37, MX51, MX6Q(rev1.0) 2562306a36Sopenharmony_ci * - MX6DL, MX6SX, MX6Q(rev1.1+) 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_cienum imx_gpt_type { 2862306a36Sopenharmony_ci GPT_TYPE_IMX1, /* i.MX1 */ 2962306a36Sopenharmony_ci GPT_TYPE_IMX21, /* i.MX21/27 */ 3062306a36Sopenharmony_ci GPT_TYPE_IMX31, /* i.MX31/35/25/37/51/6Q */ 3162306a36Sopenharmony_ci GPT_TYPE_IMX6DL, /* i.MX6DL/SX/SL */ 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* defines common for all i.MX */ 3562306a36Sopenharmony_ci#define MXC_TCTL 0x00 3662306a36Sopenharmony_ci#define MXC_TCTL_TEN (1 << 0) /* Enable module */ 3762306a36Sopenharmony_ci#define MXC_TPRER 0x04 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* MX1, MX21, MX27 */ 4062306a36Sopenharmony_ci#define MX1_2_TCTL_CLK_PCLK1 (1 << 1) 4162306a36Sopenharmony_ci#define MX1_2_TCTL_IRQEN (1 << 4) 4262306a36Sopenharmony_ci#define MX1_2_TCTL_FRR (1 << 8) 4362306a36Sopenharmony_ci#define MX1_2_TCMP 0x08 4462306a36Sopenharmony_ci#define MX1_2_TCN 0x10 4562306a36Sopenharmony_ci#define MX1_2_TSTAT 0x14 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* MX21, MX27 */ 4862306a36Sopenharmony_ci#define MX2_TSTAT_CAPT (1 << 1) 4962306a36Sopenharmony_ci#define MX2_TSTAT_COMP (1 << 0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* MX31, MX35, MX25, MX5, MX6 */ 5262306a36Sopenharmony_ci#define V2_TCTL_WAITEN (1 << 3) /* Wait enable mode */ 5362306a36Sopenharmony_ci#define V2_TCTL_CLK_IPG (1 << 6) 5462306a36Sopenharmony_ci#define V2_TCTL_CLK_PER (2 << 6) 5562306a36Sopenharmony_ci#define V2_TCTL_CLK_OSC_DIV8 (5 << 6) 5662306a36Sopenharmony_ci#define V2_TCTL_FRR (1 << 9) 5762306a36Sopenharmony_ci#define V2_TCTL_24MEN (1 << 10) 5862306a36Sopenharmony_ci#define V2_TPRER_PRE24M 12 5962306a36Sopenharmony_ci#define V2_IR 0x0c 6062306a36Sopenharmony_ci#define V2_TSTAT 0x08 6162306a36Sopenharmony_ci#define V2_TSTAT_OF1 (1 << 0) 6262306a36Sopenharmony_ci#define V2_TCN 0x24 6362306a36Sopenharmony_ci#define V2_TCMP 0x10 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define V2_TIMER_RATE_OSC_DIV8 3000000 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct imx_timer { 6862306a36Sopenharmony_ci enum imx_gpt_type type; 6962306a36Sopenharmony_ci void __iomem *base; 7062306a36Sopenharmony_ci int irq; 7162306a36Sopenharmony_ci struct clk *clk_per; 7262306a36Sopenharmony_ci struct clk *clk_ipg; 7362306a36Sopenharmony_ci const struct imx_gpt_data *gpt; 7462306a36Sopenharmony_ci struct clock_event_device ced; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct imx_gpt_data { 7862306a36Sopenharmony_ci int reg_tstat; 7962306a36Sopenharmony_ci int reg_tcn; 8062306a36Sopenharmony_ci int reg_tcmp; 8162306a36Sopenharmony_ci void (*gpt_setup_tctl)(struct imx_timer *imxtm); 8262306a36Sopenharmony_ci void (*gpt_irq_enable)(struct imx_timer *imxtm); 8362306a36Sopenharmony_ci void (*gpt_irq_disable)(struct imx_timer *imxtm); 8462306a36Sopenharmony_ci void (*gpt_irq_acknowledge)(struct imx_timer *imxtm); 8562306a36Sopenharmony_ci int (*set_next_event)(unsigned long evt, 8662306a36Sopenharmony_ci struct clock_event_device *ced); 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic inline struct imx_timer *to_imx_timer(struct clock_event_device *ced) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return container_of(ced, struct imx_timer, ced); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void imx1_gpt_irq_disable(struct imx_timer *imxtm) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci unsigned int tmp; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci tmp = readl_relaxed(imxtm->base + MXC_TCTL); 9962306a36Sopenharmony_ci writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void imx31_gpt_irq_disable(struct imx_timer *imxtm) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci writel_relaxed(0, imxtm->base + V2_IR); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void imx1_gpt_irq_enable(struct imx_timer *imxtm) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned int tmp; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci tmp = readl_relaxed(imxtm->base + MXC_TCTL); 11262306a36Sopenharmony_ci writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void imx31_gpt_irq_enable(struct imx_timer *imxtm) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci writel_relaxed(1<<0, imxtm->base + V2_IR); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci writel_relaxed(0, imxtm->base + MX1_2_TSTAT); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void imx21_gpt_irq_acknowledge(struct imx_timer *imxtm) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci writel_relaxed(MX2_TSTAT_CAPT | MX2_TSTAT_COMP, 12862306a36Sopenharmony_ci imxtm->base + MX1_2_TSTAT); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void __iomem *sched_clock_reg; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic u64 notrace mxc_read_sched_clock(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci return sched_clock_reg ? readl_relaxed(sched_clock_reg) : 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#if defined(CONFIG_ARM) 14462306a36Sopenharmony_cistatic struct delay_timer imx_delay_timer; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic unsigned long imx_read_current_timer(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return readl_relaxed(sched_clock_reg); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci#endif 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int __init mxc_clocksource_init(struct imx_timer *imxtm) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci unsigned int c = clk_get_rate(imxtm->clk_per); 15562306a36Sopenharmony_ci void __iomem *reg = imxtm->base + imxtm->gpt->reg_tcn; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#if defined(CONFIG_ARM) 15862306a36Sopenharmony_ci imx_delay_timer.read_current_timer = &imx_read_current_timer; 15962306a36Sopenharmony_ci imx_delay_timer.freq = c; 16062306a36Sopenharmony_ci register_current_timer_delay(&imx_delay_timer); 16162306a36Sopenharmony_ci#endif 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci sched_clock_reg = reg; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci sched_clock_register(mxc_read_sched_clock, 32, c); 16662306a36Sopenharmony_ci return clocksource_mmio_init(reg, "mxc_timer1", c, 200, 32, 16762306a36Sopenharmony_ci clocksource_mmio_readl_up); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* clock event */ 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int mx1_2_set_next_event(unsigned long evt, 17362306a36Sopenharmony_ci struct clock_event_device *ced) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct imx_timer *imxtm = to_imx_timer(ced); 17662306a36Sopenharmony_ci unsigned long tcmp; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci tcmp = readl_relaxed(imxtm->base + MX1_2_TCN) + evt; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci writel_relaxed(tcmp, imxtm->base + MX1_2_TCMP); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return (int)(tcmp - readl_relaxed(imxtm->base + MX1_2_TCN)) < 0 ? 18362306a36Sopenharmony_ci -ETIME : 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int v2_set_next_event(unsigned long evt, 18762306a36Sopenharmony_ci struct clock_event_device *ced) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct imx_timer *imxtm = to_imx_timer(ced); 19062306a36Sopenharmony_ci unsigned long tcmp; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci tcmp = readl_relaxed(imxtm->base + V2_TCN) + evt; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci writel_relaxed(tcmp, imxtm->base + V2_TCMP); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return evt < 0x7fffffff && 19762306a36Sopenharmony_ci (int)(tcmp - readl_relaxed(imxtm->base + V2_TCN)) < 0 ? 19862306a36Sopenharmony_ci -ETIME : 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int mxc_shutdown(struct clock_event_device *ced) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct imx_timer *imxtm = to_imx_timer(ced); 20462306a36Sopenharmony_ci u32 tcn; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Disable interrupt in GPT module */ 20762306a36Sopenharmony_ci imxtm->gpt->gpt_irq_disable(imxtm); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn); 21062306a36Sopenharmony_ci /* Set event time into far-far future */ 21162306a36Sopenharmony_ci writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Clear pending interrupt */ 21462306a36Sopenharmony_ci imxtm->gpt->gpt_irq_acknowledge(imxtm); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#ifdef DEBUG 21762306a36Sopenharmony_ci printk(KERN_INFO "%s: changing mode\n", __func__); 21862306a36Sopenharmony_ci#endif /* DEBUG */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int mxc_set_oneshot(struct clock_event_device *ced) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct imx_timer *imxtm = to_imx_timer(ced); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Disable interrupt in GPT module */ 22862306a36Sopenharmony_ci imxtm->gpt->gpt_irq_disable(imxtm); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!clockevent_state_oneshot(ced)) { 23162306a36Sopenharmony_ci u32 tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn); 23262306a36Sopenharmony_ci /* Set event time into far-far future */ 23362306a36Sopenharmony_ci writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Clear pending interrupt */ 23662306a36Sopenharmony_ci imxtm->gpt->gpt_irq_acknowledge(imxtm); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci#ifdef DEBUG 24062306a36Sopenharmony_ci printk(KERN_INFO "%s: changing mode\n", __func__); 24162306a36Sopenharmony_ci#endif /* DEBUG */ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * Do not put overhead of interrupt enable/disable into 24562306a36Sopenharmony_ci * mxc_set_next_event(), the core has about 4 minutes 24662306a36Sopenharmony_ci * to call mxc_set_next_event() or shutdown clock after 24762306a36Sopenharmony_ci * mode switching 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci imxtm->gpt->gpt_irq_enable(imxtm); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * IRQ handler for the timer 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_cistatic irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct clock_event_device *ced = dev_id; 26062306a36Sopenharmony_ci struct imx_timer *imxtm = to_imx_timer(ced); 26162306a36Sopenharmony_ci uint32_t tstat; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci imxtm->gpt->gpt_irq_acknowledge(imxtm); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ced->event_handler(ced); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return IRQ_HANDLED; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int __init mxc_clockevent_init(struct imx_timer *imxtm) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct clock_event_device *ced = &imxtm->ced; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ced->name = "mxc_timer1"; 27762306a36Sopenharmony_ci ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; 27862306a36Sopenharmony_ci ced->set_state_shutdown = mxc_shutdown; 27962306a36Sopenharmony_ci ced->set_state_oneshot = mxc_set_oneshot; 28062306a36Sopenharmony_ci ced->tick_resume = mxc_shutdown; 28162306a36Sopenharmony_ci ced->set_next_event = imxtm->gpt->set_next_event; 28262306a36Sopenharmony_ci ced->rating = 200; 28362306a36Sopenharmony_ci ced->cpumask = cpumask_of(0); 28462306a36Sopenharmony_ci ced->irq = imxtm->irq; 28562306a36Sopenharmony_ci clockevents_config_and_register(ced, clk_get_rate(imxtm->clk_per), 28662306a36Sopenharmony_ci 0xff, 0xfffffffe); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return request_irq(imxtm->irq, mxc_timer_interrupt, 28962306a36Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "i.MX Timer Tick", ced); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void imx1_gpt_setup_tctl(struct imx_timer *imxtm) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci u32 tctl_val; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN; 29762306a36Sopenharmony_ci writel_relaxed(tctl_val, imxtm->base + MXC_TCTL); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void imx31_gpt_setup_tctl(struct imx_timer *imxtm) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci u32 tctl_val; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci tctl_val = V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN; 30562306a36Sopenharmony_ci if (clk_get_rate(imxtm->clk_per) == V2_TIMER_RATE_OSC_DIV8) 30662306a36Sopenharmony_ci tctl_val |= V2_TCTL_CLK_OSC_DIV8; 30762306a36Sopenharmony_ci else 30862306a36Sopenharmony_ci tctl_val |= V2_TCTL_CLK_PER; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci writel_relaxed(tctl_val, imxtm->base + MXC_TCTL); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void imx6dl_gpt_setup_tctl(struct imx_timer *imxtm) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci u32 tctl_val; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci tctl_val = V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN; 31862306a36Sopenharmony_ci if (clk_get_rate(imxtm->clk_per) == V2_TIMER_RATE_OSC_DIV8) { 31962306a36Sopenharmony_ci tctl_val |= V2_TCTL_CLK_OSC_DIV8; 32062306a36Sopenharmony_ci /* 24 / 8 = 3 MHz */ 32162306a36Sopenharmony_ci writel_relaxed(7 << V2_TPRER_PRE24M, imxtm->base + MXC_TPRER); 32262306a36Sopenharmony_ci tctl_val |= V2_TCTL_24MEN; 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci tctl_val |= V2_TCTL_CLK_PER; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci writel_relaxed(tctl_val, imxtm->base + MXC_TCTL); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const struct imx_gpt_data imx1_gpt_data = { 33162306a36Sopenharmony_ci .reg_tstat = MX1_2_TSTAT, 33262306a36Sopenharmony_ci .reg_tcn = MX1_2_TCN, 33362306a36Sopenharmony_ci .reg_tcmp = MX1_2_TCMP, 33462306a36Sopenharmony_ci .gpt_irq_enable = imx1_gpt_irq_enable, 33562306a36Sopenharmony_ci .gpt_irq_disable = imx1_gpt_irq_disable, 33662306a36Sopenharmony_ci .gpt_irq_acknowledge = imx1_gpt_irq_acknowledge, 33762306a36Sopenharmony_ci .gpt_setup_tctl = imx1_gpt_setup_tctl, 33862306a36Sopenharmony_ci .set_next_event = mx1_2_set_next_event, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const struct imx_gpt_data imx21_gpt_data = { 34262306a36Sopenharmony_ci .reg_tstat = MX1_2_TSTAT, 34362306a36Sopenharmony_ci .reg_tcn = MX1_2_TCN, 34462306a36Sopenharmony_ci .reg_tcmp = MX1_2_TCMP, 34562306a36Sopenharmony_ci .gpt_irq_enable = imx1_gpt_irq_enable, 34662306a36Sopenharmony_ci .gpt_irq_disable = imx1_gpt_irq_disable, 34762306a36Sopenharmony_ci .gpt_irq_acknowledge = imx21_gpt_irq_acknowledge, 34862306a36Sopenharmony_ci .gpt_setup_tctl = imx1_gpt_setup_tctl, 34962306a36Sopenharmony_ci .set_next_event = mx1_2_set_next_event, 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const struct imx_gpt_data imx31_gpt_data = { 35362306a36Sopenharmony_ci .reg_tstat = V2_TSTAT, 35462306a36Sopenharmony_ci .reg_tcn = V2_TCN, 35562306a36Sopenharmony_ci .reg_tcmp = V2_TCMP, 35662306a36Sopenharmony_ci .gpt_irq_enable = imx31_gpt_irq_enable, 35762306a36Sopenharmony_ci .gpt_irq_disable = imx31_gpt_irq_disable, 35862306a36Sopenharmony_ci .gpt_irq_acknowledge = imx31_gpt_irq_acknowledge, 35962306a36Sopenharmony_ci .gpt_setup_tctl = imx31_gpt_setup_tctl, 36062306a36Sopenharmony_ci .set_next_event = v2_set_next_event, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic const struct imx_gpt_data imx6dl_gpt_data = { 36462306a36Sopenharmony_ci .reg_tstat = V2_TSTAT, 36562306a36Sopenharmony_ci .reg_tcn = V2_TCN, 36662306a36Sopenharmony_ci .reg_tcmp = V2_TCMP, 36762306a36Sopenharmony_ci .gpt_irq_enable = imx31_gpt_irq_enable, 36862306a36Sopenharmony_ci .gpt_irq_disable = imx31_gpt_irq_disable, 36962306a36Sopenharmony_ci .gpt_irq_acknowledge = imx31_gpt_irq_acknowledge, 37062306a36Sopenharmony_ci .gpt_setup_tctl = imx6dl_gpt_setup_tctl, 37162306a36Sopenharmony_ci .set_next_event = v2_set_next_event, 37262306a36Sopenharmony_ci}; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int __init _mxc_timer_init(struct imx_timer *imxtm) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci switch (imxtm->type) { 37962306a36Sopenharmony_ci case GPT_TYPE_IMX1: 38062306a36Sopenharmony_ci imxtm->gpt = &imx1_gpt_data; 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case GPT_TYPE_IMX21: 38362306a36Sopenharmony_ci imxtm->gpt = &imx21_gpt_data; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci case GPT_TYPE_IMX31: 38662306a36Sopenharmony_ci imxtm->gpt = &imx31_gpt_data; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci case GPT_TYPE_IMX6DL: 38962306a36Sopenharmony_ci imxtm->gpt = &imx6dl_gpt_data; 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (IS_ERR(imxtm->clk_per)) { 39662306a36Sopenharmony_ci pr_err("i.MX timer: unable to get clk\n"); 39762306a36Sopenharmony_ci return PTR_ERR(imxtm->clk_per); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (!IS_ERR(imxtm->clk_ipg)) 40162306a36Sopenharmony_ci clk_prepare_enable(imxtm->clk_ipg); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci clk_prepare_enable(imxtm->clk_per); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * Initialise to a known state (all timers off, and timing reset) 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci writel_relaxed(0, imxtm->base + MXC_TCTL); 41062306a36Sopenharmony_ci writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */ 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci imxtm->gpt->gpt_setup_tctl(imxtm); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* init and register the timer to the framework */ 41562306a36Sopenharmony_ci ret = mxc_clocksource_init(imxtm); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return mxc_clockevent_init(imxtm); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type type) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct imx_timer *imxtm; 42562306a36Sopenharmony_ci static int initialized; 42662306a36Sopenharmony_ci int ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Support one instance only */ 42962306a36Sopenharmony_ci if (initialized) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL); 43362306a36Sopenharmony_ci if (!imxtm) 43462306a36Sopenharmony_ci return -ENOMEM; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci imxtm->base = of_iomap(np, 0); 43762306a36Sopenharmony_ci if (!imxtm->base) { 43862306a36Sopenharmony_ci ret = -ENXIO; 43962306a36Sopenharmony_ci goto err_kfree; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci imxtm->irq = irq_of_parse_and_map(np, 0); 44362306a36Sopenharmony_ci if (imxtm->irq <= 0) { 44462306a36Sopenharmony_ci ret = -EINVAL; 44562306a36Sopenharmony_ci goto err_kfree; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci imxtm->clk_ipg = of_clk_get_by_name(np, "ipg"); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Try osc_per first, and fall back to per otherwise */ 45162306a36Sopenharmony_ci imxtm->clk_per = of_clk_get_by_name(np, "osc_per"); 45262306a36Sopenharmony_ci if (IS_ERR(imxtm->clk_per)) 45362306a36Sopenharmony_ci imxtm->clk_per = of_clk_get_by_name(np, "per"); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci imxtm->type = type; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = _mxc_timer_init(imxtm); 45862306a36Sopenharmony_ci if (ret) 45962306a36Sopenharmony_ci goto err_kfree; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci initialized = 1; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cierr_kfree: 46662306a36Sopenharmony_ci kfree(imxtm); 46762306a36Sopenharmony_ci return ret; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int __init imx1_timer_init_dt(struct device_node *np) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci return mxc_timer_init_dt(np, GPT_TYPE_IMX1); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int __init imx21_timer_init_dt(struct device_node *np) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci return mxc_timer_init_dt(np, GPT_TYPE_IMX21); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int __init imx31_timer_init_dt(struct device_node *np) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci enum imx_gpt_type type = GPT_TYPE_IMX31; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * We were using the same compatible string for i.MX6Q/D and i.MX6DL/S 48662306a36Sopenharmony_ci * GPT device, while they actually have different programming model. 48762306a36Sopenharmony_ci * This is a workaround to keep the existing i.MX6DL/S DTBs continue 48862306a36Sopenharmony_ci * working with the new kernel. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_ci if (of_machine_is_compatible("fsl,imx6dl")) 49162306a36Sopenharmony_ci type = GPT_TYPE_IMX6DL; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return mxc_timer_init_dt(np, type); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int __init imx6dl_timer_init_dt(struct device_node *np) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ciTIMER_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt); 50262306a36Sopenharmony_ciTIMER_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt); 50362306a36Sopenharmony_ciTIMER_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt); 50462306a36Sopenharmony_ciTIMER_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt); 50562306a36Sopenharmony_ciTIMER_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt); 50662306a36Sopenharmony_ciTIMER_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt); 50762306a36Sopenharmony_ciTIMER_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt); 50862306a36Sopenharmony_ciTIMER_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt); 50962306a36Sopenharmony_ciTIMER_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt); 51062306a36Sopenharmony_ciTIMER_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt); 51162306a36Sopenharmony_ciTIMER_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt); 51262306a36Sopenharmony_ciTIMER_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt); 513