timer-fsl-ftm.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Freescale FlexTimer Module (FTM) timer driver.
  4. *
  5. * Copyright 2014 Freescale Semiconductor, Inc.
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/clockchips.h>
  9. #include <linux/clocksource.h>
  10. #include <linux/err.h>
  11. #include <linux/interrupt.h>
  12. #include <linux/io.h>
  13. #include <linux/of_address.h>
  14. #include <linux/of_irq.h>
  15. #include <linux/sched_clock.h>
  16. #include <linux/slab.h>
  17. #include <linux/fsl/ftm.h>
  18. #define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT)
  19. struct ftm_clock_device {
  20. void __iomem *clksrc_base;
  21. void __iomem *clkevt_base;
  22. unsigned long periodic_cyc;
  23. unsigned long ps;
  24. bool big_endian;
  25. };
  26. static struct ftm_clock_device *priv;
  27. static inline u32 ftm_readl(void __iomem *addr)
  28. {
  29. if (priv->big_endian)
  30. return ioread32be(addr);
  31. else
  32. return ioread32(addr);
  33. }
  34. static inline void ftm_writel(u32 val, void __iomem *addr)
  35. {
  36. if (priv->big_endian)
  37. iowrite32be(val, addr);
  38. else
  39. iowrite32(val, addr);
  40. }
  41. static inline void ftm_counter_enable(void __iomem *base)
  42. {
  43. u32 val;
  44. /* select and enable counter clock source */
  45. val = ftm_readl(base + FTM_SC);
  46. val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  47. val |= priv->ps | FTM_SC_CLK(1);
  48. ftm_writel(val, base + FTM_SC);
  49. }
  50. static inline void ftm_counter_disable(void __iomem *base)
  51. {
  52. u32 val;
  53. /* disable counter clock source */
  54. val = ftm_readl(base + FTM_SC);
  55. val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  56. ftm_writel(val, base + FTM_SC);
  57. }
  58. static inline void ftm_irq_acknowledge(void __iomem *base)
  59. {
  60. u32 val;
  61. val = ftm_readl(base + FTM_SC);
  62. val &= ~FTM_SC_TOF;
  63. ftm_writel(val, base + FTM_SC);
  64. }
  65. static inline void ftm_irq_enable(void __iomem *base)
  66. {
  67. u32 val;
  68. val = ftm_readl(base + FTM_SC);
  69. val |= FTM_SC_TOIE;
  70. ftm_writel(val, base + FTM_SC);
  71. }
  72. static inline void ftm_irq_disable(void __iomem *base)
  73. {
  74. u32 val;
  75. val = ftm_readl(base + FTM_SC);
  76. val &= ~FTM_SC_TOIE;
  77. ftm_writel(val, base + FTM_SC);
  78. }
  79. static inline void ftm_reset_counter(void __iomem *base)
  80. {
  81. /*
  82. * The CNT register contains the FTM counter value.
  83. * Reset clears the CNT register. Writing any value to COUNT
  84. * updates the counter with its initial value, CNTIN.
  85. */
  86. ftm_writel(0x00, base + FTM_CNT);
  87. }
  88. static u64 notrace ftm_read_sched_clock(void)
  89. {
  90. return ftm_readl(priv->clksrc_base + FTM_CNT);
  91. }
  92. static int ftm_set_next_event(unsigned long delta,
  93. struct clock_event_device *unused)
  94. {
  95. /*
  96. * The CNNIN and MOD are all double buffer registers, writing
  97. * to the MOD register latches the value into a buffer. The MOD
  98. * register is updated with the value of its write buffer with
  99. * the following scenario:
  100. * a, the counter source clock is disabled.
  101. */
  102. ftm_counter_disable(priv->clkevt_base);
  103. /* Force the value of CNTIN to be loaded into the FTM counter */
  104. ftm_reset_counter(priv->clkevt_base);
  105. /*
  106. * The counter increments until the value of MOD is reached,
  107. * at which point the counter is reloaded with the value of CNTIN.
  108. * The TOF (the overflow flag) bit is set when the FTM counter
  109. * changes from MOD to CNTIN. So we should using the delta - 1.
  110. */
  111. ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
  112. ftm_counter_enable(priv->clkevt_base);
  113. ftm_irq_enable(priv->clkevt_base);
  114. return 0;
  115. }
  116. static int ftm_set_oneshot(struct clock_event_device *evt)
  117. {
  118. ftm_counter_disable(priv->clkevt_base);
  119. return 0;
  120. }
  121. static int ftm_set_periodic(struct clock_event_device *evt)
  122. {
  123. ftm_set_next_event(priv->periodic_cyc, evt);
  124. return 0;
  125. }
  126. static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
  127. {
  128. struct clock_event_device *evt = dev_id;
  129. ftm_irq_acknowledge(priv->clkevt_base);
  130. if (likely(clockevent_state_oneshot(evt))) {
  131. ftm_irq_disable(priv->clkevt_base);
  132. ftm_counter_disable(priv->clkevt_base);
  133. }
  134. evt->event_handler(evt);
  135. return IRQ_HANDLED;
  136. }
  137. static struct clock_event_device ftm_clockevent = {
  138. .name = "Freescale ftm timer",
  139. .features = CLOCK_EVT_FEAT_PERIODIC |
  140. CLOCK_EVT_FEAT_ONESHOT,
  141. .set_state_periodic = ftm_set_periodic,
  142. .set_state_oneshot = ftm_set_oneshot,
  143. .set_next_event = ftm_set_next_event,
  144. .rating = 300,
  145. };
  146. static int __init ftm_clockevent_init(unsigned long freq, int irq)
  147. {
  148. int err;
  149. ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
  150. ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
  151. ftm_reset_counter(priv->clkevt_base);
  152. err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
  153. "Freescale ftm timer", &ftm_clockevent);
  154. if (err) {
  155. pr_err("ftm: setup irq failed: %d\n", err);
  156. return err;
  157. }
  158. ftm_clockevent.cpumask = cpumask_of(0);
  159. ftm_clockevent.irq = irq;
  160. clockevents_config_and_register(&ftm_clockevent,
  161. freq / (1 << priv->ps),
  162. 1, 0xffff);
  163. ftm_counter_enable(priv->clkevt_base);
  164. return 0;
  165. }
  166. static int __init ftm_clocksource_init(unsigned long freq)
  167. {
  168. int err;
  169. ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
  170. ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
  171. ftm_reset_counter(priv->clksrc_base);
  172. sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
  173. err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
  174. freq / (1 << priv->ps), 300, 16,
  175. clocksource_mmio_readl_up);
  176. if (err) {
  177. pr_err("ftm: init clock source mmio failed: %d\n", err);
  178. return err;
  179. }
  180. ftm_counter_enable(priv->clksrc_base);
  181. return 0;
  182. }
  183. static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
  184. char *ftm_name)
  185. {
  186. struct clk *clk;
  187. int err;
  188. clk = of_clk_get_by_name(np, cnt_name);
  189. if (IS_ERR(clk)) {
  190. pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
  191. return PTR_ERR(clk);
  192. }
  193. err = clk_prepare_enable(clk);
  194. if (err) {
  195. pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
  196. cnt_name, err);
  197. return err;
  198. }
  199. clk = of_clk_get_by_name(np, ftm_name);
  200. if (IS_ERR(clk)) {
  201. pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
  202. return PTR_ERR(clk);
  203. }
  204. err = clk_prepare_enable(clk);
  205. if (err)
  206. pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
  207. ftm_name, err);
  208. return clk_get_rate(clk);
  209. }
  210. static unsigned long __init ftm_clk_init(struct device_node *np)
  211. {
  212. long freq;
  213. freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
  214. if (freq <= 0)
  215. return 0;
  216. freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
  217. if (freq <= 0)
  218. return 0;
  219. return freq;
  220. }
  221. static int __init ftm_calc_closest_round_cyc(unsigned long freq)
  222. {
  223. priv->ps = 0;
  224. /* The counter register is only using the lower 16 bits, and
  225. * if the 'freq' value is to big here, then the periodic_cyc
  226. * may exceed 0xFFFF.
  227. */
  228. do {
  229. priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
  230. HZ * (1 << priv->ps++));
  231. } while (priv->periodic_cyc > 0xFFFF);
  232. if (priv->ps > FTM_PS_MAX) {
  233. pr_err("ftm: the prescaler is %lu > %d\n",
  234. priv->ps, FTM_PS_MAX);
  235. return -EINVAL;
  236. }
  237. return 0;
  238. }
  239. static int __init ftm_timer_init(struct device_node *np)
  240. {
  241. unsigned long freq;
  242. int ret, irq;
  243. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  244. if (!priv)
  245. return -ENOMEM;
  246. ret = -ENXIO;
  247. priv->clkevt_base = of_iomap(np, 0);
  248. if (!priv->clkevt_base) {
  249. pr_err("ftm: unable to map event timer registers\n");
  250. goto err_clkevt;
  251. }
  252. priv->clksrc_base = of_iomap(np, 1);
  253. if (!priv->clksrc_base) {
  254. pr_err("ftm: unable to map source timer registers\n");
  255. goto err_clksrc;
  256. }
  257. ret = -EINVAL;
  258. irq = irq_of_parse_and_map(np, 0);
  259. if (irq <= 0) {
  260. pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
  261. goto err;
  262. }
  263. priv->big_endian = of_property_read_bool(np, "big-endian");
  264. freq = ftm_clk_init(np);
  265. if (!freq)
  266. goto err;
  267. ret = ftm_calc_closest_round_cyc(freq);
  268. if (ret)
  269. goto err;
  270. ret = ftm_clocksource_init(freq);
  271. if (ret)
  272. goto err;
  273. ret = ftm_clockevent_init(freq, irq);
  274. if (ret)
  275. goto err;
  276. return 0;
  277. err:
  278. iounmap(priv->clksrc_base);
  279. err_clksrc:
  280. iounmap(priv->clkevt_base);
  281. err_clkevt:
  282. kfree(priv);
  283. return ret;
  284. }
  285. TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);