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