Some user might want to go through all registered wakeup sources
and doing things accordingly. For example, SoC PM driver might need to
do HW programming to prevent powering down specific IP which wakeup
source depending on. And is user's responsibility to identify if this
wakeup source he is interested in.
Signed-off-by: Ran Wang <redacted>
---
Change in v2:
- None.
drivers/base/power/wakeup.c | 18 ++++++++++++++++++
include/linux/pm_wakeup.h | 3 +++
2 files changed, 21 insertions(+), 0 deletions(-)
By default, QorIQ SoC's RCPM register block is Big Endian. But
there are some exceptions, such as LS1088A and LS2088A, are Little
Endian. So add this optional property to help identify them.
Actually LS2021A and other Layerscapes won't totally follow Chassis
2.1, so separate them from powerpc SoC.
Signed-off-by: Ran Wang <redacted>
---
Change in v2:
- None.
Documentation/devicetree/bindings/soc/fsl/rcpm.txt | 8 +++++++-
1 files changed, 7 insertions(+), 1 deletions(-)
@@ -20,6 +20,7 @@ Required properites: * "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm * "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm * "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm+ * "fsl,qoriq-rcpm-2.1+": for chassis 2.1+ rcpm All references to "1.0" and "2.0" refer to the QorIQ chassis version to which the chip complies.
@@ -27,7 +28,12 @@ Chassis Version Example Chips --------------- ------------------------------- 1.0 p4080, p5020, p5040, p2041, p3041 2.0 t4240, b4860, b4420-2.1 t1040, ls1021+2.1 t1040,+2.1+ ls1021a, ls1012a, ls1043a, ls1046a++Optional properties:+ - little-endian : RCPM register block is Little Endian. Without it RCPM+ will be Big Endian (default case). Example: The RCPM node for T4240:
The NXP's 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.
This driver depends on PM wakeup source framework which help to
collect wake information.
Signed-off-by: Ran Wang <redacted>
---
Change in v2:
- Rebase Kconfig and Makefile update to latest mainline.
drivers/soc/fsl/Kconfig | 8 +++
drivers/soc/fsl/Makefile | 1 +
drivers/soc/fsl/rcpm.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+), 0 deletions(-)
create mode 100644 drivers/soc/fsl/rcpm.c
@@ -0,0 +1,124 @@+// 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++structrcpm{+unsignedintwakeup_cells;+void__iomem*ippdexpcr_base;+boollittle_endian;+};++staticintrcpm_pm_prepare(structdevice*dev)+{+structdevice_node*np=dev->of_node;+structwakeup_source*ws;+structrcpm*rcpm;+u32value[RCPM_WAKEUP_CELL_MAX_SIZE+1],tmp;+inti,ret;++rcpm=dev_get_drvdata(dev);+if(!rcpm)+return-EINVAL;++/* Begin with first registered wakeup source */+ws=wakeup_source_get_next(NULL);+while(ws){+ret=device_property_read_u32_array(ws->attached_dev,+"fsl,rcpm-wakeup",value,rcpm->wakeup_cells+1);++/* Wakeup source should refer to current rcpm device */+if(ret||(np->phandle!=value[0])){+dev_info(dev,"%s doesn't refer to this rcpm\n",+ws->name);+ws=wakeup_source_get_next(ws);+continue;+}++for(i=0;i<rcpm->wakeup_cells;i++){+/* We can only OR related bits */+if(value[i+1]){+if(rcpm->little_endian){+tmp=ioread32(rcpm->ippdexpcr_base+i*4);+tmp|=value[i+1];+iowrite32(tmp,rcpm->ippdexpcr_base+i*4);+}else{+tmp=ioread32be(rcpm->ippdexpcr_base+i*4);+tmp|=value[i+1];+iowrite32be(tmp,rcpm->ippdexpcr_base+i*4);+}+}+}+ws=wakeup_source_get_next(ws);+}++return0;+}++staticconststructdev_pm_opsrcpm_pm_ops={+.prepare=rcpm_pm_prepare,+};++staticintrcpm_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structresource*r;+structrcpm*rcpm;+intret;++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);+returnret;+}++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)+returnret;++dev_set_drvdata(&pdev->dev,rcpm);++return0;+}++staticconststructof_device_idrcpm_of_match[]={+{.compatible="fsl,qoriq-rcpm-2.1+",},+{}+};+MODULE_DEVICE_TABLE(of,rcpm_of_match);++staticstructplatform_driverrcpm_driver={+.driver={+.name="rcpm",+.of_match_table=rcpm_of_match,+.pm=&rcpm_pm_ops,+},+.probe=rcpm_probe,+};++module_platform_driver(rcpm_driver);
+static int rcpm_pm_prepare(struct device *dev) {
+ struct device_node *np = dev->of_node;
+ struct wakeup_source *ws;
+ struct rcpm *rcpm;
+ u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1], tmp;
+ int i, ret;
+
+ rcpm = dev_get_drvdata(dev);
+ if (!rcpm)
+ return -EINVAL;
+
+ /* Begin with first registered wakeup source */
+ ws = wakeup_source_get_next(NULL);
+ while (ws) {
while (ws = wakeup_source_get_next(NULL)) ?
Actually, we only pass NULL to wakeup_source_get_next() at very first
call to get 1st wakeup source. Then in the while loop, we will fetch
next source but not 1st, that's different. I am afraid your suggestion
is not quite correct.
From: Pavel Machek <hidden> Date: 2019-05-20 08:56:51
Hi!
quoted
quoted
+static int rcpm_pm_prepare(struct device *dev) {
+ struct device_node *np = dev->of_node;
+ struct wakeup_source *ws;
+ struct rcpm *rcpm;
+ u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1], tmp;
+ int i, ret;
+
+ rcpm = dev_get_drvdata(dev);
+ if (!rcpm)
+ return -EINVAL;
+
+ /* Begin with first registered wakeup source */
+ ws = wakeup_source_get_next(NULL);
+ while (ws) {
while (ws = wakeup_source_get_next(NULL)) ?
Actually, we only pass NULL to wakeup_source_get_next() at very first
call to get 1st wakeup source. Then in the while loop, we will fetch
next source but not 1st, that's different. I am afraid your suggestion
is not quite correct.
Sorry, I seen your next version before seeing this explanation.
You are right, but the current code is "interesting". What about
ws = NULL;
while (ws = wakeup_source_get_next(NULL)) ...
then?
Best regards,
Pavel
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Hi Pavel,
On Monday, May 20, 2019 16:57, Pavel Machek wrote:
Hi!
quoted
quoted
quoted
+static int rcpm_pm_prepare(struct device *dev) {
+ struct device_node *np = dev->of_node;
+ struct wakeup_source *ws;
+ struct rcpm *rcpm;
+ u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1], tmp;
+ int i, ret;
+
+ rcpm = dev_get_drvdata(dev);
+ if (!rcpm)
+ return -EINVAL;
+
+ /* Begin with first registered wakeup source */
+ ws = wakeup_source_get_next(NULL);
+ while (ws) {
while (ws = wakeup_source_get_next(NULL)) ?
Actually, we only pass NULL to wakeup_source_get_next() at very first
call to get 1st wakeup source. Then in the while loop, we will fetch
next source but not 1st, that's different. I am afraid your suggestion
is not quite correct.
Sorry, I seen your next version before seeing this explanation.
You are right, but the current code is "interesting". What about
ws = NULL;
while (ws = wakeup_source_get_next(NULL)) ...
then?
Did you mean:
ws = NULL;
while (ws = wakeup_source_get_next(ws)) ...
Yes, that will be the same to my original logic, do you recommend to change
to this? :)
Regards,
Ran
From: Pavel Machek <hidden> Date: 2019-05-20 09:07:55
On Mon 2019-05-20 09:03:50, Ran Wang wrote:
Hi Pavel,
On Monday, May 20, 2019 16:57, Pavel Machek wrote:
quoted
Hi!
quoted
quoted
quoted
+static int rcpm_pm_prepare(struct device *dev) {
+ struct device_node *np = dev->of_node;
+ struct wakeup_source *ws;
+ struct rcpm *rcpm;
+ u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1], tmp;
+ int i, ret;
+
+ rcpm = dev_get_drvdata(dev);
+ if (!rcpm)
+ return -EINVAL;
+
+ /* Begin with first registered wakeup source */
+ ws = wakeup_source_get_next(NULL);
+ while (ws) {
while (ws = wakeup_source_get_next(NULL)) ?
Actually, we only pass NULL to wakeup_source_get_next() at very first
call to get 1st wakeup source. Then in the while loop, we will fetch
next source but not 1st, that's different. I am afraid your suggestion
is not quite correct.
Sorry, I seen your next version before seeing this explanation.
You are right, but the current code is "interesting". What about
ws = NULL;
while (ws = wakeup_source_get_next(NULL)) ...
then?
Did you mean:
ws = NULL;
while (ws = wakeup_source_get_next(ws)) ...
Yes, that will be the same to my original logic, do you recommend to change
to this? :)
Yes please. It will be less confusing to the reader.
Thanks (and sorry for cross-talk),
Pavel
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Hi Pavel,
On Monday, May 20, 2019 17:08 Pavel Machek wrote:
quoted
quoted
Hi!
quoted
quoted
quoted
+static int rcpm_pm_prepare(struct device *dev) {
+ struct device_node *np = dev->of_node;
+ struct wakeup_source *ws;
+ struct rcpm *rcpm;
+ u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1], tmp;
+ int i, ret;
+
+ rcpm = dev_get_drvdata(dev);
+ if (!rcpm)
+ return -EINVAL;
+
+ /* Begin with first registered wakeup source */
+ ws = wakeup_source_get_next(NULL);
+ while (ws) {
while (ws = wakeup_source_get_next(NULL)) ?
Actually, we only pass NULL to wakeup_source_get_next() at very
first call to get 1st wakeup source. Then in the while loop, we
will fetch next source but not 1st, that's different. I am afraid
your suggestion is not quite correct.
Sorry, I seen your next version before seeing this explanation.
You are right, but the current code is "interesting". What about
ws = NULL;
while (ws = wakeup_source_get_next(NULL)) ...
then?
Did you mean:
ws = NULL;
while (ws = wakeup_source_get_next(ws)) ...
Yes, that will be the same to my original logic, do you recommend
to change to this? :)
Yes please. It will be less confusing to the reader.
OK, if no other comment, I will work out v4, fix this and extra ','
From: Pavel Machek <hidden> Date: 2019-05-20 09:25:03
Hi!
quoted
quoted
quoted
You are right, but the current code is "interesting". What about
ws = NULL;
while (ws = wakeup_source_get_next(NULL)) ...
then?
Did you mean:
ws = NULL;
while (ws = wakeup_source_get_next(ws)) ...
Yes, that will be the same to my original logic, do you recommend
to change to this? :)
Yes please. It will be less confusing to the reader.
OK, if no other comment, I will work out v4, fix this and extra ','
quoted
Thanks (and sorry for cross-talk),
That's OK, thanks for your time.
You can add
Acked-by: Pavel Machek <redacted>
to that version.
Best regards,
Pavel
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany