xref: /OK3568_Linux_fs/kernel/drivers/soc/bcm/bcm2835-power.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Power domain driver for Broadcom BCM2835
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2018 Broadcom
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <dt-bindings/soc/bcm2835-pm.h>
9*4882a593Smuzhiyun #include <linux/clk.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/io.h>
12*4882a593Smuzhiyun #include <linux/mfd/bcm2835-pm.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/pm_domain.h>
16*4882a593Smuzhiyun #include <linux/reset-controller.h>
17*4882a593Smuzhiyun #include <linux/types.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define PM_GNRIC                        0x00
20*4882a593Smuzhiyun #define PM_AUDIO                        0x04
21*4882a593Smuzhiyun #define PM_STATUS                       0x18
22*4882a593Smuzhiyun #define PM_RSTC				0x1c
23*4882a593Smuzhiyun #define PM_RSTS				0x20
24*4882a593Smuzhiyun #define PM_WDOG				0x24
25*4882a593Smuzhiyun #define PM_PADS0			0x28
26*4882a593Smuzhiyun #define PM_PADS2			0x2c
27*4882a593Smuzhiyun #define PM_PADS3			0x30
28*4882a593Smuzhiyun #define PM_PADS4			0x34
29*4882a593Smuzhiyun #define PM_PADS5			0x38
30*4882a593Smuzhiyun #define PM_PADS6			0x3c
31*4882a593Smuzhiyun #define PM_CAM0				0x44
32*4882a593Smuzhiyun #define PM_CAM0_LDOHPEN			BIT(2)
33*4882a593Smuzhiyun #define PM_CAM0_LDOLPEN			BIT(1)
34*4882a593Smuzhiyun #define PM_CAM0_CTRLEN			BIT(0)
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define PM_CAM1				0x48
37*4882a593Smuzhiyun #define PM_CAM1_LDOHPEN			BIT(2)
38*4882a593Smuzhiyun #define PM_CAM1_LDOLPEN			BIT(1)
39*4882a593Smuzhiyun #define PM_CAM1_CTRLEN			BIT(0)
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #define PM_CCP2TX			0x4c
42*4882a593Smuzhiyun #define PM_CCP2TX_LDOEN			BIT(1)
43*4882a593Smuzhiyun #define PM_CCP2TX_CTRLEN		BIT(0)
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define PM_DSI0				0x50
46*4882a593Smuzhiyun #define PM_DSI0_LDOHPEN			BIT(2)
47*4882a593Smuzhiyun #define PM_DSI0_LDOLPEN			BIT(1)
48*4882a593Smuzhiyun #define PM_DSI0_CTRLEN			BIT(0)
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #define PM_DSI1				0x54
51*4882a593Smuzhiyun #define PM_DSI1_LDOHPEN			BIT(2)
52*4882a593Smuzhiyun #define PM_DSI1_LDOLPEN			BIT(1)
53*4882a593Smuzhiyun #define PM_DSI1_CTRLEN			BIT(0)
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #define PM_HDMI				0x58
56*4882a593Smuzhiyun #define PM_HDMI_RSTDR			BIT(19)
57*4882a593Smuzhiyun #define PM_HDMI_LDOPD			BIT(1)
58*4882a593Smuzhiyun #define PM_HDMI_CTRLEN			BIT(0)
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun #define PM_USB				0x5c
61*4882a593Smuzhiyun /* The power gates must be enabled with this bit before enabling the LDO in the
62*4882a593Smuzhiyun  * USB block.
63*4882a593Smuzhiyun  */
64*4882a593Smuzhiyun #define PM_USB_CTRLEN			BIT(0)
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun #define PM_PXLDO			0x60
67*4882a593Smuzhiyun #define PM_PXBG				0x64
68*4882a593Smuzhiyun #define PM_DFT				0x68
69*4882a593Smuzhiyun #define PM_SMPS				0x6c
70*4882a593Smuzhiyun #define PM_XOSC				0x70
71*4882a593Smuzhiyun #define PM_SPAREW			0x74
72*4882a593Smuzhiyun #define PM_SPARER			0x78
73*4882a593Smuzhiyun #define PM_AVS_RSTDR			0x7c
74*4882a593Smuzhiyun #define PM_AVS_STAT			0x80
75*4882a593Smuzhiyun #define PM_AVS_EVENT			0x84
76*4882a593Smuzhiyun #define PM_AVS_INTEN			0x88
77*4882a593Smuzhiyun #define PM_DUMMY			0xfc
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #define PM_IMAGE			0x108
80*4882a593Smuzhiyun #define PM_GRAFX			0x10c
81*4882a593Smuzhiyun #define PM_PROC				0x110
82*4882a593Smuzhiyun #define PM_ENAB				BIT(12)
83*4882a593Smuzhiyun #define PM_ISPRSTN			BIT(8)
84*4882a593Smuzhiyun #define PM_H264RSTN			BIT(7)
85*4882a593Smuzhiyun #define PM_PERIRSTN			BIT(6)
86*4882a593Smuzhiyun #define PM_V3DRSTN			BIT(6)
87*4882a593Smuzhiyun #define PM_ISFUNC			BIT(5)
88*4882a593Smuzhiyun #define PM_MRDONE			BIT(4)
89*4882a593Smuzhiyun #define PM_MEMREP			BIT(3)
90*4882a593Smuzhiyun #define PM_ISPOW			BIT(2)
91*4882a593Smuzhiyun #define PM_POWOK			BIT(1)
92*4882a593Smuzhiyun #define PM_POWUP			BIT(0)
93*4882a593Smuzhiyun #define PM_INRUSH_SHIFT			13
94*4882a593Smuzhiyun #define PM_INRUSH_3_5_MA		0
95*4882a593Smuzhiyun #define PM_INRUSH_5_MA			1
96*4882a593Smuzhiyun #define PM_INRUSH_10_MA			2
97*4882a593Smuzhiyun #define PM_INRUSH_20_MA			3
98*4882a593Smuzhiyun #define PM_INRUSH_MASK			(3 << PM_INRUSH_SHIFT)
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun #define PM_PASSWORD			0x5a000000
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun #define PM_WDOG_TIME_SET		0x000fffff
103*4882a593Smuzhiyun #define PM_RSTC_WRCFG_CLR		0xffffffcf
104*4882a593Smuzhiyun #define PM_RSTS_HADWRH_SET		0x00000040
105*4882a593Smuzhiyun #define PM_RSTC_WRCFG_SET		0x00000030
106*4882a593Smuzhiyun #define PM_RSTC_WRCFG_FULL_RESET	0x00000020
107*4882a593Smuzhiyun #define PM_RSTC_RESET			0x00000102
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun #define PM_READ(reg) readl(power->base + (reg))
110*4882a593Smuzhiyun #define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg))
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun #define ASB_BRDG_VERSION                0x00
113*4882a593Smuzhiyun #define ASB_CPR_CTRL                    0x04
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun #define ASB_V3D_S_CTRL			0x08
116*4882a593Smuzhiyun #define ASB_V3D_M_CTRL			0x0c
117*4882a593Smuzhiyun #define ASB_ISP_S_CTRL			0x10
118*4882a593Smuzhiyun #define ASB_ISP_M_CTRL			0x14
119*4882a593Smuzhiyun #define ASB_H264_S_CTRL			0x18
120*4882a593Smuzhiyun #define ASB_H264_M_CTRL			0x1c
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun #define ASB_REQ_STOP                    BIT(0)
123*4882a593Smuzhiyun #define ASB_ACK                         BIT(1)
124*4882a593Smuzhiyun #define ASB_EMPTY                       BIT(2)
125*4882a593Smuzhiyun #define ASB_FULL                        BIT(3)
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun #define ASB_AXI_BRDG_ID			0x20
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun #define ASB_READ(reg) readl(power->asb + (reg))
130*4882a593Smuzhiyun #define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg))
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun struct bcm2835_power_domain {
133*4882a593Smuzhiyun 	struct generic_pm_domain base;
134*4882a593Smuzhiyun 	struct bcm2835_power *power;
135*4882a593Smuzhiyun 	u32 domain;
136*4882a593Smuzhiyun 	struct clk *clk;
137*4882a593Smuzhiyun };
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun struct bcm2835_power {
140*4882a593Smuzhiyun 	struct device		*dev;
141*4882a593Smuzhiyun 	/* PM registers. */
142*4882a593Smuzhiyun 	void __iomem		*base;
143*4882a593Smuzhiyun 	/* AXI Async bridge registers. */
144*4882a593Smuzhiyun 	void __iomem		*asb;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	struct genpd_onecell_data pd_xlate;
147*4882a593Smuzhiyun 	struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
148*4882a593Smuzhiyun 	struct reset_controller_dev reset;
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun 
bcm2835_asb_enable(struct bcm2835_power * power,u32 reg)151*4882a593Smuzhiyun static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	u64 start;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	if (!reg)
156*4882a593Smuzhiyun 		return 0;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	start = ktime_get_ns();
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	/* Enable the module's async AXI bridges. */
161*4882a593Smuzhiyun 	ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP);
162*4882a593Smuzhiyun 	while (ASB_READ(reg) & ASB_ACK) {
163*4882a593Smuzhiyun 		cpu_relax();
164*4882a593Smuzhiyun 		if (ktime_get_ns() - start >= 1000)
165*4882a593Smuzhiyun 			return -ETIMEDOUT;
166*4882a593Smuzhiyun 	}
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
bcm2835_asb_disable(struct bcm2835_power * power,u32 reg)171*4882a593Smuzhiyun static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	u64 start;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (!reg)
176*4882a593Smuzhiyun 		return 0;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	start = ktime_get_ns();
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	/* Enable the module's async AXI bridges. */
181*4882a593Smuzhiyun 	ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP);
182*4882a593Smuzhiyun 	while (!(ASB_READ(reg) & ASB_ACK)) {
183*4882a593Smuzhiyun 		cpu_relax();
184*4882a593Smuzhiyun 		if (ktime_get_ns() - start >= 1000)
185*4882a593Smuzhiyun 			return -ETIMEDOUT;
186*4882a593Smuzhiyun 	}
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	return 0;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun 
bcm2835_power_power_off(struct bcm2835_power_domain * pd,u32 pm_reg)191*4882a593Smuzhiyun static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	struct bcm2835_power *power = pd->power;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	/* Enable functional isolation */
196*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	/* Enable electrical isolation */
199*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	/* Open the power switches. */
202*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	return 0;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
bcm2835_power_power_on(struct bcm2835_power_domain * pd,u32 pm_reg)207*4882a593Smuzhiyun static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	struct bcm2835_power *power = pd->power;
210*4882a593Smuzhiyun 	struct device *dev = power->dev;
211*4882a593Smuzhiyun 	u64 start;
212*4882a593Smuzhiyun 	int ret;
213*4882a593Smuzhiyun 	int inrush;
214*4882a593Smuzhiyun 	bool powok;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	/* If it was already powered on by the fw, leave it that way. */
217*4882a593Smuzhiyun 	if (PM_READ(pm_reg) & PM_POWUP)
218*4882a593Smuzhiyun 		return 0;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	/* Enable power.  Allowing too much current at once may result
221*4882a593Smuzhiyun 	 * in POWOK never getting set, so start low and ramp it up as
222*4882a593Smuzhiyun 	 * necessary to succeed.
223*4882a593Smuzhiyun 	 */
224*4882a593Smuzhiyun 	powok = false;
225*4882a593Smuzhiyun 	for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) {
226*4882a593Smuzhiyun 		PM_WRITE(pm_reg,
227*4882a593Smuzhiyun 			 (PM_READ(pm_reg) & ~PM_INRUSH_MASK) |
228*4882a593Smuzhiyun 			 (inrush << PM_INRUSH_SHIFT) |
229*4882a593Smuzhiyun 			 PM_POWUP);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 		start = ktime_get_ns();
232*4882a593Smuzhiyun 		while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) {
233*4882a593Smuzhiyun 			cpu_relax();
234*4882a593Smuzhiyun 			if (ktime_get_ns() - start >= 3000)
235*4882a593Smuzhiyun 				break;
236*4882a593Smuzhiyun 		}
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 	if (!powok) {
239*4882a593Smuzhiyun 		dev_err(dev, "Timeout waiting for %s power OK\n",
240*4882a593Smuzhiyun 			pd->base.name);
241*4882a593Smuzhiyun 		ret = -ETIMEDOUT;
242*4882a593Smuzhiyun 		goto err_disable_powup;
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	/* Disable electrical isolation */
246*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	/* Repair memory */
249*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP);
250*4882a593Smuzhiyun 	start = ktime_get_ns();
251*4882a593Smuzhiyun 	while (!(PM_READ(pm_reg) & PM_MRDONE)) {
252*4882a593Smuzhiyun 		cpu_relax();
253*4882a593Smuzhiyun 		if (ktime_get_ns() - start >= 1000) {
254*4882a593Smuzhiyun 			dev_err(dev, "Timeout waiting for %s memory repair\n",
255*4882a593Smuzhiyun 				pd->base.name);
256*4882a593Smuzhiyun 			ret = -ETIMEDOUT;
257*4882a593Smuzhiyun 			goto err_disable_ispow;
258*4882a593Smuzhiyun 		}
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	/* Disable functional isolation */
262*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	return 0;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun err_disable_ispow:
267*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW);
268*4882a593Smuzhiyun err_disable_powup:
269*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK));
270*4882a593Smuzhiyun 	return ret;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
bcm2835_asb_power_on(struct bcm2835_power_domain * pd,u32 pm_reg,u32 asb_m_reg,u32 asb_s_reg,u32 reset_flags)273*4882a593Smuzhiyun static int bcm2835_asb_power_on(struct bcm2835_power_domain *pd,
274*4882a593Smuzhiyun 				u32 pm_reg,
275*4882a593Smuzhiyun 				u32 asb_m_reg,
276*4882a593Smuzhiyun 				u32 asb_s_reg,
277*4882a593Smuzhiyun 				u32 reset_flags)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	struct bcm2835_power *power = pd->power;
280*4882a593Smuzhiyun 	int ret;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	ret = clk_prepare_enable(pd->clk);
283*4882a593Smuzhiyun 	if (ret) {
284*4882a593Smuzhiyun 		dev_err(power->dev, "Failed to enable clock for %s\n",
285*4882a593Smuzhiyun 			pd->base.name);
286*4882a593Smuzhiyun 		return ret;
287*4882a593Smuzhiyun 	}
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	/* Wait 32 clocks for reset to propagate, 1 us will be enough */
290*4882a593Smuzhiyun 	udelay(1);
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	clk_disable_unprepare(pd->clk);
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	/* Deassert the resets. */
295*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	ret = clk_prepare_enable(pd->clk);
298*4882a593Smuzhiyun 	if (ret) {
299*4882a593Smuzhiyun 		dev_err(power->dev, "Failed to enable clock for %s\n",
300*4882a593Smuzhiyun 			pd->base.name);
301*4882a593Smuzhiyun 		goto err_enable_resets;
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	ret = bcm2835_asb_enable(power, asb_m_reg);
305*4882a593Smuzhiyun 	if (ret) {
306*4882a593Smuzhiyun 		dev_err(power->dev, "Failed to enable ASB master for %s\n",
307*4882a593Smuzhiyun 			pd->base.name);
308*4882a593Smuzhiyun 		goto err_disable_clk;
309*4882a593Smuzhiyun 	}
310*4882a593Smuzhiyun 	ret = bcm2835_asb_enable(power, asb_s_reg);
311*4882a593Smuzhiyun 	if (ret) {
312*4882a593Smuzhiyun 		dev_err(power->dev, "Failed to enable ASB slave for %s\n",
313*4882a593Smuzhiyun 			pd->base.name);
314*4882a593Smuzhiyun 		goto err_disable_asb_master;
315*4882a593Smuzhiyun 	}
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	return 0;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun err_disable_asb_master:
320*4882a593Smuzhiyun 	bcm2835_asb_disable(power, asb_m_reg);
321*4882a593Smuzhiyun err_disable_clk:
322*4882a593Smuzhiyun 	clk_disable_unprepare(pd->clk);
323*4882a593Smuzhiyun err_enable_resets:
324*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags);
325*4882a593Smuzhiyun 	return ret;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun 
bcm2835_asb_power_off(struct bcm2835_power_domain * pd,u32 pm_reg,u32 asb_m_reg,u32 asb_s_reg,u32 reset_flags)328*4882a593Smuzhiyun static int bcm2835_asb_power_off(struct bcm2835_power_domain *pd,
329*4882a593Smuzhiyun 				 u32 pm_reg,
330*4882a593Smuzhiyun 				 u32 asb_m_reg,
331*4882a593Smuzhiyun 				 u32 asb_s_reg,
332*4882a593Smuzhiyun 				 u32 reset_flags)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	struct bcm2835_power *power = pd->power;
335*4882a593Smuzhiyun 	int ret;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	ret = bcm2835_asb_disable(power, asb_s_reg);
338*4882a593Smuzhiyun 	if (ret) {
339*4882a593Smuzhiyun 		dev_warn(power->dev, "Failed to disable ASB slave for %s\n",
340*4882a593Smuzhiyun 			 pd->base.name);
341*4882a593Smuzhiyun 		return ret;
342*4882a593Smuzhiyun 	}
343*4882a593Smuzhiyun 	ret = bcm2835_asb_disable(power, asb_m_reg);
344*4882a593Smuzhiyun 	if (ret) {
345*4882a593Smuzhiyun 		dev_warn(power->dev, "Failed to disable ASB master for %s\n",
346*4882a593Smuzhiyun 			 pd->base.name);
347*4882a593Smuzhiyun 		bcm2835_asb_enable(power, asb_s_reg);
348*4882a593Smuzhiyun 		return ret;
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	clk_disable_unprepare(pd->clk);
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	/* Assert the resets. */
354*4882a593Smuzhiyun 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	return 0;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun 
bcm2835_power_pd_power_on(struct generic_pm_domain * domain)359*4882a593Smuzhiyun static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	struct bcm2835_power_domain *pd =
362*4882a593Smuzhiyun 		container_of(domain, struct bcm2835_power_domain, base);
363*4882a593Smuzhiyun 	struct bcm2835_power *power = pd->power;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	switch (pd->domain) {
366*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_GRAFX:
367*4882a593Smuzhiyun 		return bcm2835_power_power_on(pd, PM_GRAFX);
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_GRAFX_V3D:
370*4882a593Smuzhiyun 		return bcm2835_asb_power_on(pd, PM_GRAFX,
371*4882a593Smuzhiyun 					    ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
372*4882a593Smuzhiyun 					    PM_V3DRSTN);
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE:
375*4882a593Smuzhiyun 		return bcm2835_power_power_on(pd, PM_IMAGE);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE_PERI:
378*4882a593Smuzhiyun 		return bcm2835_asb_power_on(pd, PM_IMAGE,
379*4882a593Smuzhiyun 					    0, 0,
380*4882a593Smuzhiyun 					    PM_PERIRSTN);
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE_ISP:
383*4882a593Smuzhiyun 		return bcm2835_asb_power_on(pd, PM_IMAGE,
384*4882a593Smuzhiyun 					    ASB_ISP_M_CTRL, ASB_ISP_S_CTRL,
385*4882a593Smuzhiyun 					    PM_ISPRSTN);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE_H264:
388*4882a593Smuzhiyun 		return bcm2835_asb_power_on(pd, PM_IMAGE,
389*4882a593Smuzhiyun 					    ASB_H264_M_CTRL, ASB_H264_S_CTRL,
390*4882a593Smuzhiyun 					    PM_H264RSTN);
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_USB:
393*4882a593Smuzhiyun 		PM_WRITE(PM_USB, PM_USB_CTRLEN);
394*4882a593Smuzhiyun 		return 0;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_DSI0:
397*4882a593Smuzhiyun 		PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN);
398*4882a593Smuzhiyun 		PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN);
399*4882a593Smuzhiyun 		return 0;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_DSI1:
402*4882a593Smuzhiyun 		PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN);
403*4882a593Smuzhiyun 		PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN);
404*4882a593Smuzhiyun 		return 0;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_CCP2TX:
407*4882a593Smuzhiyun 		PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN);
408*4882a593Smuzhiyun 		PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN);
409*4882a593Smuzhiyun 		return 0;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_HDMI:
412*4882a593Smuzhiyun 		PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR);
413*4882a593Smuzhiyun 		PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN);
414*4882a593Smuzhiyun 		PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD);
415*4882a593Smuzhiyun 		usleep_range(100, 200);
416*4882a593Smuzhiyun 		PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR);
417*4882a593Smuzhiyun 		return 0;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	default:
420*4882a593Smuzhiyun 		dev_err(power->dev, "Invalid domain %d\n", pd->domain);
421*4882a593Smuzhiyun 		return -EINVAL;
422*4882a593Smuzhiyun 	}
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun 
bcm2835_power_pd_power_off(struct generic_pm_domain * domain)425*4882a593Smuzhiyun static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct bcm2835_power_domain *pd =
428*4882a593Smuzhiyun 		container_of(domain, struct bcm2835_power_domain, base);
429*4882a593Smuzhiyun 	struct bcm2835_power *power = pd->power;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	switch (pd->domain) {
432*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_GRAFX:
433*4882a593Smuzhiyun 		return bcm2835_power_power_off(pd, PM_GRAFX);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_GRAFX_V3D:
436*4882a593Smuzhiyun 		return bcm2835_asb_power_off(pd, PM_GRAFX,
437*4882a593Smuzhiyun 					     ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
438*4882a593Smuzhiyun 					     PM_V3DRSTN);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE:
441*4882a593Smuzhiyun 		return bcm2835_power_power_off(pd, PM_IMAGE);
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE_PERI:
444*4882a593Smuzhiyun 		return bcm2835_asb_power_off(pd, PM_IMAGE,
445*4882a593Smuzhiyun 					     0, 0,
446*4882a593Smuzhiyun 					     PM_PERIRSTN);
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE_ISP:
449*4882a593Smuzhiyun 		return bcm2835_asb_power_off(pd, PM_IMAGE,
450*4882a593Smuzhiyun 					     ASB_ISP_M_CTRL, ASB_ISP_S_CTRL,
451*4882a593Smuzhiyun 					     PM_ISPRSTN);
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_IMAGE_H264:
454*4882a593Smuzhiyun 		return bcm2835_asb_power_off(pd, PM_IMAGE,
455*4882a593Smuzhiyun 					     ASB_H264_M_CTRL, ASB_H264_S_CTRL,
456*4882a593Smuzhiyun 					     PM_H264RSTN);
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_USB:
459*4882a593Smuzhiyun 		PM_WRITE(PM_USB, 0);
460*4882a593Smuzhiyun 		return 0;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_DSI0:
463*4882a593Smuzhiyun 		PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN);
464*4882a593Smuzhiyun 		PM_WRITE(PM_DSI0, 0);
465*4882a593Smuzhiyun 		return 0;
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_DSI1:
468*4882a593Smuzhiyun 		PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN);
469*4882a593Smuzhiyun 		PM_WRITE(PM_DSI1, 0);
470*4882a593Smuzhiyun 		return 0;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_CCP2TX:
473*4882a593Smuzhiyun 		PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN);
474*4882a593Smuzhiyun 		PM_WRITE(PM_CCP2TX, 0);
475*4882a593Smuzhiyun 		return 0;
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	case BCM2835_POWER_DOMAIN_HDMI:
478*4882a593Smuzhiyun 		PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD);
479*4882a593Smuzhiyun 		PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN);
480*4882a593Smuzhiyun 		return 0;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	default:
483*4882a593Smuzhiyun 		dev_err(power->dev, "Invalid domain %d\n", pd->domain);
484*4882a593Smuzhiyun 		return -EINVAL;
485*4882a593Smuzhiyun 	}
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun static int
bcm2835_init_power_domain(struct bcm2835_power * power,int pd_xlate_index,const char * name)489*4882a593Smuzhiyun bcm2835_init_power_domain(struct bcm2835_power *power,
490*4882a593Smuzhiyun 			  int pd_xlate_index, const char *name)
491*4882a593Smuzhiyun {
492*4882a593Smuzhiyun 	struct device *dev = power->dev;
493*4882a593Smuzhiyun 	struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index];
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	dom->clk = devm_clk_get(dev->parent, name);
496*4882a593Smuzhiyun 	if (IS_ERR(dom->clk)) {
497*4882a593Smuzhiyun 		int ret = PTR_ERR(dom->clk);
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 		if (ret == -EPROBE_DEFER)
500*4882a593Smuzhiyun 			return ret;
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 		/* Some domains don't have a clk, so make sure that we
503*4882a593Smuzhiyun 		 * don't deref an error pointer later.
504*4882a593Smuzhiyun 		 */
505*4882a593Smuzhiyun 		dom->clk = NULL;
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	dom->base.name = name;
509*4882a593Smuzhiyun 	dom->base.power_on = bcm2835_power_pd_power_on;
510*4882a593Smuzhiyun 	dom->base.power_off = bcm2835_power_pd_power_off;
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	dom->domain = pd_xlate_index;
513*4882a593Smuzhiyun 	dom->power = power;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	/* XXX: on/off at boot? */
516*4882a593Smuzhiyun 	pm_genpd_init(&dom->base, NULL, true);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	power->pd_xlate.domains[pd_xlate_index] = &dom->base;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	return 0;
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun /** bcm2835_reset_reset - Resets a block that has a reset line in the
524*4882a593Smuzhiyun  * PM block.
525*4882a593Smuzhiyun  *
526*4882a593Smuzhiyun  * The consumer of the reset controller must have the power domain up
527*4882a593Smuzhiyun  * -- there's no reset ability with the power domain down.  To reset
528*4882a593Smuzhiyun  * the sub-block, we just disable its access to memory through the
529*4882a593Smuzhiyun  * ASB, reset, and re-enable.
530*4882a593Smuzhiyun  */
bcm2835_reset_reset(struct reset_controller_dev * rcdev,unsigned long id)531*4882a593Smuzhiyun static int bcm2835_reset_reset(struct reset_controller_dev *rcdev,
532*4882a593Smuzhiyun 			       unsigned long id)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 	struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power,
535*4882a593Smuzhiyun 						   reset);
536*4882a593Smuzhiyun 	struct bcm2835_power_domain *pd;
537*4882a593Smuzhiyun 	int ret;
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	switch (id) {
540*4882a593Smuzhiyun 	case BCM2835_RESET_V3D:
541*4882a593Smuzhiyun 		pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D];
542*4882a593Smuzhiyun 		break;
543*4882a593Smuzhiyun 	case BCM2835_RESET_H264:
544*4882a593Smuzhiyun 		pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264];
545*4882a593Smuzhiyun 		break;
546*4882a593Smuzhiyun 	case BCM2835_RESET_ISP:
547*4882a593Smuzhiyun 		pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP];
548*4882a593Smuzhiyun 		break;
549*4882a593Smuzhiyun 	default:
550*4882a593Smuzhiyun 		dev_err(power->dev, "Bad reset id %ld\n", id);
551*4882a593Smuzhiyun 		return -EINVAL;
552*4882a593Smuzhiyun 	}
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	ret = bcm2835_power_pd_power_off(&pd->base);
555*4882a593Smuzhiyun 	if (ret)
556*4882a593Smuzhiyun 		return ret;
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	return bcm2835_power_pd_power_on(&pd->base);
559*4882a593Smuzhiyun }
560*4882a593Smuzhiyun 
bcm2835_reset_status(struct reset_controller_dev * rcdev,unsigned long id)561*4882a593Smuzhiyun static int bcm2835_reset_status(struct reset_controller_dev *rcdev,
562*4882a593Smuzhiyun 				unsigned long id)
563*4882a593Smuzhiyun {
564*4882a593Smuzhiyun 	struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power,
565*4882a593Smuzhiyun 						   reset);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	switch (id) {
568*4882a593Smuzhiyun 	case BCM2835_RESET_V3D:
569*4882a593Smuzhiyun 		return !PM_READ(PM_GRAFX & PM_V3DRSTN);
570*4882a593Smuzhiyun 	case BCM2835_RESET_H264:
571*4882a593Smuzhiyun 		return !PM_READ(PM_IMAGE & PM_H264RSTN);
572*4882a593Smuzhiyun 	case BCM2835_RESET_ISP:
573*4882a593Smuzhiyun 		return !PM_READ(PM_IMAGE & PM_ISPRSTN);
574*4882a593Smuzhiyun 	default:
575*4882a593Smuzhiyun 		return -EINVAL;
576*4882a593Smuzhiyun 	}
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun static const struct reset_control_ops bcm2835_reset_ops = {
580*4882a593Smuzhiyun 	.reset = bcm2835_reset_reset,
581*4882a593Smuzhiyun 	.status = bcm2835_reset_status,
582*4882a593Smuzhiyun };
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun static const char *const power_domain_names[] = {
585*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_GRAFX] = "grafx",
586*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d",
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_IMAGE] = "image",
589*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image",
590*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264",
591*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp",
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_USB] = "usb",
594*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_DSI0] = "dsi0",
595*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_DSI1] = "dsi1",
596*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_CAM0] = "cam0",
597*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_CAM1] = "cam1",
598*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx",
599*4882a593Smuzhiyun 	[BCM2835_POWER_DOMAIN_HDMI] = "hdmi",
600*4882a593Smuzhiyun };
601*4882a593Smuzhiyun 
bcm2835_power_probe(struct platform_device * pdev)602*4882a593Smuzhiyun static int bcm2835_power_probe(struct platform_device *pdev)
603*4882a593Smuzhiyun {
604*4882a593Smuzhiyun 	struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent);
605*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
606*4882a593Smuzhiyun 	struct bcm2835_power *power;
607*4882a593Smuzhiyun 	static const struct {
608*4882a593Smuzhiyun 		int parent, child;
609*4882a593Smuzhiyun 	} domain_deps[] = {
610*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D },
611*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI },
612*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 },
613*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP },
614*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB },
615*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 },
616*4882a593Smuzhiyun 		{ BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 },
617*4882a593Smuzhiyun 	};
618*4882a593Smuzhiyun 	int ret = 0, i;
619*4882a593Smuzhiyun 	u32 id;
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 	power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
622*4882a593Smuzhiyun 	if (!power)
623*4882a593Smuzhiyun 		return -ENOMEM;
624*4882a593Smuzhiyun 	platform_set_drvdata(pdev, power);
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	power->dev = dev;
627*4882a593Smuzhiyun 	power->base = pm->base;
628*4882a593Smuzhiyun 	power->asb = pm->asb;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	id = ASB_READ(ASB_AXI_BRDG_ID);
631*4882a593Smuzhiyun 	if (id != 0x62726467 /* "BRDG" */) {
632*4882a593Smuzhiyun 		dev_err(dev, "ASB register ID returned 0x%08x\n", id);
633*4882a593Smuzhiyun 		return -ENODEV;
634*4882a593Smuzhiyun 	}
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	power->pd_xlate.domains = devm_kcalloc(dev,
637*4882a593Smuzhiyun 					       ARRAY_SIZE(power_domain_names),
638*4882a593Smuzhiyun 					       sizeof(*power->pd_xlate.domains),
639*4882a593Smuzhiyun 					       GFP_KERNEL);
640*4882a593Smuzhiyun 	if (!power->pd_xlate.domains)
641*4882a593Smuzhiyun 		return -ENOMEM;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names);
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) {
646*4882a593Smuzhiyun 		ret = bcm2835_init_power_domain(power, i, power_domain_names[i]);
647*4882a593Smuzhiyun 		if (ret)
648*4882a593Smuzhiyun 			goto fail;
649*4882a593Smuzhiyun 	}
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(domain_deps); i++) {
652*4882a593Smuzhiyun 		pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base,
653*4882a593Smuzhiyun 				       &power->domains[domain_deps[i].child].base);
654*4882a593Smuzhiyun 	}
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	power->reset.owner = THIS_MODULE;
657*4882a593Smuzhiyun 	power->reset.nr_resets = BCM2835_RESET_COUNT;
658*4882a593Smuzhiyun 	power->reset.ops = &bcm2835_reset_ops;
659*4882a593Smuzhiyun 	power->reset.of_node = dev->parent->of_node;
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	ret = devm_reset_controller_register(dev, &power->reset);
662*4882a593Smuzhiyun 	if (ret)
663*4882a593Smuzhiyun 		goto fail;
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate);
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	dev_info(dev, "Broadcom BCM2835 power domains driver");
668*4882a593Smuzhiyun 	return 0;
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun fail:
671*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) {
672*4882a593Smuzhiyun 		struct generic_pm_domain *dom = &power->domains[i].base;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 		if (dom->name)
675*4882a593Smuzhiyun 			pm_genpd_remove(dom);
676*4882a593Smuzhiyun 	}
677*4882a593Smuzhiyun 	return ret;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun 
bcm2835_power_remove(struct platform_device * pdev)680*4882a593Smuzhiyun static int bcm2835_power_remove(struct platform_device *pdev)
681*4882a593Smuzhiyun {
682*4882a593Smuzhiyun 	return 0;
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun static struct platform_driver bcm2835_power_driver = {
686*4882a593Smuzhiyun 	.probe		= bcm2835_power_probe,
687*4882a593Smuzhiyun 	.remove		= bcm2835_power_remove,
688*4882a593Smuzhiyun 	.driver = {
689*4882a593Smuzhiyun 		.name =	"bcm2835-power",
690*4882a593Smuzhiyun 	},
691*4882a593Smuzhiyun };
692*4882a593Smuzhiyun module_platform_driver(bcm2835_power_driver);
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
695*4882a593Smuzhiyun MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset");
696*4882a593Smuzhiyun MODULE_LICENSE("GPL");
697