1 // SPDX-License-Identifier: GPL-2.0
3 // rcpm.c - Freescale QorIQ RCPM driver
5 // Copyright 2019-2020 NXP
7 // Author: Ran Wang <ran.wang_1@nxp.com>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/of_address.h>
13 #include <linux/slab.h>
14 #include <linux/suspend.h>
15 #include <linux/kernel.h>
17 #define RCPM_WAKEUP_CELL_MAX_SIZE 7
20 unsigned int wakeup_cells
;
21 void __iomem
*ippdexpcr_base
;
25 #define SCFG_SPARECR8 0x051c
27 static void copy_ippdexpcr1_setting(u32 val
)
29 struct device_node
*np
;
33 np
= of_find_compatible_node(NULL
, NULL
, "fsl,ls1021a-scfg");
37 regs
= of_iomap(np
, 0);
41 reg_val
= ioread32be(regs
+ SCFG_SPARECR8
);
42 iowrite32be(val
| reg_val
, regs
+ SCFG_SPARECR8
);
48 * rcpm_pm_prepare - performs device-level tasks associated with power
49 * management, such as programming related to the wakeup source control.
50 * @dev: Device to handle.
53 static int rcpm_pm_prepare(struct device
*dev
)
57 struct wakeup_source
*ws
;
59 struct device_node
*np
= dev
->of_node
;
60 u32 value
[RCPM_WAKEUP_CELL_MAX_SIZE
+ 1];
61 u32 setting
[RCPM_WAKEUP_CELL_MAX_SIZE
] = {0};
63 rcpm
= dev_get_drvdata(dev
);
67 base
= rcpm
->ippdexpcr_base
;
68 idx
= wakeup_sources_read_lock();
70 /* Begin with first registered wakeup source */
71 for_each_wakeup_source(ws
) {
73 /* skip object which is not attached to device */
74 if (!ws
->dev
|| !ws
->dev
->parent
)
77 ret
= device_property_read_u32_array(ws
->dev
->parent
,
78 "fsl,rcpm-wakeup", value
,
79 rcpm
->wakeup_cells
+ 1);
81 /* Wakeup source should refer to current rcpm device */
82 if (ret
|| (np
->phandle
!= value
[0]))
85 /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
86 * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
87 * of wakeup source IP contains an integer array: <phandle to
88 * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
89 * IPPDEXPCR2 setting, etc>.
91 * So we will go thought them to collect setting data.
93 for (i
= 0; i
< rcpm
->wakeup_cells
; i
++)
94 setting
[i
] |= value
[i
+ 1];
97 wakeup_sources_read_unlock(idx
);
99 /* Program all IPPDEXPCRn once */
100 for (i
= 0; i
< rcpm
->wakeup_cells
; i
++) {
101 u32 tmp
= setting
[i
];
102 void __iomem
*address
= base
+ i
* 4;
107 /* We can only OR related bits */
108 if (rcpm
->little_endian
) {
109 tmp
|= ioread32(address
);
110 iowrite32(tmp
, address
);
112 tmp
|= ioread32be(address
);
113 iowrite32be(tmp
, address
);
116 * Workaround of errata A-008646 on SoC LS1021A:
117 * There is a bug of register ippdexpcr1.
118 * Reading configuration register RCPM_IPPDEXPCR1
119 * always return zero. So save ippdexpcr1's value
120 * to register SCFG_SPARECR8.And the value of
121 * ippdexpcr1 will be read from SCFG_SPARECR8.
123 if (dev_of_node(dev
) && (i
== 1))
124 if (of_device_is_compatible(np
, "fsl,ls1021a-rcpm"))
125 copy_ippdexpcr1_setting(tmp
);
131 static const struct dev_pm_ops rcpm_pm_ops
= {
132 .prepare
= rcpm_pm_prepare
,
135 static int rcpm_probe(struct platform_device
*pdev
)
137 struct device
*dev
= &pdev
->dev
;
142 rcpm
= devm_kzalloc(dev
, sizeof(*rcpm
), GFP_KERNEL
);
146 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
150 rcpm
->ippdexpcr_base
= devm_ioremap_resource(&pdev
->dev
, r
);
151 if (IS_ERR(rcpm
->ippdexpcr_base
)) {
152 ret
= PTR_ERR(rcpm
->ippdexpcr_base
);
156 rcpm
->little_endian
= device_property_read_bool(
157 &pdev
->dev
, "little-endian");
159 ret
= device_property_read_u32(&pdev
->dev
,
160 "#fsl,rcpm-wakeup-cells", &rcpm
->wakeup_cells
);
164 dev_set_drvdata(&pdev
->dev
, rcpm
);
169 static const struct of_device_id rcpm_of_match
[] = {
170 { .compatible
= "fsl,qoriq-rcpm-2.1+", },
173 MODULE_DEVICE_TABLE(of
, rcpm_of_match
);
175 static struct platform_driver rcpm_driver
= {
178 .of_match_table
= rcpm_of_match
,
184 module_platform_driver(rcpm_driver
);