blob: 6bb5be37eed2566b0fcbe5820eb35d4185041092 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From 2bd25a6b5b5af59a33c22c2bf2cc4ea3043f33c5 Mon Sep 17 00:00:00 2001
2From: Ran Wang <ran.wang_1@nxp.com>
3Date: Thu, 24 Oct 2019 16:39:30 +0800
4Subject: [PATCH] soc: fsl: add RCPM driver
5
6The NXP's QorIQ processors based on ARM Core have RCPM module
7(Run Control and Power Management), which performs system level
8tasks associated with power management such as wakeup source control.
9
10Note that this driver will not support PowerPC based QorIQ processors,
11and it depends on PM wakeup source framework which provide collect
12wake information.
13
14Signed-off-by: Ran Wang <ran.wang_1@nxp.com>
15Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
16[rebase]
17Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
18---
19 drivers/soc/fsl/Kconfig | 9 +++
20 drivers/soc/fsl/Makefile | 1 +
21 drivers/soc/fsl/rcpm.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++
22 3 files changed, 161 insertions(+)
23 create mode 100644 drivers/soc/fsl/rcpm.c
24
25--- a/drivers/soc/fsl/Kconfig
26+++ b/drivers/soc/fsl/Kconfig
27@@ -53,4 +53,13 @@ config FSL_QIXIS
28 Say y here to enable QIXIS system controller api. The qixis driver
29 provides FPGA functions to control system.
30
31+config FSL_RCPM
32+ bool "Freescale RCPM support"
33+ depends on PM_SLEEP && (ARM || ARM64)
34+ help
35+ The NXP QorIQ Processors based on ARM Core have RCPM module
36+ (Run Control and Power Management), which performs all device-level
37+ tasks associated with power management, such as wakeup source control.
38+ Note that currently this driver will not support PowerPC based
39+ QorIQ processor.
40 endmenu
41--- a/drivers/soc/fsl/Makefile
42+++ b/drivers/soc/fsl/Makefile
43@@ -10,3 +10,4 @@ obj-$(CONFIG_FSL_QIXIS) += qixis_ctrl.
44 obj-$(CONFIG_FSL_GUTS) += guts.o
45 obj-$(CONFIG_FSL_MC_DPIO) += dpio/
46 obj-$(CONFIG_DPAA2_CONSOLE) += dpaa2-console.o
47+obj-$(CONFIG_FSL_RCPM) += rcpm.o
48--- /dev/null
49+++ b/drivers/soc/fsl/rcpm.c
50@@ -0,0 +1,151 @@
51+// SPDX-License-Identifier: GPL-2.0
52+//
53+// rcpm.c - Freescale QorIQ RCPM driver
54+//
55+// Copyright 2019 NXP
56+//
57+// Author: Ran Wang <ran.wang_1@nxp.com>
58+
59+#include <linux/init.h>
60+#include <linux/module.h>
61+#include <linux/platform_device.h>
62+#include <linux/of_address.h>
63+#include <linux/slab.h>
64+#include <linux/suspend.h>
65+#include <linux/kernel.h>
66+
67+#define RCPM_WAKEUP_CELL_MAX_SIZE 7
68+
69+struct rcpm {
70+ unsigned int wakeup_cells;
71+ void __iomem *ippdexpcr_base;
72+ bool little_endian;
73+};
74+
75+/**
76+ * rcpm_pm_prepare - performs device-level tasks associated with power
77+ * management, such as programming related to the wakeup source control.
78+ * @dev: Device to handle.
79+ *
80+ */
81+static int rcpm_pm_prepare(struct device *dev)
82+{
83+ int i, ret, idx;
84+ void __iomem *base;
85+ struct wakeup_source *ws;
86+ struct rcpm *rcpm;
87+ struct device_node *np = dev->of_node;
88+ u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
89+ u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
90+
91+ rcpm = dev_get_drvdata(dev);
92+ if (!rcpm)
93+ return -EINVAL;
94+
95+ base = rcpm->ippdexpcr_base;
96+ idx = wakeup_sources_read_lock();
97+
98+ /* Begin with first registered wakeup source */
99+ for_each_wakeup_source(ws) {
100+
101+ /* skip object which is not attached to device */
102+ if (!ws->dev || !ws->dev->parent)
103+ continue;
104+
105+ ret = device_property_read_u32_array(ws->dev->parent,
106+ "fsl,rcpm-wakeup", value,
107+ rcpm->wakeup_cells + 1);
108+
109+ /* Wakeup source should refer to current rcpm device */
110+ if (ret || (np->phandle != value[0]))
111+ continue;
112+
113+ /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
114+ * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
115+ * of wakeup source IP contains an integer array: <phandle to
116+ * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
117+ * IPPDEXPCR2 setting, etc>.
118+ *
119+ * So we will go thought them to collect setting data.
120+ */
121+ for (i = 0; i < rcpm->wakeup_cells; i++)
122+ setting[i] |= value[i + 1];
123+ }
124+
125+ wakeup_sources_read_unlock(idx);
126+
127+ /* Program all IPPDEXPCRn once */
128+ for (i = 0; i < rcpm->wakeup_cells; i++) {
129+ u32 tmp = setting[i];
130+ void __iomem *address = base + i * 4;
131+
132+ if (!tmp)
133+ continue;
134+
135+ /* We can only OR related bits */
136+ if (rcpm->little_endian) {
137+ tmp |= ioread32(address);
138+ iowrite32(tmp, address);
139+ } else {
140+ tmp |= ioread32be(address);
141+ iowrite32be(tmp, address);
142+ }
143+ }
144+
145+ return 0;
146+}
147+
148+static const struct dev_pm_ops rcpm_pm_ops = {
149+ .prepare = rcpm_pm_prepare,
150+};
151+
152+static int rcpm_probe(struct platform_device *pdev)
153+{
154+ struct device *dev = &pdev->dev;
155+ struct resource *r;
156+ struct rcpm *rcpm;
157+ int ret;
158+
159+ rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
160+ if (!rcpm)
161+ return -ENOMEM;
162+
163+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
164+ if (!r)
165+ return -ENODEV;
166+
167+ rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
168+ if (IS_ERR(rcpm->ippdexpcr_base)) {
169+ ret = PTR_ERR(rcpm->ippdexpcr_base);
170+ return ret;
171+ }
172+
173+ rcpm->little_endian = device_property_read_bool(
174+ &pdev->dev, "little-endian");
175+
176+ ret = device_property_read_u32(&pdev->dev,
177+ "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
178+ if (ret)
179+ return ret;
180+
181+ dev_set_drvdata(&pdev->dev, rcpm);
182+
183+ return 0;
184+}
185+
186+static const struct of_device_id rcpm_of_match[] = {
187+ { .compatible = "fsl,qoriq-rcpm-2.1+", },
188+ {}
189+};
190+MODULE_DEVICE_TABLE(of, rcpm_of_match);
191+
192+static struct platform_driver rcpm_driver = {
193+ .driver = {
194+ .name = "rcpm",
195+ .of_match_table = rcpm_of_match,
196+ .pm = &rcpm_pm_ops,
197+ },
198+ .probe = rcpm_probe,
199+};
200+
201+module_platform_driver(rcpm_driver);