From f9d91de0ad8c765f5ec6f2e71db89cb2fb99820b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 20 Apr 2016 13:25:01 +0100 Subject: [PATCH 1/6] firmware: arm_scpi: remove dvfs_get packed structure dvfs_get packed structure is used to read the DVFS/OPP index from the firmware. It just contains a single byte that needs no packing making the whole structure defination unnecessary. This patch replaces the unnecessary dvfs_get packed structure with an unsigned byte. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scpi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 7e3e595c9f30..279fb84f69e1 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -210,10 +210,6 @@ struct dvfs_info { } opps[MAX_DVFS_OPPS]; } __packed; -struct dvfs_get { - u8 index; -} __packed; - struct dvfs_set { u8 domain; u8 index; @@ -431,11 +427,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate) static int scpi_dvfs_get_idx(u8 domain) { int ret; - struct dvfs_get dvfs; + u8 dvfs_idx; ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain), - &dvfs, sizeof(dvfs)); - return ret ? ret : dvfs.index; + &dvfs_idx, sizeof(dvfs_idx)); + return ret ? ret : dvfs_idx; } static int scpi_dvfs_set_idx(u8 domain, u8 index) From 3678b98f86f920f9a2db38a560c34d6bf899adc0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 23 Feb 2016 16:21:16 +0000 Subject: [PATCH 2/6] firmware: arm_scpi: mark scpi_get_sensor_value as static scpi_get_sensor_value like other scpi operations needs to be static. This patch marks it as static to be consistent with others. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 279fb84f69e1..51c6db0774cc 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -522,7 +522,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info) return ret; } -int scpi_sensor_get_value(u16 sensor, u64 *val) +static int scpi_sensor_get_value(u16 sensor, u64 *val) { __le16 id = cpu_to_le16(sensor); struct sensor_value buf; From 8f1498c03d1503c8675a633e9f354041558cc459 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Sun, 5 Jun 2016 18:23:21 +0100 Subject: [PATCH 3/6] firmware: arm_scpi: make it depend on MAILBOX instead of ARM_MHU ARM_SCPI_PROTOCOL can be used with any mailbox and not just ARM MHU mailbox controller. This patch drops it's dependency on ARM_MHU and make it depend on just mailbox framework. Signed-off-by: Sudeep Holla --- drivers/firmware/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 6664f1108c7c..41abdc54815e 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -10,7 +10,7 @@ config ARM_PSCI_FW config ARM_SCPI_PROTOCOL tristate "ARM System Control and Power Interface (SCPI) Message Protocol" - depends on ARM_MHU + depends on MAILBOX help System Control and Power Interface (SCPI) Message Protocol is defined for the purpose of communication between the Application From 37a441dcd5f4db7f769fee50ba7a2c602903a052 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 20 Apr 2016 14:05:14 +0100 Subject: [PATCH 4/6] firmware: arm_scpi: add support for device power state management SCPI protocol supports device power state management. This deals with power states of various peripheral devices in the system other than the core compute subsystem. This patch adds support for the power state management of those peripheral devices. Tested-by: Mathieu Poirier Tested-by: Jon Medhurst Reviewed-by: Jon Medhurst Reviewed-by: Kevin Hilman Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scpi.c | 30 ++++++++++++++++++++++++++++++ include/linux/scpi_protocol.h | 2 ++ 2 files changed, 32 insertions(+) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 51c6db0774cc..438893762076 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -231,6 +231,11 @@ struct sensor_value { __le32 hi_val; } __packed; +struct dev_pstate_set { + u16 dev_id; + u8 pstate; +} __packed; + static struct scpi_drvinfo *scpi_info; static int scpi_linux_errmap[SCPI_ERR_MAX] = { @@ -537,6 +542,29 @@ static int scpi_sensor_get_value(u16 sensor, u64 *val) return ret; } +static int scpi_device_get_power_state(u16 dev_id) +{ + int ret; + u8 pstate; + __le16 id = cpu_to_le16(dev_id); + + ret = scpi_send_message(SCPI_CMD_GET_DEVICE_PWR_STATE, &id, + sizeof(id), &pstate, sizeof(pstate)); + return ret ? ret : pstate; +} + +static int scpi_device_set_power_state(u16 dev_id, u8 pstate) +{ + int stat; + struct dev_pstate_set dev_set = { + .dev_id = cpu_to_le16(dev_id), + .pstate = pstate, + }; + + return scpi_send_message(SCPI_CMD_SET_DEVICE_PWR_STATE, &dev_set, + sizeof(dev_set), &stat, sizeof(stat)); +} + static struct scpi_ops scpi_ops = { .get_version = scpi_get_version, .clk_get_range = scpi_clk_get_range, @@ -548,6 +576,8 @@ static struct scpi_ops scpi_ops = { .sensor_get_capability = scpi_sensor_get_capability, .sensor_get_info = scpi_sensor_get_info, .sensor_get_value = scpi_sensor_get_value, + .device_get_power_state = scpi_device_get_power_state, + .device_set_power_state = scpi_device_set_power_state, }; struct scpi_ops *get_scpi_ops(void) diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h index 35de50a65665..dc5f989be226 100644 --- a/include/linux/scpi_protocol.h +++ b/include/linux/scpi_protocol.h @@ -70,6 +70,8 @@ struct scpi_ops { int (*sensor_get_capability)(u16 *sensors); int (*sensor_get_info)(u16 sensor_id, struct scpi_sensor_info *); int (*sensor_get_value)(u16, u64 *); + int (*device_get_power_state)(u16); + int (*device_set_power_state)(u16, u8); }; #if IS_REACHABLE(CONFIG_ARM_SCPI_PROTOCOL) From 43752b8d7999ac2ebab85f28dc0c92e248b7b5fa Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Sun, 5 Jun 2016 18:50:50 +0100 Subject: [PATCH 5/6] Documentation: add DT bindings for ARM SCPI power domains The System Control Processor (SCP) provides peripheral devices with power domains that can be enabled and disabled viathe System Control and Power Interface (SCPI) Message Protocol. Add bindings to allow probing of these device power domians. Cc: Rob Herring Tested-by: Mathieu Poirier Tested-by: Jon Medhurst Acked-by: Mark Rutland Reviewed-by: Jon Medhurst Reviewed-by: Kevin Hilman Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla --- .../devicetree/bindings/arm/arm,scpi.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/arm,scpi.txt b/Documentation/devicetree/bindings/arm/arm,scpi.txt index 313dabdc14f9..faa4b44572e3 100644 --- a/Documentation/devicetree/bindings/arm/arm,scpi.txt +++ b/Documentation/devicetree/bindings/arm/arm,scpi.txt @@ -87,10 +87,33 @@ Required properties: implementation for the IDs to use. For Juno R0 and Juno R1 refer to [3]. +Power domain bindings for the power domains based on SCPI Message Protocol +------------------------------------------------------------ + +This binding uses the generic power domain binding[4]. + +PM domain providers +=================== + +Required properties: + - #power-domain-cells : Should be 1. Contains the device or the power + domain ID value used by SCPI commands. + - num-domains: Total number of power domains provided by SCPI. This is + needed as the SCPI message protocol lacks a mechanism to + query this information at runtime. + +PM domain consumers +=================== + +Required properties: + - power-domains : A phandle and PM domain specifier as defined by bindings of + the power controller specified by phandle. + [0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html [1] Documentation/devicetree/bindings/clock/clock-bindings.txt [2] Documentation/devicetree/bindings/thermal/thermal.txt [3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html +[4] Documentation/devicetree/bindings/power/power_domain.txt Example: @@ -144,6 +167,12 @@ scpi_protocol: scpi@2e000000 { compatible = "arm,scpi-sensors"; #thermal-sensor-cells = <1>; }; + + scpi_devpd: scpi-power-domains { + compatible = "arm,scpi-power-domains"; + num-domains = <2>; + #power-domain-cells = <1>; + }; }; cpu@0 { @@ -156,6 +185,7 @@ hdlcd@7ff60000 { ... reg = <0 0x7ff60000 0 0x1000>; clocks = <&scpi_clk 4>; + power-domains = <&scpi_devpd 1>; }; thermal-zones { @@ -186,3 +216,7 @@ The thermal-sensors property in the soc_thermal node uses the temperature sensor provided by SCP firmware to setup a thermal zone. The ID "3" is the sensor identifier for the temperature sensor as used by the firmware. + +The num-domains property in scpi-power-domains domain specifies that +SCPI provides 2 power domains. The hdlcd node uses the power domain with +domain ID 1. From 8bec4337ad4023b26de35d3b0c3a3b2735ffc5c7 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 2 Jun 2016 16:34:03 +0100 Subject: [PATCH 6/6] firmware: scpi: add device power domain support using genpd This patch hooks up the support for device power domain provided by SCPI using the Linux generic power domain infrastructure. Cc: "Rafael J. Wysocki" Tested-by: Mathieu Poirier Tested-by: Jon Medhurst Reviewed-by: Jon Medhurst Reviewed-by: Kevin Hilman Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla --- drivers/firmware/Kconfig | 10 ++ drivers/firmware/Makefile | 1 + drivers/firmware/scpi_pm_domain.c | 163 ++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 drivers/firmware/scpi_pm_domain.c diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 41abdc54815e..4683bc375e96 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -27,6 +27,16 @@ config ARM_SCPI_PROTOCOL This protocol library provides interface for all the client drivers making use of the features offered by the SCP. +config ARM_SCPI_POWER_DOMAIN + tristate "SCPI power domain driver" + depends on ARM_SCPI_PROTOCOL || COMPILE_TEST + default y + select PM_GENERIC_DOMAINS if PM + select PM_GENERIC_DOMAINS_OF if PM + help + This enables support for the SCPI power domains which can be + enabled or disabled via the SCP firmware + config EDD tristate "BIOS Enhanced Disk Drive calls determine boot disk" depends on X86 diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 474bada56fcd..44a59dcfc398 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_ARM_PSCI_FW) += psci.o obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o +obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o obj-$(CONFIG_DMI) += dmi_scan.o obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o obj-$(CONFIG_EDD) += edd.o diff --git a/drivers/firmware/scpi_pm_domain.c b/drivers/firmware/scpi_pm_domain.c new file mode 100644 index 000000000000..f395dec27113 --- /dev/null +++ b/drivers/firmware/scpi_pm_domain.c @@ -0,0 +1,163 @@ +/* + * SCPI Generic power domain support. + * + * Copyright (C) 2016 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct scpi_pm_domain { + struct generic_pm_domain genpd; + struct scpi_ops *ops; + u32 domain; + char name[30]; +}; + +/* + * These device power state values are not well-defined in the specification. + * In case, different implementations use different values, we can make these + * specific to compatibles rather than getting these values from device tree. + */ +enum scpi_power_domain_state { + SCPI_PD_STATE_ON = 0, + SCPI_PD_STATE_OFF = 3, +}; + +#define to_scpi_pd(gpd) container_of(gpd, struct scpi_pm_domain, genpd) + +static int scpi_pd_power(struct scpi_pm_domain *pd, bool power_on) +{ + int ret; + enum scpi_power_domain_state state; + + if (power_on) + state = SCPI_PD_STATE_ON; + else + state = SCPI_PD_STATE_OFF; + + ret = pd->ops->device_set_power_state(pd->domain, state); + if (ret) + return ret; + + return !(state == pd->ops->device_get_power_state(pd->domain)); +} + +static int scpi_pd_power_on(struct generic_pm_domain *domain) +{ + struct scpi_pm_domain *pd = to_scpi_pd(domain); + + return scpi_pd_power(pd, true); +} + +static int scpi_pd_power_off(struct generic_pm_domain *domain) +{ + struct scpi_pm_domain *pd = to_scpi_pd(domain); + + return scpi_pd_power(pd, false); +} + +static int scpi_pm_domain_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct scpi_pm_domain *scpi_pd; + struct genpd_onecell_data *scpi_pd_data; + struct generic_pm_domain **domains; + struct scpi_ops *scpi_ops; + int ret, num_domains, i; + + scpi_ops = get_scpi_ops(); + if (!scpi_ops) + return -EPROBE_DEFER; + + if (!np) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + if (!scpi_ops->device_set_power_state || + !scpi_ops->device_get_power_state) { + dev_err(dev, "power domains not supported in the firmware\n"); + return -ENODEV; + } + + ret = of_property_read_u32(np, "num-domains", &num_domains); + if (ret) { + dev_err(dev, "number of domains not found\n"); + return -EINVAL; + } + + scpi_pd = devm_kcalloc(dev, num_domains, sizeof(*scpi_pd), GFP_KERNEL); + if (!scpi_pd) + return -ENOMEM; + + scpi_pd_data = devm_kzalloc(dev, sizeof(*scpi_pd_data), GFP_KERNEL); + if (!scpi_pd_data) + return -ENOMEM; + + domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + for (i = 0; i < num_domains; i++, scpi_pd++) { + domains[i] = &scpi_pd->genpd; + + scpi_pd->domain = i; + scpi_pd->ops = scpi_ops; + sprintf(scpi_pd->name, "%s.%d", np->name, i); + scpi_pd->genpd.name = scpi_pd->name; + scpi_pd->genpd.power_off = scpi_pd_power_off; + scpi_pd->genpd.power_on = scpi_pd_power_on; + + /* + * Treat all power domains as off at boot. + * + * The SCP firmware itself may have switched on some domains, + * but for reference counting purpose, keep it this way. + */ + pm_genpd_init(&scpi_pd->genpd, NULL, true); + } + + scpi_pd_data->domains = domains; + scpi_pd_data->num_domains = num_domains; + + of_genpd_add_provider_onecell(np, scpi_pd_data); + + return 0; +} + +static const struct of_device_id scpi_power_domain_ids[] = { + { .compatible = "arm,scpi-power-domains", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, scpi_power_domain_ids); + +static struct platform_driver scpi_power_domain_driver = { + .driver = { + .name = "scpi_power_domain", + .of_match_table = scpi_power_domain_ids, + }, + .probe = scpi_pm_domain_probe, +}; +module_platform_driver(scpi_power_domain_driver); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("ARM SCPI power domain driver"); +MODULE_LICENSE("GPL v2");