layerscape: add patches-5.4
[openwrt/staging/wigyori.git] / target / linux / layerscape / patches-5.4 / 813-pm-0001-soc-fsl-add-RCPM-driver.patch
diff --git a/target/linux/layerscape/patches-5.4/813-pm-0001-soc-fsl-add-RCPM-driver.patch b/target/linux/layerscape/patches-5.4/813-pm-0001-soc-fsl-add-RCPM-driver.patch
new file mode 100644 (file)
index 0000000..533534d
--- /dev/null
@@ -0,0 +1,201 @@
+From 2bd25a6b5b5af59a33c22c2bf2cc4ea3043f33c5 Mon Sep 17 00:00:00 2001
+From: Ran Wang <ran.wang_1@nxp.com>
+Date: Thu, 24 Oct 2019 16:39:30 +0800
+Subject: [PATCH] soc: fsl: add RCPM driver
+
+The NXP's QorIQ processors based on ARM Core have RCPM module
+(Run Control and Power Management), which performs system level
+tasks associated with power management such as wakeup source control.
+
+Note that this driver will not support PowerPC based QorIQ processors,
+and it depends on PM wakeup source framework which provide collect
+wake information.
+
+Signed-off-by: Ran Wang <ran.wang_1@nxp.com>
+Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+[rebase]
+Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
+---
+ drivers/soc/fsl/Kconfig  |   9 +++
+ drivers/soc/fsl/Makefile |   1 +
+ drivers/soc/fsl/rcpm.c   | 151 +++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 161 insertions(+)
+ create mode 100644 drivers/soc/fsl/rcpm.c
+
+--- a/drivers/soc/fsl/Kconfig
++++ b/drivers/soc/fsl/Kconfig
+@@ -52,4 +52,13 @@ config FSL_QIXIS
+         Say y here to enable QIXIS system controller api. The qixis driver
+         provides FPGA functions to control system.
++config FSL_RCPM
++      bool "Freescale RCPM support"
++      depends on PM_SLEEP && (ARM || ARM64)
++      help
++        The NXP QorIQ Processors based on ARM Core have RCPM module
++        (Run Control and Power Management), which performs all device-level
++        tasks associated with power management, such as wakeup source control.
++        Note that currently this driver will not support PowerPC based
++        QorIQ processor.
+ endmenu
+--- a/drivers/soc/fsl/Makefile
++++ b/drivers/soc/fsl/Makefile
+@@ -10,3 +10,4 @@ obj-$(CONFIG_FSL_QIXIS)              += qixis_ctrl.
+ obj-$(CONFIG_FSL_GUTS)                        += guts.o
+ obj-$(CONFIG_FSL_MC_DPIO)             += dpio/
+ obj-$(CONFIG_DPAA2_CONSOLE)           += dpaa2-console.o
++obj-$(CONFIG_FSL_RCPM)                        += rcpm.o
+--- /dev/null
++++ b/drivers/soc/fsl/rcpm.c
+@@ -0,0 +1,151 @@
++// SPDX-License-Identifier: GPL-2.0
++//
++// rcpm.c - Freescale QorIQ RCPM driver
++//
++// Copyright 2019 NXP
++//
++// Author: Ran Wang <ran.wang_1@nxp.com>
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/of_address.h>
++#include <linux/slab.h>
++#include <linux/suspend.h>
++#include <linux/kernel.h>
++
++#define RCPM_WAKEUP_CELL_MAX_SIZE     7
++
++struct rcpm {
++      unsigned int    wakeup_cells;
++      void __iomem    *ippdexpcr_base;
++      bool            little_endian;
++};
++
++/**
++ * rcpm_pm_prepare - performs device-level tasks associated with power
++ * management, such as programming related to the wakeup source control.
++ * @dev: Device to handle.
++ *
++ */
++static int rcpm_pm_prepare(struct device *dev)
++{
++      int i, ret, idx;
++      void __iomem *base;
++      struct wakeup_source    *ws;
++      struct rcpm             *rcpm;
++      struct device_node      *np = dev->of_node;
++      u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
++      u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
++
++      rcpm = dev_get_drvdata(dev);
++      if (!rcpm)
++              return -EINVAL;
++
++      base = rcpm->ippdexpcr_base;
++      idx = wakeup_sources_read_lock();
++
++      /* Begin with first registered wakeup source */
++      for_each_wakeup_source(ws) {
++
++              /* skip object which is not attached to device */
++              if (!ws->dev || !ws->dev->parent)
++                      continue;
++
++              ret = device_property_read_u32_array(ws->dev->parent,
++                              "fsl,rcpm-wakeup", value,
++                              rcpm->wakeup_cells + 1);
++
++              /*  Wakeup source should refer to current rcpm device */
++              if (ret || (np->phandle != value[0]))
++                      continue;
++
++              /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
++               * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
++               * of wakeup source IP contains an integer array: <phandle to
++               * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
++               * IPPDEXPCR2 setting, etc>.
++               *
++               * So we will go thought them to collect setting data.
++               */
++              for (i = 0; i < rcpm->wakeup_cells; i++)
++                      setting[i] |= value[i + 1];
++      }
++
++      wakeup_sources_read_unlock(idx);
++
++      /* Program all IPPDEXPCRn once */
++      for (i = 0; i < rcpm->wakeup_cells; i++) {
++              u32 tmp = setting[i];
++              void __iomem *address = base + i * 4;
++
++              if (!tmp)
++                      continue;
++
++              /* We can only OR related bits */
++              if (rcpm->little_endian) {
++                      tmp |= ioread32(address);
++                      iowrite32(tmp, address);
++              } else {
++                      tmp |= ioread32be(address);
++                      iowrite32be(tmp, address);
++              }
++      }
++
++      return 0;
++}
++
++static const struct dev_pm_ops rcpm_pm_ops = {
++      .prepare =  rcpm_pm_prepare,
++};
++
++static int rcpm_probe(struct platform_device *pdev)
++{
++      struct device   *dev = &pdev->dev;
++      struct resource *r;
++      struct rcpm     *rcpm;
++      int ret;
++
++      rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
++      if (!rcpm)
++              return -ENOMEM;
++
++      r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      if (!r)
++              return -ENODEV;
++
++      rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
++      if (IS_ERR(rcpm->ippdexpcr_base)) {
++              ret =  PTR_ERR(rcpm->ippdexpcr_base);
++              return ret;
++      }
++
++      rcpm->little_endian = device_property_read_bool(
++                      &pdev->dev, "little-endian");
++
++      ret = device_property_read_u32(&pdev->dev,
++                      "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
++      if (ret)
++              return ret;
++
++      dev_set_drvdata(&pdev->dev, rcpm);
++
++      return 0;
++}
++
++static const struct of_device_id rcpm_of_match[] = {
++      { .compatible = "fsl,qoriq-rcpm-2.1+", },
++      {}
++};
++MODULE_DEVICE_TABLE(of, rcpm_of_match);
++
++static struct platform_driver rcpm_driver = {
++      .driver = {
++              .name = "rcpm",
++              .of_match_table = rcpm_of_match,
++              .pm     = &rcpm_pm_ops,
++      },
++      .probe = rcpm_probe,
++};
++
++module_platform_driver(rcpm_driver);