1 From b92e223750a07b28f175eae97d5ce3978df41be8 Mon Sep 17 00:00:00 2001
2 From: Yangbo Lu <yangbo.lu@nxp.com>
3 Date: Wed, 17 Jan 2018 15:32:05 +0800
4 Subject: [PATCH 18/30] flextimer: support layerscape
6 This is an 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 | 367 +++++++++++++++++++++++++++++++++
14 2 files changed, 371 insertions(+), 4 deletions(-)
15 create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
17 --- a/drivers/clocksource/fsl_ftm_timer.c
18 +++ b/drivers/clocksource/fsl_ftm_timer.c
19 @@ -83,11 +83,11 @@ static inline void ftm_counter_disable(v
21 static inline void ftm_irq_acknowledge(void __iomem *base)
24 + unsigned int timeout = 100;
26 - val = ftm_readl(base + FTM_SC);
28 - ftm_writel(val, base + FTM_SC);
29 + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
30 + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
34 static inline void ftm_irq_enable(void __iomem *base)
36 +++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
39 + * Freescale FlexTimer Module (FTM) Alarm driver.
41 + * Copyright 2014 Freescale Semiconductor, Inc.
43 + * This program is free software; you can redistribute it and/or
44 + * modify it under the terms of the GNU General Public License
45 + * as published by the Free Software Foundation; either version 2
46 + * of the License, or (at your option) any later version.
49 +#include <linux/device.h>
50 +#include <linux/err.h>
51 +#include <linux/interrupt.h>
52 +#include <linux/io.h>
53 +#include <linux/of_address.h>
54 +#include <linux/of_irq.h>
55 +#include <linux/platform_device.h>
56 +#include <linux/of.h>
57 +#include <linux/of_device.h>
58 +#include <linux/libata.h>
59 +#include <linux/module.h>
62 +#define FTM_SC_CLK_SHIFT 3
63 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
64 +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
65 +#define FTM_SC_PS_MASK 0x7
66 +#define FTM_SC_TOIE BIT(6)
67 +#define FTM_SC_TOF BIT(7)
69 +#define FTM_SC_CLKS_FIXED_FREQ 0x02
73 +#define FTM_CNTIN 0x4C
75 +#define FIXED_FREQ_CLK 32000
76 +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
77 +#define MAX_COUNT_VAL 0xffff
79 +static void __iomem *ftm1_base;
80 +static void __iomem *rcpm_ftm_addr;
81 +static u32 alarm_freq;
82 +static bool big_endian;
84 +enum pmu_endian_type {
90 + enum pmu_endian_type big_endian; /* Big/Little endian of PMU module */
91 + u32 flextimer_set_bit; /* FlexTimer1 is not powerdown during device LPM20 */
94 +static struct rcpm_cfg ls1012a_rcpm_cfg = {
95 + .big_endian = BIG_ENDIAN,
96 + .flextimer_set_bit = 0x20000,
99 +static struct rcpm_cfg ls1021a_rcpm_cfg = {
100 + .big_endian = BIG_ENDIAN,
101 + .flextimer_set_bit = 0x20000,
104 +static struct rcpm_cfg ls1043a_rcpm_cfg = {
105 + .big_endian = BIG_ENDIAN,
106 + .flextimer_set_bit = 0x20000,
109 +static struct rcpm_cfg ls1046a_rcpm_cfg = {
110 + .big_endian = BIG_ENDIAN,
111 + .flextimer_set_bit = 0x20000,
114 +static struct rcpm_cfg ls1088a_rcpm_cfg = {
115 + .big_endian = LITTLE_ENDIAN,
116 + .flextimer_set_bit = 0x4000,
119 +static struct rcpm_cfg ls208xa_rcpm_cfg = {
120 + .big_endian = LITTLE_ENDIAN,
121 + .flextimer_set_bit = 0x4000,
124 +static const struct of_device_id ippdexpcr_of_match[] = {
125 + { .compatible = "fsl,ls1012a-ftm", .data = &ls1012a_rcpm_cfg},
126 + { .compatible = "fsl,ls1021a-ftm", .data = &ls1021a_rcpm_cfg},
127 + { .compatible = "fsl,ls1043a-ftm", .data = &ls1043a_rcpm_cfg},
128 + { .compatible = "fsl,ls1046a-ftm", .data = &ls1046a_rcpm_cfg},
129 + { .compatible = "fsl,ls1088a-ftm", .data = &ls1088a_rcpm_cfg},
130 + { .compatible = "fsl,ls208xa-ftm", .data = &ls208xa_rcpm_cfg},
133 +MODULE_DEVICE_TABLE(of, ippdexpcr_of_match);
135 +static inline u32 ftm_readl(void __iomem *addr)
138 + return ioread32be(addr);
140 + return ioread32(addr);
143 +static inline void ftm_writel(u32 val, void __iomem *addr)
146 + iowrite32be(val, addr);
148 + iowrite32(val, addr);
151 +static inline void ftm_counter_enable(void __iomem *base)
155 + /* select and enable counter clock source */
156 + val = ftm_readl(base + FTM_SC);
157 + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
158 + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
159 + ftm_writel(val, base + FTM_SC);
162 +static inline void ftm_counter_disable(void __iomem *base)
166 + /* disable counter clock source */
167 + val = ftm_readl(base + FTM_SC);
168 + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
169 + ftm_writel(val, base + FTM_SC);
172 +static inline void ftm_irq_acknowledge(void __iomem *base)
174 + unsigned int timeout = 100;
176 + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
177 + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
181 +static inline void ftm_irq_enable(void __iomem *base)
185 + val = ftm_readl(base + FTM_SC);
186 + val |= FTM_SC_TOIE;
187 + ftm_writel(val, base + FTM_SC);
190 +static inline void ftm_irq_disable(void __iomem *base)
194 + val = ftm_readl(base + FTM_SC);
195 + val &= ~FTM_SC_TOIE;
196 + ftm_writel(val, base + FTM_SC);
199 +static inline void ftm_reset_counter(void __iomem *base)
202 + * The CNT register contains the FTM counter value.
203 + * Reset clears the CNT register. Writing any value to COUNT
204 + * updates the counter with its initial value, CNTIN.
206 + ftm_writel(0x00, base + FTM_CNT);
209 +static u32 time_to_cycle(unsigned long time)
213 + cycle = time * alarm_freq;
214 + if (cycle > MAX_COUNT_VAL) {
215 + pr_err("Out of alarm range.\n");
222 +static u32 cycle_to_time(u32 cycle)
224 + return cycle / alarm_freq + 1;
227 +static void ftm_clean_alarm(void)
229 + ftm_counter_disable(ftm1_base);
231 + ftm_writel(0x00, ftm1_base + FTM_CNTIN);
232 + ftm_writel(~0U, ftm1_base + FTM_MOD);
234 + ftm_reset_counter(ftm1_base);
237 +static int ftm_set_alarm(u64 cycle)
239 + ftm_irq_disable(ftm1_base);
242 + * The counter increments until the value of MOD is reached,
243 + * at which point the counter is reloaded with the value of CNTIN.
244 + * The TOF (the overflow flag) bit is set when the FTM counter
245 + * changes from MOD to CNTIN. So we should using the cycle - 1.
247 + ftm_writel(cycle - 1, ftm1_base + FTM_MOD);
249 + ftm_counter_enable(ftm1_base);
251 + ftm_irq_enable(ftm1_base);
256 +static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id)
258 + ftm_irq_acknowledge(ftm1_base);
259 + ftm_irq_disable(ftm1_base);
262 + return IRQ_HANDLED;
265 +static ssize_t ftm_alarm_show(struct device *dev,
266 + struct device_attribute *attr,
271 + count = ftm_readl(ftm1_base + FTM_MOD);
272 + val = ftm_readl(ftm1_base + FTM_CNT);
273 + val = (count & MAX_COUNT_VAL) - val;
274 + val = cycle_to_time(val);
276 + return sprintf(buf, "%u\n", val);
279 +static ssize_t ftm_alarm_store(struct device *dev,
280 + struct device_attribute *attr,
281 + const char *buf, size_t count)
284 + unsigned long time;
286 + if (kstrtoul(buf, 0, &time))
291 + cycle = time_to_cycle(time);
295 + ftm_set_alarm(cycle);
300 +static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644,
301 + ftm_alarm_show, ftm_alarm_store);
303 +static int ftm_alarm_probe(struct platform_device *pdev)
305 + struct device_node *np = pdev->dev.of_node;
306 + struct resource *r;
309 + struct rcpm_cfg *rcpm_cfg;
310 + u32 ippdexpcr, flextimer;
311 + const struct of_device_id *of_id;
312 + enum pmu_endian_type endian;
314 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
318 + ftm1_base = devm_ioremap_resource(&pdev->dev, r);
319 + if (IS_ERR(ftm1_base))
320 + return PTR_ERR(ftm1_base);
322 + of_id = of_match_node(ippdexpcr_of_match, np);
326 + rcpm_cfg = devm_kzalloc(&pdev->dev, sizeof(*rcpm_cfg), GFP_KERNEL);
330 + rcpm_cfg = (struct rcpm_cfg*)of_id->data;
331 + endian = rcpm_cfg->big_endian;
332 + flextimer = rcpm_cfg->flextimer_set_bit;
334 + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "FlexTimer1");
336 + rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
337 + if (IS_ERR(rcpm_ftm_addr))
338 + return PTR_ERR(rcpm_ftm_addr);
339 + if (endian == BIG_ENDIAN)
340 + ippdexpcr = ioread32be(rcpm_ftm_addr);
342 + ippdexpcr = ioread32(rcpm_ftm_addr);
343 + ippdexpcr |= flextimer;
344 + if (endian == BIG_ENDIAN)
345 + iowrite32be(ippdexpcr, rcpm_ftm_addr);
347 + iowrite32(ippdexpcr, rcpm_ftm_addr);
350 + irq = irq_of_parse_and_map(np, 0);
352 + pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
356 + big_endian = of_property_read_bool(np, "big-endian");
358 + ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt,
359 + IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL);
361 + dev_err(&pdev->dev, "failed to request irq\n");
365 + ret = device_create_file(&pdev->dev, &ftm_alarm_attributes);
367 + dev_err(&pdev->dev, "create sysfs fail.\n");
371 + alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
375 + device_init_wakeup(&pdev->dev, true);
380 +static const struct of_device_id ftm_alarm_match[] = {
381 + { .compatible = "fsl,ls1012a-ftm", },
382 + { .compatible = "fsl,ls1021a-ftm", },
383 + { .compatible = "fsl,ls1043a-ftm", },
384 + { .compatible = "fsl,ls1046a-ftm", },
385 + { .compatible = "fsl,ls1088a-ftm", },
386 + { .compatible = "fsl,ls208xa-ftm", },
387 + { .compatible = "fsl,ftm-timer", },
391 +static struct platform_driver ftm_alarm_driver = {
392 + .probe = ftm_alarm_probe,
394 + .name = "ftm-alarm",
395 + .owner = THIS_MODULE,
396 + .of_match_table = ftm_alarm_match,
400 +static int __init ftm_alarm_init(void)
402 + return platform_driver_register(&ftm_alarm_driver);
404 +device_initcall(ftm_alarm_init);