cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

raspberrypi-power.c (7405B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
      3 *
      4 * Authors:
      5 * Alexander Aring <aar@pengutronix.de>
      6 * Eric Anholt <eric@anholt.net>
      7 */
      8
      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>
     15
     16/*
     17 * Firmware indices for the old power domains interface.  Only a few
     18 * of them were actually implemented.
     19 */
     20#define RPI_OLD_POWER_DOMAIN_USB		3
     21#define RPI_OLD_POWER_DOMAIN_V3D		10
     22
     23struct rpi_power_domain {
     24	u32 domain;
     25	bool enabled;
     26	bool old_interface;
     27	struct generic_pm_domain base;
     28	struct rpi_firmware *fw;
     29};
     30
     31struct 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];
     36};
     37
     38/*
     39 * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and
     40 * RPI_FIRMWARE_SET_DOMAIN_STATE
     41 */
     42struct rpi_power_domain_packet {
     43	u32 domain;
     44	u32 on;
     45};
     46
     47/*
     48 * Asks the firmware to enable or disable power on a specific power
     49 * domain.
     50 */
     51static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on)
     52{
     53	struct rpi_power_domain_packet packet;
     54
     55	packet.domain = rpi_domain->domain;
     56	packet.on = on;
     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));
     62}
     63
     64static int rpi_domain_off(struct generic_pm_domain *domain)
     65{
     66	struct rpi_power_domain *rpi_domain =
     67		container_of(domain, struct rpi_power_domain, base);
     68
     69	return rpi_firmware_set_power(rpi_domain, false);
     70}
     71
     72static int rpi_domain_on(struct generic_pm_domain *domain)
     73{
     74	struct rpi_power_domain *rpi_domain =
     75		container_of(domain, struct rpi_power_domain, base);
     76
     77	return rpi_firmware_set_power(rpi_domain, true);
     78}
     79
     80static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains,
     81					 int xlate_index, const char *name)
     82{
     83	struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
     84
     85	dom->fw = rpi_domains->fw;
     86
     87	dom->base.name = name;
     88	dom->base.power_on = rpi_domain_on;
     89	dom->base.power_off = rpi_domain_off;
     90
     91	/*
     92	 * Treat all power domains as off at boot.
     93	 *
     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.
     98	 */
     99	pm_genpd_init(&dom->base, NULL, true);
    100
    101	rpi_domains->xlate.domains[xlate_index] = &dom->base;
    102}
    103
    104static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains,
    105				  int xlate_index, const char *name)
    106{
    107	struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
    108
    109	if (!rpi_domains->has_new_interface)
    110		return;
    111
    112	/* The DT binding index is the firmware's domain index minus one. */
    113	dom->domain = xlate_index + 1;
    114
    115	rpi_common_init_power_domain(rpi_domains, xlate_index, name);
    116}
    117
    118static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains,
    119				      int xlate_index, int domain,
    120				      const char *name)
    121{
    122	struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
    123
    124	dom->old_interface = true;
    125	dom->domain = domain;
    126
    127	rpi_common_init_power_domain(rpi_domains, xlate_index, name);
    128}
    129
    130/*
    131 * Detects whether the firmware supports the new power domains interface.
    132 *
    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
    136 * unchanged.
    137 */
    138static bool
    139rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains)
    140{
    141	struct rpi_power_domain_packet packet;
    142	int ret;
    143
    144	packet.domain = RPI_POWER_DOMAIN_ARM;
    145	packet.on = ~0;
    146
    147	ret = rpi_firmware_property(rpi_domains->fw,
    148				    RPI_FIRMWARE_GET_DOMAIN_STATE,
    149				    &packet, sizeof(packet));
    150
    151	return ret == 0 && packet.on != ~0;
    152}
    153
    154static int rpi_power_probe(struct platform_device *pdev)
    155{
    156	struct device_node *fw_np;
    157	struct device *dev = &pdev->dev;
    158	struct rpi_power_domains *rpi_domains;
    159
    160	rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL);
    161	if (!rpi_domains)
    162		return -ENOMEM;
    163
    164	rpi_domains->xlate.domains =
    165		devm_kcalloc(dev,
    166			     RPI_POWER_DOMAIN_COUNT,
    167			     sizeof(*rpi_domains->xlate.domains),
    168			     GFP_KERNEL);
    169	if (!rpi_domains->xlate.domains)
    170		return -ENOMEM;
    171
    172	rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT;
    173
    174	fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
    175	if (!fw_np) {
    176		dev_err(&pdev->dev, "no firmware node\n");
    177		return -ENODEV;
    178	}
    179
    180	rpi_domains->fw = devm_rpi_firmware_get(&pdev->dev, fw_np);
    181	of_node_put(fw_np);
    182	if (!rpi_domains->fw)
    183		return -EPROBE_DEFER;
    184
    185	rpi_domains->has_new_interface =
    186		rpi_has_new_domain_support(rpi_domains);
    187
    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,
    192			      "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");
    195
    196	/*
    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.
    199	 */
    200	rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB,
    201				  RPI_OLD_POWER_DOMAIN_USB, "USB");
    202
    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,
    216			      "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");
    220
    221	of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate);
    222
    223	platform_set_drvdata(pdev, rpi_domains);
    224
    225	return 0;
    226}
    227
    228static const struct of_device_id rpi_power_of_match[] = {
    229	{ .compatible = "raspberrypi,bcm2835-power", },
    230	{},
    231};
    232MODULE_DEVICE_TABLE(of, rpi_power_of_match);
    233
    234static struct platform_driver rpi_power_driver = {
    235	.driver = {
    236		.name = "raspberrypi-power",
    237		.of_match_table = rpi_power_of_match,
    238	},
    239	.probe		= rpi_power_probe,
    240};
    241builtin_platform_driver(rpi_power_driver);
    242
    243MODULE_AUTHOR("Alexander Aring <aar@pengutronix.de>");
    244MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
    245MODULE_DESCRIPTION("Raspberry Pi power domain driver");
    246MODULE_LICENSE("GPL v2");