mvebu: drop support for old kernels
[openwrt/svn-archive/archive.git] / target / linux / mvebu / patches-3.18 / 202-gpio_mvebu_add_limited_pwm_support.patch
diff --git a/target/linux/mvebu/patches-3.18/202-gpio_mvebu_add_limited_pwm_support.patch b/target/linux/mvebu/patches-3.18/202-gpio_mvebu_add_limited_pwm_support.patch
deleted file mode 100644 (file)
index 49a8c0a..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-Armada 370/XP devices can 'blink' gpio lines with a configurable on
-and off period. This can be modelled as a PWM.
-
-However, there are only two sets of PWM configuration registers for
-all the gpio lines. This driver simply allows a single gpio line per
-gpio chip of 32 lines to be used as a PWM. Attempts to use more return
-EBUSY.
-
-Due to the interleaving of registers it is not simple to separate the
-PWM driver from the gpio driver. Thus the gpio driver has been
-extended with a PWM driver.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- drivers/gpio/Kconfig          |   5 ++
- drivers/gpio/Makefile         |   1 +
- drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++
- drivers/gpio/gpio-mvebu.c     |  37 +++-----
- drivers/gpio/gpio-mvebu.h     |  79 +++++++++++++++++
- 5 files changed, 299 insertions(+), 25 deletions(-)
- create mode 100644 drivers/gpio/gpio-mvebu-pwm.c
- create mode 100644 drivers/gpio/gpio-mvebu.h
-
---- a/drivers/gpio/Kconfig
-+++ b/drivers/gpio/Kconfig
-@@ -223,6 +223,11 @@ config GPIO_MVEBU
-       select GPIO_GENERIC
-       select GENERIC_IRQ_CHIP
-+config GPIO_MVEBU_PWM
-+      def_bool y
-+      depends on GPIO_MVEBU
-+      depends on PWM
-+
- config GPIO_MXC
-       def_bool y
-       depends on ARCH_MXC
---- a/drivers/gpio/Makefile
-+++ b/drivers/gpio/Makefile
-@@ -58,6 +58,7 @@ obj-$(CONFIG_GPIO_MSIC)              += gpio-msic.o
- obj-$(CONFIG_GPIO_MSM_V1)     += gpio-msm-v1.o
- obj-$(CONFIG_GPIO_MSM_V2)     += gpio-msm-v2.o
- obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
-+obj-$(CONFIG_GPIO_MVEBU_PWM)  += gpio-mvebu-pwm.o
- obj-$(CONFIG_GPIO_MXC)                += gpio-mxc.o
- obj-$(CONFIG_GPIO_MXS)                += gpio-mxs.o
- obj-$(CONFIG_GPIO_OCTEON)     += gpio-octeon.o
---- /dev/null
-+++ b/drivers/gpio/gpio-mvebu-pwm.c
-@@ -0,0 +1,202 @@
-+#include "asm/io.h"
-+#include <linux/err.h>
-+#include <linux/module.h>
-+#include <linux/gpio.h>
-+#include <linux/pwm.h>
-+#include <linux/clk.h>
-+#include <linux/platform_device.h>
-+#include "gpio-mvebu.h"
-+#include "gpiolib.h"
-+static void __iomem *mvebu_gpioreg_blink_select(struct mvebu_gpio_chip *mvchip)
-+{
-+      return mvchip->membase + GPIO_BLINK_CNT_SELECT;
-+}
-+
-+static inline struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip)
-+{
-+      return container_of(chip, struct mvebu_pwm, chip);
-+}
-+
-+static inline struct mvebu_gpio_chip *to_mvchip(struct mvebu_pwm *pwm)
-+{
-+      return container_of(pwm, struct mvebu_gpio_chip, pwm);
-+}
-+
-+static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwmd)
-+{
-+      struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
-+      struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
-+      struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
-+      unsigned long flags;
-+      int ret = 0;
-+
-+      spin_lock_irqsave(&pwm->lock, flags);
-+      if (pwm->used) {
-+              ret = -EBUSY;
-+      } else {
-+              if (!desc) {
-+                      ret = -ENODEV;
-+                      goto out;
-+              }
-+              ret = gpiod_request(desc, "mvebu-pwm");
-+              if (ret)
-+                      goto out;
-+
-+              ret = gpiod_direction_output(desc, 0);
-+              if (ret) {
-+                      gpiod_free(desc);
-+                      goto out;
-+              }
-+
-+              pwm->pin = pwmd->pwm - mvchip->chip.base;
-+              pwm->used = true;
-+      }
-+
-+out:
-+      spin_unlock_irqrestore(&pwm->lock, flags);
-+      return ret;
-+}
-+
-+static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwmd)
-+{
-+      struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
-+      struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
-+      unsigned long flags;
-+
-+      spin_lock_irqsave(&pwm->lock, flags);
-+      gpiod_free(desc);
-+      pwm->used = false;
-+      spin_unlock_irqrestore(&pwm->lock, flags);
-+}
-+
-+static int mvebu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwmd,
-+                          int duty_ns, int period_ns)
-+{
-+      struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
-+      struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
-+      unsigned int on, off;
-+      unsigned long long val;
-+      u32 u;
-+
-+      val = (unsigned long long) pwm->clk_rate * duty_ns;
-+      do_div(val, NSEC_PER_SEC);
-+      if (val > UINT_MAX)
-+              return -EINVAL;
-+      if (val)
-+              on = val;
-+      else
-+              on = 1;
-+
-+      val = (unsigned long long) pwm->clk_rate * (period_ns - duty_ns);
-+      do_div(val, NSEC_PER_SEC);
-+      if (val > UINT_MAX)
-+              return -EINVAL;
-+      if (val)
-+              off = val;
-+      else
-+              off = 1;
-+
-+      u = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
-+      u &= ~(1 << pwm->pin);
-+      u |= (pwm->id << pwm->pin);
-+      writel_relaxed(u, mvebu_gpioreg_blink_select(mvchip));
-+
-+      writel_relaxed(on, pwm->membase + BLINK_ON_DURATION);
-+      writel_relaxed(off, pwm->membase + BLINK_OFF_DURATION);
-+
-+      return 0;
-+}
-+
-+static int mvebu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwmd)
-+{
-+      struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
-+      struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
-+
-+      mvebu_gpio_blink(&mvchip->chip, pwm->pin, 1);
-+
-+      return 0;
-+}
-+
-+static void mvebu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwmd)
-+{
-+      struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
-+      struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
-+
-+      mvebu_gpio_blink(&mvchip->chip, pwm->pin, 0);
-+}
-+
-+static const struct pwm_ops mvebu_pwm_ops = {
-+      .request = mvebu_pwm_request,
-+      .free = mvebu_pwm_free,
-+      .config = mvebu_pwm_config,
-+      .enable = mvebu_pwm_enable,
-+      .disable = mvebu_pwm_disable,
-+      .owner = THIS_MODULE,
-+};
-+
-+void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
-+{
-+      struct mvebu_pwm *pwm = &mvchip->pwm;
-+
-+      pwm->blink_select = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
-+      pwm->blink_on_duration =
-+              readl_relaxed(pwm->membase + BLINK_ON_DURATION);
-+      pwm->blink_off_duration =
-+              readl_relaxed(pwm->membase + BLINK_OFF_DURATION);
-+}
-+
-+void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
-+{
-+      struct mvebu_pwm *pwm = &mvchip->pwm;
-+
-+      writel_relaxed(pwm->blink_select, mvebu_gpioreg_blink_select(mvchip));
-+      writel_relaxed(pwm->blink_on_duration,
-+                     pwm->membase + BLINK_ON_DURATION);
-+      writel_relaxed(pwm->blink_off_duration,
-+                     pwm->membase + BLINK_OFF_DURATION);
-+}
-+
-+/*
-+ * Armada 370/XP has simple PWM support for gpio lines. Other SoCs
-+ * don't have this hardware. So if we don't have the necessary
-+ * resource, it is not an error.
-+ */
-+int mvebu_pwm_probe(struct platform_device *pdev,
-+                  struct mvebu_gpio_chip *mvchip,
-+                  int id)
-+{
-+      struct device *dev = &pdev->dev;
-+      struct mvebu_pwm *pwm = &mvchip->pwm;
-+      struct resource *res;
-+
-+      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
-+      if (!res)
-+              return 0;
-+
-+      mvchip->pwm.membase = devm_ioremap_resource(&pdev->dev, res);
-+      if (IS_ERR(mvchip->pwm.membase))
-+              return PTR_ERR(mvchip->percpu_membase);
-+
-+      if (id < 0 || id > 1)
-+              return -EINVAL;
-+      pwm->id = id;
-+
-+      if (IS_ERR(mvchip->clk))
-+              return PTR_ERR(mvchip->clk);
-+
-+      pwm->clk_rate = clk_get_rate(mvchip->clk);
-+      if (!pwm->clk_rate) {
-+              dev_err(dev, "failed to get clock rate\n");
-+              return -EINVAL;
-+      }
-+
-+      pwm->chip.dev = dev;
-+      pwm->chip.ops = &mvebu_pwm_ops;
-+      pwm->chip.base = mvchip->chip.base;
-+      pwm->chip.npwm = mvchip->chip.ngpio;
-+      pwm->chip.can_sleep = false;
-+
-+      spin_lock_init(&pwm->lock);
-+
-+      return pwmchip_add(&pwm->chip);
-+}
---- a/drivers/gpio/gpio-mvebu.c
-+++ b/drivers/gpio/gpio-mvebu.c
-@@ -42,10 +42,11 @@
- #include <linux/io.h>
- #include <linux/of_irq.h>
- #include <linux/of_device.h>
-+#include <linux/pwm.h>
- #include <linux/clk.h>
- #include <linux/pinctrl/consumer.h>
- #include <linux/irqchip/chained_irq.h>
--
-+#include "gpio-mvebu.h"
- /*
-  * GPIO unit register offsets.
-  */
-@@ -75,24 +76,6 @@
- #define MVEBU_MAX_GPIO_PER_BANK               32
--struct mvebu_gpio_chip {
--      struct gpio_chip   chip;
--      spinlock_t         lock;
--      void __iomem      *membase;
--      void __iomem      *percpu_membase;
--      int                irqbase;
--      struct irq_domain *domain;
--      int                soc_variant;
--
--      /* Used to preserve GPIO registers across suspend/resume */
--      u32                out_reg;
--      u32                io_conf_reg;
--      u32                blink_en_reg;
--      u32                in_pol_reg;
--      u32                edge_mask_regs[4];
--      u32                level_mask_regs[4];
--};
--
- /*
-  * Functions returning addresses of individual registers for a given
-  * GPIO controller.
-@@ -228,7 +211,7 @@ static int mvebu_gpio_get(struct gpio_ch
-       return (u >> pin) & 1;
- }
--static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
-+void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
- {
-       struct mvebu_gpio_chip *mvchip =
-               container_of(chip, struct mvebu_gpio_chip, chip);
-@@ -617,6 +600,8 @@ static int mvebu_gpio_suspend(struct pla
-               BUG();
-       }
-+      mvebu_pwm_suspend(mvchip);
-+
-       return 0;
- }
-@@ -660,6 +645,8 @@ static int mvebu_gpio_resume(struct plat
-               BUG();
-       }
-+      mvebu_pwm_resume(mvchip);
-+
-       return 0;
- }
-@@ -671,7 +658,6 @@ static int mvebu_gpio_probe(struct platf
-       struct resource *res;
-       struct irq_chip_generic *gc;
-       struct irq_chip_type *ct;
--      struct clk *clk;
-       unsigned int ngpios;
-       int soc_variant;
-       int i, cpu, id;
-@@ -701,10 +687,10 @@ static int mvebu_gpio_probe(struct platf
-               return id;
-       }
--      clk = devm_clk_get(&pdev->dev, NULL);
-+      mvchip->clk = devm_clk_get(&pdev->dev, NULL);
-       /* Not all SoCs require a clock.*/
--      if (!IS_ERR(clk))
--              clk_prepare_enable(clk);
-+      if (!IS_ERR(mvchip->clk))
-+              clk_prepare_enable(mvchip->clk);
-       mvchip->soc_variant = soc_variant;
-       mvchip->chip.label = dev_name(&pdev->dev);
-@@ -838,7 +824,8 @@ static int mvebu_gpio_probe(struct platf
-               goto err_generic_chip;
-       }
--      return 0;
-+      /* Armada 370/XP has simple PWM support for gpio lines */
-+      return mvebu_pwm_probe(pdev, mvchip, id);
- err_generic_chip:
-       irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
---- /dev/null
-+++ b/drivers/gpio/gpio-mvebu.h
-@@ -0,0 +1,79 @@
-+/*
-+ * Interface between MVEBU GPIO driver and PWM driver for GPIO pins
-+ *
-+ * Copyright (C) 2015, Andrew Lunn <andrew@lunn.ch>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+
-+#ifndef MVEBU_GPIO_PWM_H
-+#define MVEBU_GPIO_PWM_H
-+
-+#define BLINK_ON_DURATION     0x0
-+#define BLINK_OFF_DURATION    0x4
-+#define GPIO_BLINK_CNT_SELECT 0x0020
-+
-+struct mvebu_pwm {
-+      void __iomem    *membase;
-+      unsigned long    clk_rate;
-+      bool             used;
-+      unsigned         pin;
-+      struct pwm_chip  chip;
-+      int              id;
-+      spinlock_t       lock;
-+
-+      /* Used to preserve GPIO/PWM registers across suspend /
-+       * resume */
-+      u32              blink_select;
-+      u32              blink_on_duration;
-+      u32              blink_off_duration;
-+};
-+
-+struct mvebu_gpio_chip {
-+      struct gpio_chip   chip;
-+      spinlock_t         lock;
-+      void __iomem      *membase;
-+      void __iomem      *percpu_membase;
-+      int                irqbase;
-+      struct irq_domain *domain;
-+      int                soc_variant;
-+      struct clk        *clk;
-+#ifdef CONFIG_PWM
-+      struct mvebu_pwm pwm;
-+#endif
-+      /* Used to preserve GPIO registers across suspend/resume */
-+      u32                out_reg;
-+      u32                io_conf_reg;
-+      u32                blink_en_reg;
-+      u32                in_pol_reg;
-+      u32                edge_mask_regs[4];
-+      u32                level_mask_regs[4];
-+};
-+
-+void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value);
-+
-+#ifdef CONFIG_PWM
-+int mvebu_pwm_probe(struct platform_device *pdev,
-+                  struct mvebu_gpio_chip *mvchip,
-+                  int id);
-+void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip);
-+void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip);
-+#else
-+int mvebu_pwm_probe(struct platform_device *pdev,
-+                  struct mvebu_gpio_chip *mvchip,
-+                  int id)
-+{
-+      return 0;
-+}
-+
-+void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
-+{
-+}
-+
-+void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
-+{
-+}
-+#endif
-+#endif