diff options
Diffstat (limited to 'target/linux/layerscape/patches-4.14/803-flextimer-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.14/803-flextimer-support-layerscape.patch | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.14/803-flextimer-support-layerscape.patch b/target/linux/layerscape/patches-4.14/803-flextimer-support-layerscape.patch new file mode 100644 index 0000000000..55ee185111 --- /dev/null +++ b/target/linux/layerscape/patches-4.14/803-flextimer-support-layerscape.patch @@ -0,0 +1,449 @@ +From d586effc9b71ddf240fb294b1ab1205bbe6fec4b Mon Sep 17 00:00:00 2001 +From: Biwen Li <biwen.li@nxp.com> +Date: Tue, 30 Oct 2018 18:26:32 +0800 +Subject: [PATCH 20/40] flextimer: support layerscape +This is an integrated patch of flextimer for layerscape + +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> +Signed-off-by: Biwen Li <biwen.li@nxp.com> +--- + .../bindings/soc/fsl/layerscape/ftm-alarm.txt | 32 ++ + drivers/clocksource/fsl_ftm_timer.c | 8 +- + drivers/soc/fsl/layerscape/ftm_alarm.c | 375 ++++++++++++++++++ + 3 files changed, 411 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,375 @@ ++/* ++ * 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 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}, ++ {}, ++}; ++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,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); |