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>
10 #include <linux/of_platform.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 rpi_power_domain
*rpi_domain
, bool on
)
53 struct rpi_power_domain_packet packet
;
55 packet
.domain
= rpi_domain
->domain
;
57 return rpi_firmware_property(rpi_domain
->fw
,
58 rpi_domain
->old_interface
?
59 RPI_FIRMWARE_SET_POWER_STATE
:
60 RPI_FIRMWARE_SET_DOMAIN_STATE
,
61 &packet
, sizeof(packet
));
64 static int rpi_domain_off(struct generic_pm_domain
*domain
)
66 struct rpi_power_domain
*rpi_domain
=
67 container_of(domain
, struct rpi_power_domain
, base
);
69 return rpi_firmware_set_power(rpi_domain
, false);
72 static int rpi_domain_on(struct generic_pm_domain
*domain
)
74 struct rpi_power_domain
*rpi_domain
=
75 container_of(domain
, struct rpi_power_domain
, base
);
77 return rpi_firmware_set_power(rpi_domain
, true);
80 static void rpi_common_init_power_domain(struct rpi_power_domains
*rpi_domains
,
81 int xlate_index
, const char *name
)
83 struct rpi_power_domain
*dom
= &rpi_domains
->domains
[xlate_index
];
85 dom
->fw
= rpi_domains
->fw
;
87 dom
->base
.name
= name
;
88 dom
->base
.power_on
= rpi_domain_on
;
89 dom
->base
.power_off
= rpi_domain_off
;
92 * Treat all power domains as off at boot.
94 * The firmware itself may be keeping some domains on, but
95 * from Linux's perspective all we control is the refcounts
96 * that we give to the firmware, and we can't ask the firmware
97 * to turn off something that we haven't ourselves turned on.
99 pm_genpd_init(&dom
->base
, NULL
, true);
101 rpi_domains
->xlate
.domains
[xlate_index
] = &dom
->base
;
104 static void rpi_init_power_domain(struct rpi_power_domains
*rpi_domains
,
105 int xlate_index
, const char *name
)
107 struct rpi_power_domain
*dom
= &rpi_domains
->domains
[xlate_index
];
109 if (!rpi_domains
->has_new_interface
)
112 /* The DT binding index is the firmware's domain index minus one. */
113 dom
->domain
= xlate_index
+ 1;
115 rpi_common_init_power_domain(rpi_domains
, xlate_index
, name
);
118 static void rpi_init_old_power_domain(struct rpi_power_domains
*rpi_domains
,
119 int xlate_index
, int domain
,
122 struct rpi_power_domain
*dom
= &rpi_domains
->domains
[xlate_index
];
124 dom
->old_interface
= true;
125 dom
->domain
= domain
;
127 rpi_common_init_power_domain(rpi_domains
, xlate_index
, name
);
131 * Detects whether the firmware supports the new power domains interface.
133 * The firmware doesn't actually return an error on an unknown tag,
134 * and just skips over it, so we do the detection by putting an
135 * unexpected value in the return field and checking if it was
139 rpi_has_new_domain_support(struct rpi_power_domains
*rpi_domains
)
141 struct rpi_power_domain_packet packet
;
144 packet
.domain
= RPI_POWER_DOMAIN_ARM
;
147 ret
= rpi_firmware_property(rpi_domains
->fw
,
148 RPI_FIRMWARE_GET_DOMAIN_STATE
,
149 &packet
, sizeof(packet
));
151 return ret
== 0 && packet
.on
!= ~0;
154 static int rpi_power_probe(struct platform_device
*pdev
)
156 struct device_node
*fw_np
;
157 struct device
*dev
= &pdev
->dev
;
158 struct rpi_power_domains
*rpi_domains
;
160 rpi_domains
= devm_kzalloc(dev
, sizeof(*rpi_domains
), GFP_KERNEL
);
164 rpi_domains
->xlate
.domains
=
166 RPI_POWER_DOMAIN_COUNT
,
167 sizeof(*rpi_domains
->xlate
.domains
),
169 if (!rpi_domains
->xlate
.domains
)
172 rpi_domains
->xlate
.num_domains
= RPI_POWER_DOMAIN_COUNT
;
174 fw_np
= of_parse_phandle(pdev
->dev
.of_node
, "firmware", 0);
176 dev_err(&pdev
->dev
, "no firmware node\n");
180 rpi_domains
->fw
= rpi_firmware_get(fw_np
);
182 if (!rpi_domains
->fw
)
183 return -EPROBE_DEFER
;
185 rpi_domains
->has_new_interface
=
186 rpi_has_new_domain_support(rpi_domains
);
188 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_I2C0
, "I2C0");
189 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_I2C1
, "I2C1");
190 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_I2C2
, "I2C2");
191 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_VIDEO_SCALER
,
193 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_VPU1
, "VPU1");
194 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_HDMI
, "HDMI");
197 * Use the old firmware interface for USB power, so that we
198 * can turn it on even if the firmware hasn't been updated.
200 rpi_init_old_power_domain(rpi_domains
, RPI_POWER_DOMAIN_USB
,
201 RPI_OLD_POWER_DOMAIN_USB
, "USB");
203 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_VEC
, "VEC");
204 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_JPEG
, "JPEG");
205 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_H264
, "H264");
206 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_V3D
, "V3D");
207 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_ISP
, "ISP");
208 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_UNICAM0
, "UNICAM0");
209 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_UNICAM1
, "UNICAM1");
210 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CCP2RX
, "CCP2RX");
211 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CSI2
, "CSI2");
212 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CPI
, "CPI");
213 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_DSI0
, "DSI0");
214 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_DSI1
, "DSI1");
215 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_TRANSPOSER
,
217 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CCP2TX
, "CCP2TX");
218 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_CDP
, "CDP");
219 rpi_init_power_domain(rpi_domains
, RPI_POWER_DOMAIN_ARM
, "ARM");
221 of_genpd_add_provider_onecell(dev
->of_node
, &rpi_domains
->xlate
);
223 platform_set_drvdata(pdev
, rpi_domains
);
228 static const struct of_device_id rpi_power_of_match
[] = {
229 { .compatible
= "raspberrypi,bcm2835-power", },
232 MODULE_DEVICE_TABLE(of
, rpi_power_of_match
);
234 static struct platform_driver rpi_power_driver
= {
236 .name
= "raspberrypi-power",
237 .of_match_table
= rpi_power_of_match
,
239 .probe
= rpi_power_probe
,
241 builtin_platform_driver(rpi_power_driver
);
243 MODULE_AUTHOR("Alexander Aring <aar@pengutronix.de>");
244 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
245 MODULE_DESCRIPTION("Raspberry Pi power domain driver");
246 MODULE_LICENSE("GPL v2");