1 From 0f31298eb0a9b2cd7990b709ff18229fadfa474b Mon Sep 17 00:00:00 2001
2 From: Biwen Li <biwen.li@nxp.com>
3 Date: Wed, 17 Apr 2019 18:58:38 +0800
4 Subject: [PATCH] flextimer: support layerscape
6 This is an integrated patch of flextimer for layerscape
8 Signed-off-by: Biwen Li <biwen.li@nxp.com>
9 Signed-off-by: Meng Yi <meng.yi@nxp.com>
10 Signed-off-by: Ran Wang <ran.wang_1@nxp.com>
11 Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
13 .../bindings/soc/fsl/layerscape/ftm-alarm.txt | 32 ++
14 drivers/clocksource/fsl_ftm_timer.c | 8 +-
15 drivers/soc/fsl/layerscape/ftm_alarm.c | 382 ++++++++++++++++++
16 3 files changed, 418 insertions(+), 4 deletions(-)
17 create mode 100644 Documentation/devicetree/bindings/soc/fsl/layerscape/ftm-alarm.txt
18 create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
21 +++ b/Documentation/devicetree/bindings/soc/fsl/layerscape/ftm-alarm.txt
23 +Freescale FlexTimer Module (FTM) Alarm
27 +- compatible : Should be "fsl,ftm-alarm" or "fsl,<chip>-ftm-alarm", the
28 + supported chips include
29 + "fsl,ls1012a-ftm-alarm"
30 + "fsl,ls1021a-ftm-alarm"
31 + "fsl,ls1043a-ftm-alarm"
32 + "fsl,ls1046a-ftm-alarm"
33 + "fsl,ls1088a-ftm-alarm"
34 + "fsl,ls208xa-ftm-alarm"
35 +- reg : Specifies base physical address and size of the register sets for the
36 + FlexTimer Module and base physical address of IP Powerdown Exception Control
38 +- reg-names: names of the mapped memory regions listed in regs property.
39 + should include the following entries:
40 + "ftm": Address of the register sets for FlexTimer Module
41 + "pmctrl": Address of IP Powerdown Exception Control register
42 +- interrupts : Should be the FlexTimer Module interrupt.
43 +- big-endian: If the host controller is big-endian mode, specify this property.
44 + The default endian mode is little-endian.
48 + compatible = "fsl,ls1043a-ftm-alarm";
49 + reg = <0x0 0x29d0000 0x0 0x10000>,
50 + <0x0 0x1ee2140 0x0 0x4>;
51 + reg-names = "ftm", "pmctrl";
52 + interrupts = <0 86 0x4>;
55 --- a/drivers/clocksource/fsl_ftm_timer.c
56 +++ b/drivers/clocksource/fsl_ftm_timer.c
57 @@ -83,11 +83,11 @@ static inline void ftm_counter_disable(v
59 static inline void ftm_irq_acknowledge(void __iomem *base)
62 + unsigned int timeout = 100;
64 - val = ftm_readl(base + FTM_SC);
66 - ftm_writel(val, base + FTM_SC);
67 + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
68 + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
72 static inline void ftm_irq_enable(void __iomem *base)
74 +++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
77 + * Freescale FlexTimer Module (FTM) Alarm driver.
79 + * Copyright 2014 Freescale Semiconductor, Inc.
81 + * This program is free software; you can redistribute it and/or
82 + * modify it under the terms of the GNU General Public License
83 + * as published by the Free Software Foundation; either version 2
84 + * of the License, or (at your option) any later version.
87 +#include <linux/device.h>
88 +#include <linux/err.h>
89 +#include <linux/interrupt.h>
90 +#include <linux/io.h>
91 +#include <linux/of_address.h>
92 +#include <linux/of_irq.h>
93 +#include <linux/platform_device.h>
94 +#include <linux/of.h>
95 +#include <linux/of_device.h>
96 +#include <linux/libata.h>
97 +#include <linux/module.h>
100 +#define FTM_SC_CLK_SHIFT 3
101 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
102 +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
103 +#define FTM_SC_PS_MASK 0x7
104 +#define FTM_SC_TOIE BIT(6)
105 +#define FTM_SC_TOF BIT(7)
107 +#define FTM_SC_CLKS_FIXED_FREQ 0x02
109 +#define FTM_CNT 0x04
110 +#define FTM_MOD 0x08
111 +#define FTM_CNTIN 0x4C
113 +#define FIXED_FREQ_CLK 32000
114 +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
115 +#define MAX_COUNT_VAL 0xffff
117 +static void __iomem *ftm1_base;
118 +static void __iomem *rcpm_ftm_addr;
119 +static void __iomem *scfg_scrachpad_addr;
120 +static u32 alarm_freq;
121 +static bool big_endian;
123 +enum pmu_endian_type {
129 + enum pmu_endian_type big_endian; /* Big/Little endian of PMU module */
130 + u32 flextimer_set_bit; /* FTM is not powerdown during device LPM20 */
133 +static struct rcpm_cfg ls1012a_rcpm_cfg = {
134 + .big_endian = BIG_ENDIAN,
135 + .flextimer_set_bit = 0x20000,
138 +static struct rcpm_cfg ls1021a_rcpm_cfg = {
139 + .big_endian = BIG_ENDIAN,
140 + .flextimer_set_bit = 0x30000000,
143 +static struct rcpm_cfg ls1043a_rcpm_cfg = {
144 + .big_endian = BIG_ENDIAN,
145 + .flextimer_set_bit = 0x20000,
148 +static struct rcpm_cfg ls1046a_rcpm_cfg = {
149 + .big_endian = BIG_ENDIAN,
150 + .flextimer_set_bit = 0x20000,
153 +static struct rcpm_cfg ls1088a_rcpm_cfg = {
154 + .big_endian = LITTLE_ENDIAN,
155 + .flextimer_set_bit = 0x4000,
158 +static struct rcpm_cfg ls208xa_rcpm_cfg = {
159 + .big_endian = LITTLE_ENDIAN,
160 + .flextimer_set_bit = 0x4000,
163 +static struct rcpm_cfg lx2160a_rcpm_cfg = {
164 + .big_endian = LITTLE_ENDIAN,
165 + .flextimer_set_bit = 0x4000,
168 +static const struct of_device_id ippdexpcr_of_match[] = {
169 + { .compatible = "fsl,ls1012a-ftm-alarm", .data = &ls1012a_rcpm_cfg},
170 + { .compatible = "fsl,ls1021a-ftm-alarm", .data = &ls1021a_rcpm_cfg},
171 + { .compatible = "fsl,ls1043a-ftm-alarm", .data = &ls1043a_rcpm_cfg},
172 + { .compatible = "fsl,ls1046a-ftm-alarm", .data = &ls1046a_rcpm_cfg},
173 + { .compatible = "fsl,ls1088a-ftm-alarm", .data = &ls1088a_rcpm_cfg},
174 + { .compatible = "fsl,ls208xa-ftm-alarm", .data = &ls208xa_rcpm_cfg},
175 + { .compatible = "fsl,lx2160a-ftm-alarm", .data = &lx2160a_rcpm_cfg},
178 +MODULE_DEVICE_TABLE(of, ippdexpcr_of_match);
180 +static inline u32 ftm_readl(void __iomem *addr)
183 + return ioread32be(addr);
185 + return ioread32(addr);
188 +static inline void ftm_writel(u32 val, void __iomem *addr)
191 + iowrite32be(val, addr);
193 + iowrite32(val, addr);
196 +static inline void ftm_counter_enable(void __iomem *base)
200 + /* select and enable counter clock source */
201 + val = ftm_readl(base + FTM_SC);
202 + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
203 + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
204 + ftm_writel(val, base + FTM_SC);
207 +static inline void ftm_counter_disable(void __iomem *base)
211 + /* disable counter clock source */
212 + val = ftm_readl(base + FTM_SC);
213 + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
214 + ftm_writel(val, base + FTM_SC);
217 +static inline void ftm_irq_acknowledge(void __iomem *base)
219 + unsigned int timeout = 100;
221 + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
222 + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
226 +static inline void ftm_irq_enable(void __iomem *base)
230 + val = ftm_readl(base + FTM_SC);
231 + val |= FTM_SC_TOIE;
232 + ftm_writel(val, base + FTM_SC);
235 +static inline void ftm_irq_disable(void __iomem *base)
239 + val = ftm_readl(base + FTM_SC);
240 + val &= ~FTM_SC_TOIE;
241 + ftm_writel(val, base + FTM_SC);
244 +static inline void ftm_reset_counter(void __iomem *base)
247 + * The CNT register contains the FTM counter value.
248 + * Reset clears the CNT register. Writing any value to COUNT
249 + * updates the counter with its initial value, CNTIN.
251 + ftm_writel(0x00, base + FTM_CNT);
254 +static u32 time_to_cycle(unsigned long time)
258 + cycle = time * alarm_freq;
259 + if (cycle > MAX_COUNT_VAL) {
260 + pr_err("Out of alarm range.\n");
267 +static u32 cycle_to_time(u32 cycle)
269 + return cycle / alarm_freq + 1;
272 +static void ftm_clean_alarm(void)
274 + ftm_counter_disable(ftm1_base);
276 + ftm_writel(0x00, ftm1_base + FTM_CNTIN);
277 + ftm_writel(~0U, ftm1_base + FTM_MOD);
279 + ftm_reset_counter(ftm1_base);
282 +static int ftm_set_alarm(u64 cycle)
284 + ftm_irq_disable(ftm1_base);
287 + * The counter increments until the value of MOD is reached,
288 + * at which point the counter is reloaded with the value of CNTIN.
289 + * The TOF (the overflow flag) bit is set when the FTM counter
290 + * changes from MOD to CNTIN. So we should using the cycle - 1.
292 + ftm_writel(cycle - 1, ftm1_base + FTM_MOD);
294 + ftm_counter_enable(ftm1_base);
296 + ftm_irq_enable(ftm1_base);
301 +static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id)
303 + ftm_irq_acknowledge(ftm1_base);
304 + ftm_irq_disable(ftm1_base);
307 + return IRQ_HANDLED;
310 +static ssize_t ftm_alarm_show(struct device *dev,
311 + struct device_attribute *attr,
316 + count = ftm_readl(ftm1_base + FTM_MOD);
317 + val = ftm_readl(ftm1_base + FTM_CNT);
318 + val = (count & MAX_COUNT_VAL) - val;
319 + val = cycle_to_time(val);
321 + return sprintf(buf, "%u\n", val);
324 +static ssize_t ftm_alarm_store(struct device *dev,
325 + struct device_attribute *attr,
326 + const char *buf, size_t count)
329 + unsigned long time;
331 + if (kstrtoul(buf, 0, &time))
336 + cycle = time_to_cycle(time);
340 + ftm_set_alarm(cycle);
345 +static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644,
346 + ftm_alarm_show, ftm_alarm_store);
348 +static int ftm_alarm_probe(struct platform_device *pdev)
350 + struct device_node *np = pdev->dev.of_node;
351 + struct resource *r;
354 + struct rcpm_cfg *rcpm_cfg;
355 + u32 ippdexpcr, flextimer;
356 + const struct of_device_id *of_id;
357 + enum pmu_endian_type endian;
359 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
363 + ftm1_base = devm_ioremap_resource(&pdev->dev, r);
364 + if (IS_ERR(ftm1_base))
365 + return PTR_ERR(ftm1_base);
367 + of_id = of_match_node(ippdexpcr_of_match, np);
371 + rcpm_cfg = devm_kzalloc(&pdev->dev, sizeof(*rcpm_cfg), GFP_KERNEL);
375 + rcpm_cfg = (struct rcpm_cfg *)of_id->data;
376 + endian = rcpm_cfg->big_endian;
377 + flextimer = rcpm_cfg->flextimer_set_bit;
379 + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmctrl");
381 + rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
382 + if (IS_ERR(rcpm_ftm_addr))
383 + return PTR_ERR(rcpm_ftm_addr);
384 + if (endian == BIG_ENDIAN)
385 + ippdexpcr = ioread32be(rcpm_ftm_addr);
387 + ippdexpcr = ioread32(rcpm_ftm_addr);
388 + ippdexpcr |= flextimer;
389 + if (endian == BIG_ENDIAN)
390 + iowrite32be(ippdexpcr, rcpm_ftm_addr);
392 + iowrite32(ippdexpcr, rcpm_ftm_addr);
394 + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scrachpad");
396 + scfg_scrachpad_addr = devm_ioremap_resource(&pdev->dev, r);
397 + iowrite32(ippdexpcr, scfg_scrachpad_addr);
401 + irq = irq_of_parse_and_map(np, 0);
403 + pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
407 + big_endian = of_property_read_bool(np, "big-endian");
409 + ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt,
410 + IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL);
412 + dev_err(&pdev->dev, "failed to request irq\n");
416 + ret = device_create_file(&pdev->dev, &ftm_alarm_attributes);
418 + dev_err(&pdev->dev, "create sysfs fail.\n");
422 + alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
426 + device_init_wakeup(&pdev->dev, true);
431 +static const struct of_device_id ftm_alarm_match[] = {
432 + { .compatible = "fsl,ftm-alarm", },
433 + { .compatible = "fsl,ls1012a-ftm-alarm", },
434 + { .compatible = "fsl,ls1021a-ftm-alarm", },
435 + { .compatible = "fsl,ls1043a-ftm-alarm", },
436 + { .compatible = "fsl,ls1046a-ftm-alarm", },
437 + { .compatible = "fsl,ls1088a-ftm-alarm", },
438 + { .compatible = "fsl,ls208xa-ftm-alarm", },
439 + { .compatible = "fsl,lx2160a-ftm-alarm", },
440 + { .compatible = "fsl,ftm-timer", },
444 +static struct platform_driver ftm_alarm_driver = {
445 + .probe = ftm_alarm_probe,
447 + .name = "ftm-alarm",
448 + .owner = THIS_MODULE,
449 + .of_match_table = ftm_alarm_match,
453 +static int __init ftm_alarm_init(void)
455 + return platform_driver_register(&ftm_alarm_driver);
457 +device_initcall(ftm_alarm_init);