layerscape: add linux 4.9 support
[openwrt/staging/hauke.git] / target / linux / layerscape / patches-4.9 / 806-flextimer-support-layerscape.patch
diff --git a/target/linux/layerscape/patches-4.9/806-flextimer-support-layerscape.patch b/target/linux/layerscape/patches-4.9/806-flextimer-support-layerscape.patch
new file mode 100644 (file)
index 0000000..190e8d5
--- /dev/null
@@ -0,0 +1,331 @@
+From a5b3155b532289af793c26251cb087b4a24d5c15 Mon Sep 17 00:00:00 2001
+From: Yangbo Lu <yangbo.lu@nxp.com>
+Date: Mon, 25 Sep 2017 12:13:12 +0800
+Subject: [PATCH] flextimer: support layerscape
+
+This is a integrated patch for layerscape flextimer support.
+
+Signed-off-by: Wang Dongsheng <dongsheng.wang@nxp.com>
+Signed-off-by: Meng Yi <meng.yi@nxp.com>
+Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
+---
+ drivers/clocksource/fsl_ftm_timer.c    |   8 +-
+ drivers/soc/fsl/layerscape/ftm_alarm.c | 286 +++++++++++++++++++++++++++++++++
+ 2 files changed, 290 insertions(+), 4 deletions(-)
+ create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
+
+diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
+index 738515b8..770bbbca 100644
+--- a/drivers/clocksource/fsl_ftm_timer.c
++++ b/drivers/clocksource/fsl_ftm_timer.c
+@@ -83,11 +83,11 @@ static inline void ftm_counter_disable(void __iomem *base)
+ 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)
+diff --git a/drivers/soc/fsl/layerscape/ftm_alarm.c b/drivers/soc/fsl/layerscape/ftm_alarm.c
+new file mode 100644
+index 00000000..6f9882ff
+--- /dev/null
++++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
+@@ -0,0 +1,286 @@
++/*
++ * 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>
++
++#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 u32 alarm_freq;
++static bool big_endian;
++
++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;
++      u32 ippdexpcr;
++
++      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);
++
++      r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "FlexTimer1");
++      if (r) {
++              rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
++              if (IS_ERR(rcpm_ftm_addr))
++                      return PTR_ERR(rcpm_ftm_addr);
++              ippdexpcr = ioread32be(rcpm_ftm_addr);
++              ippdexpcr |= 0x20000;
++              iowrite32be(ippdexpcr, rcpm_ftm_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,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);
+-- 
+2.14.1
+