Commit a136deed authored by Olof Johansson's avatar Olof Johansson

Merge tag 'scpi-updates-4.8' of...

Merge tag 'scpi-updates-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into next/drivers

SCPI updates and fixes for v4.8

1. Adds support for device power state management using generic power
   domains and runtime PM

2. Other minor/miscellaneous fixes to the driver

* tag 'scpi-updates-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: scpi: add device power domain support using genpd
  Documentation: add DT bindings for ARM SCPI power domains
  firmware: arm_scpi: add support for device power state management
  firmware: arm_scpi: make it depend on MAILBOX instead of ARM_MHU
  firmware: arm_scpi: mark scpi_get_sensor_value as static
  firmware: arm_scpi: remove dvfs_get packed structure
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents c8b2da0e 8bec4337
...@@ -87,10 +87,33 @@ Required properties: ...@@ -87,10 +87,33 @@ Required properties:
implementation for the IDs to use. For Juno implementation for the IDs to use. For Juno
R0 and Juno R1 refer to [3]. 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 [0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt [1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Documentation/devicetree/bindings/thermal/thermal.txt [2] Documentation/devicetree/bindings/thermal/thermal.txt
[3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html [3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html
[4] Documentation/devicetree/bindings/power/power_domain.txt
Example: Example:
...@@ -144,6 +167,12 @@ scpi_protocol: scpi@2e000000 { ...@@ -144,6 +167,12 @@ scpi_protocol: scpi@2e000000 {
compatible = "arm,scpi-sensors"; compatible = "arm,scpi-sensors";
#thermal-sensor-cells = <1>; #thermal-sensor-cells = <1>;
}; };
scpi_devpd: scpi-power-domains {
compatible = "arm,scpi-power-domains";
num-domains = <2>;
#power-domain-cells = <1>;
};
}; };
cpu@0 { cpu@0 {
...@@ -156,6 +185,7 @@ hdlcd@7ff60000 { ...@@ -156,6 +185,7 @@ hdlcd@7ff60000 {
... ...
reg = <0 0x7ff60000 0 0x1000>; reg = <0 0x7ff60000 0 0x1000>;
clocks = <&scpi_clk 4>; clocks = <&scpi_clk 4>;
power-domains = <&scpi_devpd 1>;
}; };
thermal-zones { thermal-zones {
...@@ -186,3 +216,7 @@ The thermal-sensors property in the soc_thermal node uses the ...@@ -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 temperature sensor provided by SCP firmware to setup a thermal
zone. The ID "3" is the sensor identifier for the temperature sensor zone. The ID "3" is the sensor identifier for the temperature sensor
as used by the firmware. 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.
...@@ -10,7 +10,7 @@ config ARM_PSCI_FW ...@@ -10,7 +10,7 @@ config ARM_PSCI_FW
config ARM_SCPI_PROTOCOL config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol" tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
depends on ARM_MHU depends on MAILBOX
help help
System Control and Power Interface (SCPI) Message Protocol is System Control and Power Interface (SCPI) Message Protocol is
defined for the purpose of communication between the Application defined for the purpose of communication between the Application
...@@ -27,6 +27,16 @@ config ARM_SCPI_PROTOCOL ...@@ -27,6 +27,16 @@ config ARM_SCPI_PROTOCOL
This protocol library provides interface for all the client drivers This protocol library provides interface for all the client drivers
making use of the features offered by the SCP. 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 config EDD
tristate "BIOS Enhanced Disk Drive calls determine boot disk" tristate "BIOS Enhanced Disk Drive calls determine boot disk"
depends on X86 depends on X86
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
obj-$(CONFIG_ARM_PSCI_FW) += psci.o obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.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) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_EDD) += edd.o
......
...@@ -210,10 +210,6 @@ struct dvfs_info { ...@@ -210,10 +210,6 @@ struct dvfs_info {
} opps[MAX_DVFS_OPPS]; } opps[MAX_DVFS_OPPS];
} __packed; } __packed;
struct dvfs_get {
u8 index;
} __packed;
struct dvfs_set { struct dvfs_set {
u8 domain; u8 domain;
u8 index; u8 index;
...@@ -235,6 +231,11 @@ struct sensor_value { ...@@ -235,6 +231,11 @@ struct sensor_value {
__le32 hi_val; __le32 hi_val;
} __packed; } __packed;
struct dev_pstate_set {
u16 dev_id;
u8 pstate;
} __packed;
static struct scpi_drvinfo *scpi_info; static struct scpi_drvinfo *scpi_info;
static int scpi_linux_errmap[SCPI_ERR_MAX] = { static int scpi_linux_errmap[SCPI_ERR_MAX] = {
...@@ -431,11 +432,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate) ...@@ -431,11 +432,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
static int scpi_dvfs_get_idx(u8 domain) static int scpi_dvfs_get_idx(u8 domain)
{ {
int ret; int ret;
struct dvfs_get dvfs; u8 dvfs_idx;
ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain), ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain),
&dvfs, sizeof(dvfs)); &dvfs_idx, sizeof(dvfs_idx));
return ret ? ret : dvfs.index; return ret ? ret : dvfs_idx;
} }
static int scpi_dvfs_set_idx(u8 domain, u8 index) static int scpi_dvfs_set_idx(u8 domain, u8 index)
...@@ -526,7 +527,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info) ...@@ -526,7 +527,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
return ret; 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); __le16 id = cpu_to_le16(sensor);
struct sensor_value buf; struct sensor_value buf;
...@@ -541,6 +542,29 @@ int scpi_sensor_get_value(u16 sensor, u64 *val) ...@@ -541,6 +542,29 @@ int scpi_sensor_get_value(u16 sensor, u64 *val)
return ret; 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 = { static struct scpi_ops scpi_ops = {
.get_version = scpi_get_version, .get_version = scpi_get_version,
.clk_get_range = scpi_clk_get_range, .clk_get_range = scpi_clk_get_range,
...@@ -552,6 +576,8 @@ static struct scpi_ops scpi_ops = { ...@@ -552,6 +576,8 @@ static struct scpi_ops scpi_ops = {
.sensor_get_capability = scpi_sensor_get_capability, .sensor_get_capability = scpi_sensor_get_capability,
.sensor_get_info = scpi_sensor_get_info, .sensor_get_info = scpi_sensor_get_info,
.sensor_get_value = scpi_sensor_get_value, .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) struct scpi_ops *get_scpi_ops(void)
......
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/pm_domain.h>
#include <linux/scpi_protocol.h>
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 <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCPI power domain driver");
MODULE_LICENSE("GPL v2");
...@@ -70,6 +70,8 @@ struct scpi_ops { ...@@ -70,6 +70,8 @@ struct scpi_ops {
int (*sensor_get_capability)(u16 *sensors); int (*sensor_get_capability)(u16 *sensors);
int (*sensor_get_info)(u16 sensor_id, struct scpi_sensor_info *); int (*sensor_get_info)(u16 sensor_id, struct scpi_sensor_info *);
int (*sensor_get_value)(u16, u64 *); 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) #if IS_REACHABLE(CONFIG_ARM_SCPI_PROTOCOL)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment