mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
202 lines
5.4 KiB
Diff
202 lines
5.4 KiB
Diff
|
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);
|