You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
457 lines
12 KiB
457 lines
12 KiB
From 0f31298eb0a9b2cd7990b709ff18229fadfa474b Mon Sep 17 00:00:00 2001
|
|
From: Biwen Li <biwen.li@nxp.com>
|
|
Date: Wed, 17 Apr 2019 18:58:38 +0800
|
|
Subject: [PATCH] flextimer: support layerscape
|
|
|
|
This is an integrated patch of flextimer for layerscape
|
|
|
|
Signed-off-by: Biwen Li <biwen.li@nxp.com>
|
|
Signed-off-by: Meng Yi <meng.yi@nxp.com>
|
|
Signed-off-by: Ran Wang <ran.wang_1@nxp.com>
|
|
Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
|
|
---
|
|
.../bindings/soc/fsl/layerscape/ftm-alarm.txt | 32 ++
|
|
drivers/clocksource/fsl_ftm_timer.c | 8 +-
|
|
drivers/soc/fsl/layerscape/ftm_alarm.c | 382 ++++++++++++++++++
|
|
3 files changed, 418 insertions(+), 4 deletions(-)
|
|
create mode 100644 Documentation/devicetree/bindings/soc/fsl/layerscape/ftm-alarm.txt
|
|
create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
|
|
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/soc/fsl/layerscape/ftm-alarm.txt
|
|
@@ -0,0 +1,32 @@
|
|
+Freescale FlexTimer Module (FTM) Alarm
|
|
+
|
|
+Required properties:
|
|
+
|
|
+- compatible : Should be "fsl,ftm-alarm" or "fsl,<chip>-ftm-alarm", the
|
|
+ supported chips include
|
|
+ "fsl,ls1012a-ftm-alarm"
|
|
+ "fsl,ls1021a-ftm-alarm"
|
|
+ "fsl,ls1043a-ftm-alarm"
|
|
+ "fsl,ls1046a-ftm-alarm"
|
|
+ "fsl,ls1088a-ftm-alarm"
|
|
+ "fsl,ls208xa-ftm-alarm"
|
|
+- reg : Specifies base physical address and size of the register sets for the
|
|
+ FlexTimer Module and base physical address of IP Powerdown Exception Control
|
|
+ Register.
|
|
+- reg-names: names of the mapped memory regions listed in regs property.
|
|
+ should include the following entries:
|
|
+ "ftm": Address of the register sets for FlexTimer Module
|
|
+ "pmctrl": Address of IP Powerdown Exception Control register
|
|
+- interrupts : Should be the FlexTimer Module interrupt.
|
|
+- big-endian: If the host controller is big-endian mode, specify this property.
|
|
+ The default endian mode is little-endian.
|
|
+
|
|
+Example:
|
|
+ftm0: ftm0@29d0000 {
|
|
+ compatible = "fsl,ls1043a-ftm-alarm";
|
|
+ reg = <0x0 0x29d0000 0x0 0x10000>,
|
|
+ <0x0 0x1ee2140 0x0 0x4>;
|
|
+ reg-names = "ftm", "pmctrl";
|
|
+ interrupts = <0 86 0x4>;
|
|
+ big-endian;
|
|
+};
|
|
--- a/drivers/clocksource/fsl_ftm_timer.c
|
|
+++ b/drivers/clocksource/fsl_ftm_timer.c
|
|
@@ -83,11 +83,11 @@ static inline void ftm_counter_disable(v
|
|
|
|
static inline void ftm_irq_acknowledge(void __iomem *base)
|
|
{
|
|
- u32 val;
|
|
+ unsigned int timeout = 100;
|
|
|
|
- val = ftm_readl(base + FTM_SC);
|
|
- val &= ~FTM_SC_TOF;
|
|
- ftm_writel(val, base + FTM_SC);
|
|
+ while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
|
|
+ ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
|
|
+ base + FTM_SC);
|
|
}
|
|
|
|
static inline void ftm_irq_enable(void __iomem *base)
|
|
--- /dev/null
|
|
+++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
|
|
@@ -0,0 +1,382 @@
|
|
+/*
|
|
+ * Freescale FlexTimer Module (FTM) Alarm driver.
|
|
+ *
|
|
+ * Copyright 2014 Freescale Semiconductor, Inc.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License
|
|
+ * as published by the Free Software Foundation; either version 2
|
|
+ * of the License, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/device.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/libata.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#define FTM_SC 0x00
|
|
+#define FTM_SC_CLK_SHIFT 3
|
|
+#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
|
|
+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
|
|
+#define FTM_SC_PS_MASK 0x7
|
|
+#define FTM_SC_TOIE BIT(6)
|
|
+#define FTM_SC_TOF BIT(7)
|
|
+
|
|
+#define FTM_SC_CLKS_FIXED_FREQ 0x02
|
|
+
|
|
+#define FTM_CNT 0x04
|
|
+#define FTM_MOD 0x08
|
|
+#define FTM_CNTIN 0x4C
|
|
+
|
|
+#define FIXED_FREQ_CLK 32000
|
|
+#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
|
|
+#define MAX_COUNT_VAL 0xffff
|
|
+
|
|
+static void __iomem *ftm1_base;
|
|
+static void __iomem *rcpm_ftm_addr;
|
|
+static void __iomem *scfg_scrachpad_addr;
|
|
+static u32 alarm_freq;
|
|
+static bool big_endian;
|
|
+
|
|
+enum pmu_endian_type {
|
|
+ BIG_ENDIAN,
|
|
+ LITTLE_ENDIAN,
|
|
+};
|
|
+
|
|
+struct rcpm_cfg {
|
|
+ enum pmu_endian_type big_endian; /* Big/Little endian of PMU module */
|
|
+ u32 flextimer_set_bit; /* FTM is not powerdown during device LPM20 */
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg ls1012a_rcpm_cfg = {
|
|
+ .big_endian = BIG_ENDIAN,
|
|
+ .flextimer_set_bit = 0x20000,
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg ls1021a_rcpm_cfg = {
|
|
+ .big_endian = BIG_ENDIAN,
|
|
+ .flextimer_set_bit = 0x30000000,
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg ls1043a_rcpm_cfg = {
|
|
+ .big_endian = BIG_ENDIAN,
|
|
+ .flextimer_set_bit = 0x20000,
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg ls1046a_rcpm_cfg = {
|
|
+ .big_endian = BIG_ENDIAN,
|
|
+ .flextimer_set_bit = 0x20000,
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg ls1088a_rcpm_cfg = {
|
|
+ .big_endian = LITTLE_ENDIAN,
|
|
+ .flextimer_set_bit = 0x4000,
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg ls208xa_rcpm_cfg = {
|
|
+ .big_endian = LITTLE_ENDIAN,
|
|
+ .flextimer_set_bit = 0x4000,
|
|
+};
|
|
+
|
|
+static struct rcpm_cfg lx2160a_rcpm_cfg = {
|
|
+ .big_endian = LITTLE_ENDIAN,
|
|
+ .flextimer_set_bit = 0x4000,
|
|
+};
|
|
+
|
|
+static const struct of_device_id ippdexpcr_of_match[] = {
|
|
+ { .compatible = "fsl,ls1012a-ftm-alarm", .data = &ls1012a_rcpm_cfg},
|
|
+ { .compatible = "fsl,ls1021a-ftm-alarm", .data = &ls1021a_rcpm_cfg},
|
|
+ { .compatible = "fsl,ls1043a-ftm-alarm", .data = &ls1043a_rcpm_cfg},
|
|
+ { .compatible = "fsl,ls1046a-ftm-alarm", .data = &ls1046a_rcpm_cfg},
|
|
+ { .compatible = "fsl,ls1088a-ftm-alarm", .data = &ls1088a_rcpm_cfg},
|
|
+ { .compatible = "fsl,ls208xa-ftm-alarm", .data = &ls208xa_rcpm_cfg},
|
|
+ { .compatible = "fsl,lx2160a-ftm-alarm", .data = &lx2160a_rcpm_cfg},
|
|
+ {},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, ippdexpcr_of_match);
|
|
+
|
|
+static inline u32 ftm_readl(void __iomem *addr)
|
|
+{
|
|
+ if (big_endian)
|
|
+ return ioread32be(addr);
|
|
+
|
|
+ return ioread32(addr);
|
|
+}
|
|
+
|
|
+static inline void ftm_writel(u32 val, void __iomem *addr)
|
|
+{
|
|
+ if (big_endian)
|
|
+ iowrite32be(val, addr);
|
|
+ else
|
|
+ iowrite32(val, addr);
|
|
+}
|
|
+
|
|
+static inline void ftm_counter_enable(void __iomem *base)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ /* select and enable counter clock source */
|
|
+ val = ftm_readl(base + FTM_SC);
|
|
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
|
|
+ val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
|
|
+ ftm_writel(val, base + FTM_SC);
|
|
+}
|
|
+
|
|
+static inline void ftm_counter_disable(void __iomem *base)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ /* disable counter clock source */
|
|
+ val = ftm_readl(base + FTM_SC);
|
|
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
|
|
+ ftm_writel(val, base + FTM_SC);
|
|
+}
|
|
+
|
|
+static inline void ftm_irq_acknowledge(void __iomem *base)
|
|
+{
|
|
+ unsigned int timeout = 100;
|
|
+
|
|
+ while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
|
|
+ ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
|
|
+ base + FTM_SC);
|
|
+}
|
|
+
|
|
+static inline void ftm_irq_enable(void __iomem *base)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = ftm_readl(base + FTM_SC);
|
|
+ val |= FTM_SC_TOIE;
|
|
+ ftm_writel(val, base + FTM_SC);
|
|
+}
|
|
+
|
|
+static inline void ftm_irq_disable(void __iomem *base)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = ftm_readl(base + FTM_SC);
|
|
+ val &= ~FTM_SC_TOIE;
|
|
+ ftm_writel(val, base + FTM_SC);
|
|
+}
|
|
+
|
|
+static inline void ftm_reset_counter(void __iomem *base)
|
|
+{
|
|
+ /*
|
|
+ * The CNT register contains the FTM counter value.
|
|
+ * Reset clears the CNT register. Writing any value to COUNT
|
|
+ * updates the counter with its initial value, CNTIN.
|
|
+ */
|
|
+ ftm_writel(0x00, base + FTM_CNT);
|
|
+}
|
|
+
|
|
+static u32 time_to_cycle(unsigned long time)
|
|
+{
|
|
+ u32 cycle;
|
|
+
|
|
+ cycle = time * alarm_freq;
|
|
+ if (cycle > MAX_COUNT_VAL) {
|
|
+ pr_err("Out of alarm range.\n");
|
|
+ cycle = 0;
|
|
+ }
|
|
+
|
|
+ return cycle;
|
|
+}
|
|
+
|
|
+static u32 cycle_to_time(u32 cycle)
|
|
+{
|
|
+ return cycle / alarm_freq + 1;
|
|
+}
|
|
+
|
|
+static void ftm_clean_alarm(void)
|
|
+{
|
|
+ ftm_counter_disable(ftm1_base);
|
|
+
|
|
+ ftm_writel(0x00, ftm1_base + FTM_CNTIN);
|
|
+ ftm_writel(~0U, ftm1_base + FTM_MOD);
|
|
+
|
|
+ ftm_reset_counter(ftm1_base);
|
|
+}
|
|
+
|
|
+static int ftm_set_alarm(u64 cycle)
|
|
+{
|
|
+ ftm_irq_disable(ftm1_base);
|
|
+
|
|
+ /*
|
|
+ * The counter increments until the value of MOD is reached,
|
|
+ * at which point the counter is reloaded with the value of CNTIN.
|
|
+ * The TOF (the overflow flag) bit is set when the FTM counter
|
|
+ * changes from MOD to CNTIN. So we should using the cycle - 1.
|
|
+ */
|
|
+ ftm_writel(cycle - 1, ftm1_base + FTM_MOD);
|
|
+
|
|
+ ftm_counter_enable(ftm1_base);
|
|
+
|
|
+ ftm_irq_enable(ftm1_base);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id)
|
|
+{
|
|
+ ftm_irq_acknowledge(ftm1_base);
|
|
+ ftm_irq_disable(ftm1_base);
|
|
+ ftm_clean_alarm();
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static ssize_t ftm_alarm_show(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ u32 count, val;
|
|
+
|
|
+ count = ftm_readl(ftm1_base + FTM_MOD);
|
|
+ val = ftm_readl(ftm1_base + FTM_CNT);
|
|
+ val = (count & MAX_COUNT_VAL) - val;
|
|
+ val = cycle_to_time(val);
|
|
+
|
|
+ return sprintf(buf, "%u\n", val);
|
|
+}
|
|
+
|
|
+static ssize_t ftm_alarm_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ u32 cycle;
|
|
+ unsigned long time;
|
|
+
|
|
+ if (kstrtoul(buf, 0, &time))
|
|
+ return -EINVAL;
|
|
+
|
|
+ ftm_clean_alarm();
|
|
+
|
|
+ cycle = time_to_cycle(time);
|
|
+ if (!cycle)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ftm_set_alarm(cycle);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644,
|
|
+ ftm_alarm_show, ftm_alarm_store);
|
|
+
|
|
+static int ftm_alarm_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device_node *np = pdev->dev.of_node;
|
|
+ struct resource *r;
|
|
+ int irq;
|
|
+ int ret;
|
|
+ struct rcpm_cfg *rcpm_cfg;
|
|
+ u32 ippdexpcr, flextimer;
|
|
+ const struct of_device_id *of_id;
|
|
+ enum pmu_endian_type endian;
|
|
+
|
|
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (!r)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ftm1_base = devm_ioremap_resource(&pdev->dev, r);
|
|
+ if (IS_ERR(ftm1_base))
|
|
+ return PTR_ERR(ftm1_base);
|
|
+
|
|
+ of_id = of_match_node(ippdexpcr_of_match, np);
|
|
+ if (!of_id)
|
|
+ return -ENODEV;
|
|
+
|
|
+ rcpm_cfg = devm_kzalloc(&pdev->dev, sizeof(*rcpm_cfg), GFP_KERNEL);
|
|
+ if (!rcpm_cfg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ rcpm_cfg = (struct rcpm_cfg *)of_id->data;
|
|
+ endian = rcpm_cfg->big_endian;
|
|
+ flextimer = rcpm_cfg->flextimer_set_bit;
|
|
+
|
|
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmctrl");
|
|
+ if (r) {
|
|
+ rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
|
|
+ if (IS_ERR(rcpm_ftm_addr))
|
|
+ return PTR_ERR(rcpm_ftm_addr);
|
|
+ if (endian == BIG_ENDIAN)
|
|
+ ippdexpcr = ioread32be(rcpm_ftm_addr);
|
|
+ else
|
|
+ ippdexpcr = ioread32(rcpm_ftm_addr);
|
|
+ ippdexpcr |= flextimer;
|
|
+ if (endian == BIG_ENDIAN)
|
|
+ iowrite32be(ippdexpcr, rcpm_ftm_addr);
|
|
+ else
|
|
+ iowrite32(ippdexpcr, rcpm_ftm_addr);
|
|
+
|
|
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scrachpad");
|
|
+ if (r) {
|
|
+ scfg_scrachpad_addr = devm_ioremap_resource(&pdev->dev, r);
|
|
+ iowrite32(ippdexpcr, scfg_scrachpad_addr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ irq = irq_of_parse_and_map(np, 0);
|
|
+ if (irq <= 0) {
|
|
+ pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ big_endian = of_property_read_bool(np, "big-endian");
|
|
+
|
|
+ ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt,
|
|
+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&pdev->dev, "failed to request irq\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = device_create_file(&pdev->dev, &ftm_alarm_attributes);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "create sysfs fail.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
|
|
+
|
|
+ ftm_clean_alarm();
|
|
+
|
|
+ device_init_wakeup(&pdev->dev, true);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct of_device_id ftm_alarm_match[] = {
|
|
+ { .compatible = "fsl,ftm-alarm", },
|
|
+ { .compatible = "fsl,ls1012a-ftm-alarm", },
|
|
+ { .compatible = "fsl,ls1021a-ftm-alarm", },
|
|
+ { .compatible = "fsl,ls1043a-ftm-alarm", },
|
|
+ { .compatible = "fsl,ls1046a-ftm-alarm", },
|
|
+ { .compatible = "fsl,ls1088a-ftm-alarm", },
|
|
+ { .compatible = "fsl,ls208xa-ftm-alarm", },
|
|
+ { .compatible = "fsl,lx2160a-ftm-alarm", },
|
|
+ { .compatible = "fsl,ftm-timer", },
|
|
+ { },
|
|
+};
|
|
+
|
|
+static struct platform_driver ftm_alarm_driver = {
|
|
+ .probe = ftm_alarm_probe,
|
|
+ .driver = {
|
|
+ .name = "ftm-alarm",
|
|
+ .owner = THIS_MODULE,
|
|
+ .of_match_table = ftm_alarm_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init ftm_alarm_init(void)
|
|
+{
|
|
+ return platform_driver_register(&ftm_alarm_driver);
|
|
+}
|
|
+device_initcall(ftm_alarm_init);
|
|
|