aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2017-04-27 15:08:51 +0100
committerRyan Harkin <ryan.harkin@linaro.org>2017-07-21 12:47:49 +0100
commit7fce0d4307e0d5562577a459bf8c0386ee8e1f84 (patch)
tree059748fbb0f5f0bc6b6bbce5ddea9cde452e69aa
parent7cbd7b9cb719f416eeee7c78d728edb31fb0bf0e (diff)
firmware: arm_scpi: add support to populate OPPs and get transition latency
Currently only CPU devices use the transition latency and the OPPs populated in the SCPI driver. scpi-cpufreq has logic to handle these. However, even GPU and other users of SCPI DVFS will need the same logic. In order to avoid duplication, this patch adds support to get DVFS transition latency and add all the OPPs to the device using OPP library helper functions. The helper functions added here can be used for any device whose DVFS are managed by SCPI. Also, we also have incorrect dependency on the cluster identifier for the CPUs. It's fundamentally wrong as the domain id need not match the cluster id. This patch gets rid of that dependency by making use of the clock bindings which are already in place. Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
-rw-r--r--drivers/firmware/arm_scpi.c63
-rw-r--r--include/linux/scpi_protocol.h3
2 files changed, 66 insertions, 0 deletions
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index f6cfc31d34c7..8043e51de897 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -39,6 +39,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/printk.h>
+#include <linux/pm_opp.h>
#include <linux/scpi_protocol.h>
#include <linux/slab.h>
#include <linux/sort.h>
@@ -684,6 +685,65 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
return info;
}
+static int scpi_dev_domain_id(struct device *dev)
+{
+ struct of_phandle_args clkspec;
+
+ if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
+ 0, &clkspec))
+ return -EINVAL;
+
+ return clkspec.args[0];
+}
+
+static struct scpi_dvfs_info *scpi_dvfs_info(struct device *dev)
+{
+ int domain = scpi_dev_domain_id(dev);
+
+ if (domain < 0)
+ return ERR_PTR(domain);
+
+ return scpi_dvfs_get_info(domain);
+}
+
+static int scpi_dvfs_get_transition_latency(struct device *dev)
+{
+ struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
+
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ if (!info->latency)
+ return 0;
+
+ return info->latency;
+}
+
+static int scpi_dvfs_add_opps_to_device(struct device *dev)
+{
+ int idx, ret;
+ struct scpi_opp *opp;
+ struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
+
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ if (!info->opps)
+ return -EIO;
+
+ for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
+ ret = dev_pm_opp_add(dev, opp->freq, opp->m_volt * 1000);
+ if (ret) {
+ dev_warn(dev, "failed to add opp %uHz %umV\n",
+ opp->freq, opp->m_volt);
+ while (idx-- > 0)
+ dev_pm_opp_remove(dev, (--opp)->freq);
+ return ret;
+ }
+ }
+ return 0;
+}
+
static int scpi_sensor_get_capability(u16 *sensors)
{
struct sensor_capabilities cap_buf;
@@ -765,6 +825,9 @@ static struct scpi_ops scpi_ops = {
.dvfs_get_idx = scpi_dvfs_get_idx,
.dvfs_set_idx = scpi_dvfs_set_idx,
.dvfs_get_info = scpi_dvfs_get_info,
+ .device_domain_id = scpi_dev_domain_id,
+ .get_transition_latency = scpi_dvfs_get_transition_latency,
+ .add_opps_to_device = scpi_dvfs_add_opps_to_device,
.sensor_get_capability = scpi_sensor_get_capability,
.sensor_get_info = scpi_sensor_get_info,
.sensor_get_value = scpi_sensor_get_value,
diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h
index dc5f989be226..327d65663dbf 100644
--- a/include/linux/scpi_protocol.h
+++ b/include/linux/scpi_protocol.h
@@ -67,6 +67,9 @@ struct scpi_ops {
int (*dvfs_get_idx)(u8);
int (*dvfs_set_idx)(u8, u8);
struct scpi_dvfs_info *(*dvfs_get_info)(u8);
+ int (*device_domain_id)(struct device *);
+ int (*get_transition_latency)(struct device *);
+ int (*add_opps_to_device)(struct device *);
int (*sensor_get_capability)(u16 *sensors);
int (*sensor_get_info)(u16 sensor_id, struct scpi_sensor_info *);
int (*sensor_get_value)(u16, u64 *);