Skip to content

Vc4 kms v3d new pm domains #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Raspberry Pi power domain driver

Required properties:

- compatible: Should be "raspberrypi,bcm2835-power".
- firmware: Reference to the RPi firmware device node.
- #power-domain-cells: Should be <1>, we providing multiple power domains.

The valid defines for power domain are:

RPI_POWER_DOMAIN_USB

Example:

power: power {
compatible = "raspberrypi,bcm2835-power";
firmware = <&firmware>;
#power-domain-cells = <1>;
};

Example for using power domain:

&usb {
power-domains = <&power RPI_POWER_DOMAIN_USB>;
};
13 changes: 12 additions & 1 deletion arch/arm/boot/dts/bcm2835-rpi.dtsi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <dt-bindings/arm/raspberrypi-power.h>
#include "bcm2835.dtsi"

/ {
Expand All @@ -20,6 +21,12 @@
compatible = "raspberrypi,bcm2835-firmware";
mboxes = <&mailbox>;
};

power: power {
compatible = "raspberrypi,bcm2835-power";
firmware = <&firmware>;
#power-domain-cells = <1>;
};
};
};

Expand Down Expand Up @@ -65,6 +72,10 @@
bus-width = <4>;
};

&usb {
power-domains = <&power RPI_POWER_DOMAIN_USB>;
};

&v3d {
power-domains = <&v3d_power>;
power-domains = <&power RPI_POWER_DOMAIN_V3D>;
};
2 changes: 1 addition & 1 deletion arch/arm/boot/dts/bcm2835.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
status = "disabled";
};

usb@7e980000 {
usb: usb@7e980000 {
compatible = "brcm,bcm2835-usb";
reg = <0x7e980000 0x10000>;
interrupts = <1 9>;
Expand Down
10 changes: 10 additions & 0 deletions arch/arm/mach-bcm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ config ARCH_BCM2835
This enables support for the Broadcom BCM2835 SoC. This SoC is
used in the Raspberry Pi and Roku 2 devices.

config RASPBERRYPI_POWER
bool "Raspberry Pi power domain driver"
depends on ARCH_BCM2835 || COMPILE_TEST
depends on RASPBERRYPI_FIRMWARE
select PM_GENERIC_DOMAINS if PM
select PM_GENERIC_DOMAINS_OF if PM
help
This enables support for the RPi power domains which can be enabled
or disabled via the RPi firmware.

config ARCH_BCM_63XX
bool "Broadcom BCM63xx DSL SoC" if ARCH_MULTI_V7
depends on MMU
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-bcm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ endif

# BCM2835
obj-$(CONFIG_ARCH_BCM2835) += board_bcm2835.o
obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o

# BCM5301X
obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o
Expand Down
246 changes: 246 additions & 0 deletions arch/arm/mach-bcm/raspberrypi-power.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Authors:
* (C) 2015 Pengutronix, Alexander Aring <[email protected]>
* Eric Anholt <[email protected]>
*/

#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <dt-bindings/arm/raspberrypi-power.h>
#include <soc/bcm2835/raspberrypi-firmware.h>

/*
* Firmware indices for the old power domains interface. Only a few
* of them were actually implemented.
*/
#define RPI_OLD_POWER_DOMAIN_USB 3
#define RPI_OLD_POWER_DOMAIN_V3D 10

struct rpi_power_domain {
u32 domain;
bool enabled;
bool old_interface;
struct generic_pm_domain base;
struct rpi_firmware *fw;
};

struct rpi_power_domains {
struct genpd_onecell_data xlate;
struct rpi_firmware *fw;
struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT];
};

/*
* Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and
* RPI_FIRMWARE_SET_POWER_DOMAIN
*/
struct rpi_power_domain_packet {
u32 domain;
u32 on;
} __packet;

/*
* Asks the firmware to enable or disable power on a specific power
* domain.
*/
static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on)
{
struct rpi_power_domain_packet packet;

packet.domain = rpi_domain->domain;
packet.on = on;
return rpi_firmware_property(rpi_domain->fw,
rpi_domain->old_interface ?
RPI_FIRMWARE_SET_POWER_STATE :
RPI_FIRMWARE_SET_DOMAIN_STATE,
&packet, sizeof(packet));
}

static int rpi_domain_off(struct generic_pm_domain *domain)
{
struct rpi_power_domain *rpi_domain =
container_of(domain, struct rpi_power_domain, base);

return rpi_firmware_set_power(rpi_domain, false);
}

static int rpi_domain_on(struct generic_pm_domain *domain)
{
struct rpi_power_domain *rpi_domain =
container_of(domain, struct rpi_power_domain, base);

return rpi_firmware_set_power(rpi_domain, true);
}

static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index, int domain,
const char *name)
{
struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
struct rpi_power_domain_packet packet;
int ret;

packet.domain = dom->domain;
ret = rpi_firmware_property(rpi_domains->fw,
dom->old_interface ?
RPI_FIRMWARE_GET_POWER_STATE :
RPI_FIRMWARE_GET_DOMAIN_STATE,
&packet, sizeof(packet));
if (ret < 0)
return;

dom->domain = xlate_index + 1;

dom->fw = rpi_domains->fw;

dom->base.name = name;
dom->base.power_on = rpi_domain_on;
dom->base.power_off = rpi_domain_off;

pm_genpd_init(&dom->base, NULL, !packet.on);

/* If we succeeded, plug this one into the DT translation mapping. */
rpi_domains->xlate.domains[xlate_index] = &dom->base;
}

static void
rpi_init_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index,
const char *name)
{
/* The DT binding index is the firmware's domain index minus one. */
rpi_common_init_power_domain(rpi_domains, xlate_index, xlate_index + 1,
name);
}

static void
rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index,
int domain,
const char *name)
{
struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];

dom->old_interface = true;
rpi_common_init_power_domain(rpi_domains, xlate_index, domain, name);
}

static int rpi_power_probe(struct platform_device *pdev)
{
struct device_node *fw_np;
struct device *dev = &pdev->dev;
struct rpi_power_domains *rpi_domains;
int ret, i;

rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL);
if (!rpi_domains)
return -ENOMEM;

rpi_domains->xlate.domains =
devm_kzalloc(dev, sizeof(*rpi_domains->xlate.domains) *
RPI_POWER_DOMAIN_COUNT, GFP_KERNEL);
if (!rpi_domains->xlate.domains)
return -ENOMEM;

rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT;

fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
if (!fw_np) {
dev_err(&pdev->dev, "no firmware node\n");
return -ENODEV;
}

rpi_domains->fw = rpi_firmware_get(fw_np);
of_node_put(fw_np);
if (!rpi_domains->fw)
return -EPROBE_DEFER;

rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER,
"VIDEO_SCALER");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB, "USB");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D");

/*
* Use the old firmware interface for USB power, so that we
* can turn it on even if a new firmware hasn't been
* downloaded.
*/
rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB,
RPI_OLD_POWER_DOMAIN_USB, "USB");

rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER,
"TRANPOSER");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM");

ret = of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate);
if (ret < 0)
goto exit_pm;

platform_set_drvdata(pdev, rpi_domains);

return 0;

exit_pm:
for (i = 0; i < rpi_domains->xlate.num_domains; i++)
pm_genpd_exit(rpi_domains->xlate.domains[i]);

return ret;
}

static int rpi_power_remove(struct platform_device *pdev)
{
struct rpi_power_domains *rpi_domains = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int i;

for (i = 0; i < rpi_domains->xlate.num_domains; i++)
pm_genpd_exit(rpi_domains->xlate.domains[i]);

of_genpd_del_provider(dev->of_node);

return 0;
}

static const struct of_device_id rpi_power_of_match[] = {
{ .compatible = "raspberrypi,bcm2835-power", },
{},
};
MODULE_DEVICE_TABLE(of, rpi_power_of_match);

static struct platform_driver rpi_power_driver = {
.driver = {
.name = "raspberrypi-power",
.of_match_table = rpi_power_of_match,
},
.probe = rpi_power_probe,
.remove = rpi_power_remove,
};
module_platform_driver(rpi_power_driver);

MODULE_AUTHOR("Alexander Aring <[email protected]>");
MODULE_AUTHOR("Eric Anholt <[email protected]>");
MODULE_DESCRIPTION("Raspberry Pi power domain driver");
MODULE_LICENSE("GPL v2");
41 changes: 41 additions & 0 deletions include/dt-bindings/arm/raspberrypi-power.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright © 2015 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#ifndef _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H
#define _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H

/* These power domain indices are the firmware interface's indices
* minus one.
*/
#define RPI_POWER_DOMAIN_I2C0 0
#define RPI_POWER_DOMAIN_I2C1 1
#define RPI_POWER_DOMAIN_I2C2 2
#define RPI_POWER_DOMAIN_VIDEO_SCALER 3
#define RPI_POWER_DOMAIN_VPU1 4
#define RPI_POWER_DOMAIN_HDMI 5
#define RPI_POWER_DOMAIN_USB 6
#define RPI_POWER_DOMAIN_VEC 7
#define RPI_POWER_DOMAIN_JPEG 8
#define RPI_POWER_DOMAIN_H264 9
#define RPI_POWER_DOMAIN_V3D 10
#define RPI_POWER_DOMAIN_ISP 11
#define RPI_POWER_DOMAIN_UNICAM0 12
#define RPI_POWER_DOMAIN_UNICAM1 13
#define RPI_POWER_DOMAIN_CCP2RX 14
#define RPI_POWER_DOMAIN_CSI2 15
#define RPI_POWER_DOMAIN_CPI 16
#define RPI_POWER_DOMAIN_DSI0 17
#define RPI_POWER_DOMAIN_DSI1 18
#define RPI_POWER_DOMAIN_TRANSPOSER 19
#define RPI_POWER_DOMAIN_CCP2TX 20
#define RPI_POWER_DOMAIN_CDP 21
#define RPI_POWER_DOMAIN_ARM 22

#define RPI_POWER_DOMAIN_COUNT 23

#endif /* _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H */