1 // SPDX-License-Identifier: GPL-2.0
2 /* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
5 * Alexander Aring <aar@pengutronix.de>
6 * Eric Anholt <eric@anholt.net>
9 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm_domain.h>
13 #include <dt-bindings/power/raspberrypi-power.h>
14 #include <soc/bcm2835/raspberrypi-firmware.h>
17 * Firmware indices for the old power domains interface. Only a few
18 * of them were actually implemented.
20 #define RPI_OLD_POWER_DOMAIN_USB 3
21 #define RPI_OLD_POWER_DOMAIN_V3D 10
23 struct rpi_power_domain
{
27 struct generic_pm_domain base
;
28 struct rpi_firmware
*fw
;
31 struct rpi_power_domains
{
32 bool has_new_interface
;
33 struct genpd_onecell_data xlate
;
34 struct rpi_firmware
*fw
;
35 struct rpi_power_domain domains
[RPI_POWER_DOMAIN_COUNT
];
39 * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and
40 * RPI_FIRMWARE_SET_DOMAIN_STATE
42 struct rpi_power_domain_packet
{
48 * Asks the firmware to enable or disable power on a specific power
51 static int rpi_firmware_set_power(struct generic_pm_domain
*domain
, bool on
)
53 struct rpi_power_domain
*rpi_domain
=
54 container_of(domain
, struct rpi_power_domain
, base
);
55 bool old_interface
= rpi_domain
->old_interface
;
56 struct rpi_power_domain_packet packet
;
59 packet
.domain
= rpi_domain
->domain
;
62 ret
= rpi_firmware_property(rpi_domain
->fw
, old_interface
?
63 RPI_FIRMWARE_SET_POWER_STATE
:
64 RPI_FIRMWARE_SET_DOMAIN_STATE
,
65 &packet
, sizeof(packet
));
67 dev_err(&domain
->dev
, "Failed to set %s to %u (%d)\n",
68 old_interface
? "power" : "domain", on
, ret
);
70 dev_dbg(&domain
->dev
, "Set %s to %u\n",
71 old_interface
? "power" : "domain", on
);
76 static int rpi_domain_off(struct generic_pm_domain
*domain
)
78 return rpi_firmware_set_power(domain
, false);
81 static int rpi_domain_on(struct generic_pm_domain
*domain
)
83 return rpi_firmware_set_power(domain
, true);
86 static void rpi_common_init_power_domain(struct rpi_power_domains
*rpi_domains
,
87 int xlate_index
, const char *name
)
89 struct rpi_power_domain
*dom
= &rpi_domains
->domains
[xlate_index
];
91 dom
->fw
= rpi_domains
->fw
;
93 dom
->base
.name
= name
;
94 dom
->base
.flags
= GENPD_FLAG_ACTIVE_WAKEUP
;
95 dom
->base
.power_on
= rpi_domain_on
;
96 dom
->base
.power_off
= rpi_domain_off
;
99 * Treat all power domains as off at boot.
101 * The firmware itself may be keeping some domains on, but
102 * from Linux's perspective all we control is the refcounts
103 * that we give to the firmware, and we can't ask the firmware
104 * to turn off something that we haven't ourselves turned on.
106 pm_genpd_init(&dom
->base
, NULL
, true);
108 rpi_domains
->xlate
.domains
[xlate_index
] = &dom
->base
;
111 static void rpi_init_power_domain(struct rpi_power_domains
*rpi_domains
,
112 int xlate_index
, const char *name
)
114 struct rpi_power_domain
*dom
= &rpi_domains
->domains
[xlate_index
];
116 if (!rpi_domains
->has_new_interface
)
119 /* The DT binding index is the firmware's domain index minus one. */
120 dom
->domain
= xlate_index
+ 1;
122 rpi_common_init_power_domain(rpi_domains
, xlate_index
, name
);
125 static void rpi_init_old_power_domain(struct rpi_power_domains
*rpi_domains
,
126 int xlate_index
, int domain
,
129 struct rpi_power_domain
*dom
= &rpi_domains
->domains
[xlate_index
];
131 dom
->old_interface
= true;
132 dom
->domain
= domain
;
134 rpi_common_init_power_domain(rpi_domains
, xlate_index
, name
);
138 * Detects whether the firmware supports the new power domains interface.
140 * The firmware doesn't actually return an error on an unknown tag,
141 * and just skips over it, so we do the detection by putting an
142 * unexpected value in the return field and checking if it was
146 rpi_has_new_domain_support(struct rpi_power_domains
*rpi_domains
)
148 struct rpi_power_domain_packet packet
;
151 packet
.domain
= RPI_POWER_DOMAIN_ARM
;
154 ret
= rpi_firmware_property(rpi_domains
->fw
,
155 RPI_FIRMWARE_GET_DOMAIN_STATE
,
156 &packet
, sizeof(packet
));
158 return ret
== 0 && packet
.state
!= ~0;
161 static int rpi_power_probe(struct platform_device
*pdev
)
163 struct device_node
*fw_np
;
164 struct device
*dev
= &pdev
->dev
;
165 struct rpi_power_domains
*rpi_domains
;
167 rpi_domains
= devm_kzalloc(dev
, sizeof(*rpi_domains
), GFP_KERNEL
);
171 rpi_domains
->xlate
.domains
=
173 RPI_POWER_DOMAIN_COUNT
,
174 sizeof(*rpi_domains
->xlate
.domains
),
176 if (!rpi_domains
->xlate
.domains
)
179 rpi_domains
->xlate
.num_domains
= RPI_POWER_DOMAIN_COUNT
;
181 fw_np
= of_parse_phandle(pdev
->dev
.of_node
, "firmware", 0);
183 dev_err(&pdev
->dev
, "no firmware node\n");
187 rpi_domains
->fw
= devm_rpi_firmware_get(&pdev
->dev
, fw_np
);
189 if (!rpi_domains
->fw
)
190 return -EPROBE_DEFER
;
192 rpi_domains
->has_new_interface
=
193 rpi_has_new_domain_support(rpi_domains
);
195 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_I2C0
, "I2C0");
196 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_I2C1
, "I2C1");
197 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_I2C2
, "I2C2");
198 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_VIDEO_SCALER
,
200 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_VPU1
, "VPU1");
201 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_HDMI
, "HDMI");
204 * Use the old firmware interface for USB power, so that we
205 * can turn it on even if the firmware hasn't been updated.
207 rpi_init_old_power_domain(rpi_domains
, RPI_POWER_DOMAIN_USB
,
208 RPI_OLD_POWER_DOMAIN_USB
, "USB");
210 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_VEC
, "VEC");
211 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_JPEG
, "JPEG");
212 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_H264
, "H264");
213 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_V3D
, "V3D");
214 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_ISP
, "ISP");
215 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_UNICAM0
, "UNICAM0");
216 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_UNICAM1
, "UNICAM1");
217 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CCP2RX
, "CCP2RX");
218 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CSI2
, "CSI2");
219 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CPI
, "CPI");
220 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_DSI0
, "DSI0");
221 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_DSI1
, "DSI1");
222 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_TRANSPOSER
,
224 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CCP2TX
, "CCP2TX");
225 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CDP
, "CDP");
226 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_ARM
, "ARM");
228 of_genpd_add_provider_onecell(dev
->of_node
, &rpi_domains
->xlate
);
230 platform_set_drvdata(pdev
, rpi_domains
);
235 static const struct of_device_id rpi_power_of_match
[] = {
236 { .compatible
= "raspberrypi,bcm2835-power", },
239 MODULE_DEVICE_TABLE(of
, rpi_power_of_match
);
241 static struct platform_driver rpi_power_driver
= {
243 .name
= "raspberrypi-power",
244 .of_match_table
= rpi_power_of_match
,
246 .probe
= rpi_power_probe
,
248 builtin_platform_driver(rpi_power_driver
);
250 MODULE_AUTHOR("Alexander Aring <aar@pengutronix.de>");
251 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
252 MODULE_DESCRIPTION("Raspberry Pi power domain driver");