Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a136deed authored by Olof Johansson's avatar Olof Johansson
Browse files

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
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -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.
+11 −1
Original line number Diff line number Diff line
@@ -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
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+34 −8
Original line number Diff line number Diff line
@@ -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;
@@ -235,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] = {
@@ -431,11 +432,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)
@@ -526,7 +527,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;
@@ -541,6 +542,29 @@ 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,
@@ -552,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)
+163 −0
Original line number Diff line number Diff line
/*
 * 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");
Loading