1 From a5b3155b532289af793c26251cb087b4a24d5c15 Mon Sep 17 00:00:00 2001
2 From: Yangbo Lu <yangbo.lu@nxp.com>
3 Date: Mon, 25 Sep 2017 12:13:12 +0800
4 Subject: [PATCH] flextimer: support layerscape
6 This is a integrated patch for layerscape flextimer support.
8 Signed-off-by: Wang Dongsheng <dongsheng.wang@nxp.com>
9 Signed-off-by: Meng Yi <meng.yi@nxp.com>
10 Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
12 drivers/clocksource/fsl_ftm_timer.c | 8 +-
13 drivers/soc/fsl/layerscape/ftm_alarm.c | 286 +++++++++++++++++++++++++++++++++
14 2 files changed, 290 insertions(+), 4 deletions(-)
15 create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
17 diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
18 index 738515b8..770bbbca 100644
19 --- a/drivers/clocksource/fsl_ftm_timer.c
20 +++ b/drivers/clocksource/fsl_ftm_timer.c
21 @@ -83,11 +83,11 @@ static inline void ftm_counter_disable(void __iomem *base)
23 static inline void ftm_irq_acknowledge(void __iomem *base)
26 + unsigned int timeout = 100;
28 - val = ftm_readl(base + FTM_SC);
30 - ftm_writel(val, base + FTM_SC);
31 + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
32 + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
36 static inline void ftm_irq_enable(void __iomem *base)
37 diff --git a/drivers/soc/fsl/layerscape/ftm_alarm.c b/drivers/soc/fsl/layerscape/ftm_alarm.c
39 index 00000000..6f9882ff
41 +++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
44 + * Freescale FlexTimer Module (FTM) Alarm driver.
46 + * Copyright 2014 Freescale Semiconductor, Inc.
48 + * This program is free software; you can redistribute it and/or
49 + * modify it under the terms of the GNU General Public License
50 + * as published by the Free Software Foundation; either version 2
51 + * of the License, or (at your option) any later version.
54 +#include <linux/device.h>
55 +#include <linux/err.h>
56 +#include <linux/interrupt.h>
57 +#include <linux/io.h>
58 +#include <linux/of_address.h>
59 +#include <linux/of_irq.h>
60 +#include <linux/platform_device.h>
63 +#define FTM_SC_CLK_SHIFT 3
64 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
65 +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
66 +#define FTM_SC_PS_MASK 0x7
67 +#define FTM_SC_TOIE BIT(6)
68 +#define FTM_SC_TOF BIT(7)
70 +#define FTM_SC_CLKS_FIXED_FREQ 0x02
74 +#define FTM_CNTIN 0x4C
76 +#define FIXED_FREQ_CLK 32000
77 +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
78 +#define MAX_COUNT_VAL 0xffff
80 +static void __iomem *ftm1_base;
81 +static void __iomem *rcpm_ftm_addr;
82 +static u32 alarm_freq;
83 +static bool big_endian;
85 +static inline u32 ftm_readl(void __iomem *addr)
88 + return ioread32be(addr);
90 + return ioread32(addr);
93 +static inline void ftm_writel(u32 val, void __iomem *addr)
96 + iowrite32be(val, addr);
98 + iowrite32(val, addr);
101 +static inline void ftm_counter_enable(void __iomem *base)
105 + /* select and enable counter clock source */
106 + val = ftm_readl(base + FTM_SC);
107 + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
108 + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
109 + ftm_writel(val, base + FTM_SC);
112 +static inline void ftm_counter_disable(void __iomem *base)
116 + /* disable counter clock source */
117 + val = ftm_readl(base + FTM_SC);
118 + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
119 + ftm_writel(val, base + FTM_SC);
122 +static inline void ftm_irq_acknowledge(void __iomem *base)
124 + unsigned int timeout = 100;
126 + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
127 + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
131 +static inline void ftm_irq_enable(void __iomem *base)
135 + val = ftm_readl(base + FTM_SC);
136 + val |= FTM_SC_TOIE;
137 + ftm_writel(val, base + FTM_SC);
140 +static inline void ftm_irq_disable(void __iomem *base)
144 + val = ftm_readl(base + FTM_SC);
145 + val &= ~FTM_SC_TOIE;
146 + ftm_writel(val, base + FTM_SC);
149 +static inline void ftm_reset_counter(void __iomem *base)
152 + * The CNT register contains the FTM counter value.
153 + * Reset clears the CNT register. Writing any value to COUNT
154 + * updates the counter with its initial value, CNTIN.
156 + ftm_writel(0x00, base + FTM_CNT);
159 +static u32 time_to_cycle(unsigned long time)
163 + cycle = time * alarm_freq;
164 + if (cycle > MAX_COUNT_VAL) {
165 + pr_err("Out of alarm range.\n");
172 +static u32 cycle_to_time(u32 cycle)
174 + return cycle / alarm_freq + 1;
177 +static void ftm_clean_alarm(void)
179 + ftm_counter_disable(ftm1_base);
181 + ftm_writel(0x00, ftm1_base + FTM_CNTIN);
182 + ftm_writel(~0U, ftm1_base + FTM_MOD);
184 + ftm_reset_counter(ftm1_base);
187 +static int ftm_set_alarm(u64 cycle)
189 + ftm_irq_disable(ftm1_base);
192 + * The counter increments until the value of MOD is reached,
193 + * at which point the counter is reloaded with the value of CNTIN.
194 + * The TOF (the overflow flag) bit is set when the FTM counter
195 + * changes from MOD to CNTIN. So we should using the cycle - 1.
197 + ftm_writel(cycle - 1, ftm1_base + FTM_MOD);
199 + ftm_counter_enable(ftm1_base);
201 + ftm_irq_enable(ftm1_base);
206 +static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id)
208 + ftm_irq_acknowledge(ftm1_base);
209 + ftm_irq_disable(ftm1_base);
212 + return IRQ_HANDLED;
215 +static ssize_t ftm_alarm_show(struct device *dev,
216 + struct device_attribute *attr,
221 + count = ftm_readl(ftm1_base + FTM_MOD);
222 + val = ftm_readl(ftm1_base + FTM_CNT);
223 + val = (count & MAX_COUNT_VAL) - val;
224 + val = cycle_to_time(val);
226 + return sprintf(buf, "%u\n", val);
229 +static ssize_t ftm_alarm_store(struct device *dev,
230 + struct device_attribute *attr,
231 + const char *buf, size_t count)
234 + unsigned long time;
236 + if (kstrtoul(buf, 0, &time))
241 + cycle = time_to_cycle(time);
245 + ftm_set_alarm(cycle);
250 +static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644,
251 + ftm_alarm_show, ftm_alarm_store);
253 +static int ftm_alarm_probe(struct platform_device *pdev)
255 + struct device_node *np = pdev->dev.of_node;
256 + struct resource *r;
261 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
265 + ftm1_base = devm_ioremap_resource(&pdev->dev, r);
266 + if (IS_ERR(ftm1_base))
267 + return PTR_ERR(ftm1_base);
269 + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "FlexTimer1");
271 + rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
272 + if (IS_ERR(rcpm_ftm_addr))
273 + return PTR_ERR(rcpm_ftm_addr);
274 + ippdexpcr = ioread32be(rcpm_ftm_addr);
275 + ippdexpcr |= 0x20000;
276 + iowrite32be(ippdexpcr, rcpm_ftm_addr);
279 + irq = irq_of_parse_and_map(np, 0);
281 + pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
285 + big_endian = of_property_read_bool(np, "big-endian");
287 + ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt,
288 + IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL);
290 + dev_err(&pdev->dev, "failed to request irq\n");
294 + ret = device_create_file(&pdev->dev, &ftm_alarm_attributes);
296 + dev_err(&pdev->dev, "create sysfs fail.\n");
300 + alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
304 + device_init_wakeup(&pdev->dev, true);
309 +static const struct of_device_id ftm_alarm_match[] = {
310 + { .compatible = "fsl,ftm-alarm", },
311 + { .compatible = "fsl,ftm-timer", },
315 +static struct platform_driver ftm_alarm_driver = {
316 + .probe = ftm_alarm_probe,
318 + .name = "ftm-alarm",
319 + .owner = THIS_MODULE,
320 + .of_match_table = ftm_alarm_match,
324 +static int __init ftm_alarm_init(void)
326 + return platform_driver_register(&ftm_alarm_driver);
328 +device_initcall(ftm_alarm_init);