aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2015-08-04 16:13:51 +0100
committerJon Medhurst <tixy@linaro.org>2015-08-04 16:13:51 +0100
commita26a830786c1b0864f554af76fdcf15aec2115ab (patch)
treeca406c7b7b8cf2b7f75d9d3b60e92d2a5beeee52
parenteac86f4dfd7d5705101f93c0e19ba7c799b03bc9 (diff)
parent3a2859d44938473bca16ab5b09e4db2d5392fcff (diff)
Merge branch 'lsk-3.10-armlt-scpi-thermal' into integration-lsk-3.10-juno-android
Conflicts: arch/arm64/boot/dts/juno.dts linaro/configs/vexpress64.conf
-rw-r--r--arch/arm64/boot/dts/juno.dts58
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/scpi-thermal.c308
-rw-r--r--linaro/configs/vexpress64.conf11
5 files changed, 383 insertions, 3 deletions
diff --git a/arch/arm64/boot/dts/juno.dts b/arch/arm64/boot/dts/juno.dts
index 3772b8aba6cd..a905e8f887c4 100644
--- a/arch/arm64/boot/dts/juno.dts
+++ b/arch/arm64/boot/dts/juno.dts
@@ -9,6 +9,7 @@
/dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/thermal/thermal.h>
/ {
model = "ARM Juno development board (r0)";
@@ -35,7 +36,9 @@
#size-cells = <0>;
cpu-map {
- cluster0 {
+ cluster0: cluster0 {
+ #cooling-cells = <2>; /* min followed by max */
+
core0 {
cpu = <&A57_0>;
};
@@ -44,7 +47,9 @@
};
};
- cluster1 {
+ cluster1: cluster1 {
+ #cooling-cells = <2>; /* min followed by max */
+
core0 {
cpu = <&A53_0>;
};
@@ -224,6 +229,52 @@
};
};
+ scpi_sensor0: scpi-sensor@0 {
+ compatible = "arm,scpi-thermal";
+ #thermal-sensor-cells = <1>;
+ };
+
+ thermal-zones {
+ soc_thermal {
+ polling-delay = <1000>;
+ polling-delay-passive = <100>;
+ sustainable-power = <2500>;
+
+ thermal-sensors = <&scpi_sensor0 3>;
+
+ trips {
+ threshold: trip-point@0 {
+ temperature = <55000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ target: trip-point@1 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&target>;
+ cooling-device = <&cluster0 0 4>;
+ contribution = <1024>;
+ };
+ map1 {
+ trip = <&target>;
+ cooling-device = <&cluster1 0 4>;
+ contribution = <2048>;
+ };
+ map2 {
+ trip = <&target>;
+ cooling-device = <&gpu 0 4>;
+ contribution = <1024>;
+ };
+ };
+ };
+ };
+
/include/ "juno-clocks.dtsi"
hwmon@1c010000 {
@@ -308,8 +359,9 @@
clock-names = "apb_pclk";
};
- gpu@0x2d000000 {
+ gpu: gpu@0x2d000000 {
compatible = "arm,malit6xx", "arm,mali";
+ #cooling-cells = <2>; /* min followed by max */
reg = <0x0 0x2d000000 0x0 0x4000>;
interrupts = <0 33 4>, <0 34 4>, <0 32 4>;
interrupt-names = "JOB", "MMU", "GPU";
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index b57a6bd7a54e..a91bc4527402 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -221,6 +221,14 @@ config DB8500_CPUFREQ_COOLING
bound cpufreq cooling device turns active to set CPU frequency low to
cool down the CPU.
+config SCPI_THERMAL
+ tristate "Temperature sensor on ARM SoC based on SCPI Firmware interface"
+ depends on ARM_SCPI_PROTOCOL
+ depends on OF
+ help
+ Support for the SCPI thermal sensor driver in the Linux thermal
+ framework.
+
config INTEL_POWERCLAMP
tristate "Intel PowerClamp idle injection driver"
depends on THERMAL
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 578f21f44672..19c364e29cb7 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -30,5 +30,6 @@ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
+obj-$(CONFIG_SCPI_THERMAL) += scpi-thermal.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
diff --git a/drivers/thermal/scpi-thermal.c b/drivers/thermal/scpi-thermal.c
new file mode 100644
index 000000000000..41eb908e4b70
--- /dev/null
+++ b/drivers/thermal/scpi-thermal.c
@@ -0,0 +1,308 @@
+#include <linux/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scpi_protocol.h>
+#include <linux/thermal.h>
+#include <linux/topology.h>
+
+#define SOC_SENSOR "SENSOR_TEMP_SOC"
+
+enum cluster_type {
+ CLUSTER_BIG = 0,
+ CLUSTER_LITTLE,
+ NUM_CLUSTERS
+};
+
+struct cluster_power_coefficients {
+ int dyn_coeff;
+ int static_coeff;
+ int cache_static_coeff;
+} cluster_data[] = {
+ [CLUSTER_BIG] = {
+ .dyn_coeff = 530,
+ .static_coeff = 103, /* 75 mW @ 85C/0.9V */
+ .cache_static_coeff = 88, /* 64 mW @ 85C/0.9V */
+ },
+ [CLUSTER_LITTLE] = {
+ .dyn_coeff = 140,
+ .static_coeff = 36, /* 26 mW @ 85C/0.9V */
+ .cache_static_coeff = 73, /* 53 mW @ 85C/0.9V */
+ },
+};
+
+struct scpi_sensor {
+ u16 sensor_id;
+ unsigned long prev_temp;
+ u32 alpha;
+ struct thermal_zone_device *tzd;
+ struct thermal_zone_device *gpu_tzd;
+ struct cpumask cluster[NUM_CLUSTERS];
+ struct thermal_cooling_device *cdevs[NUM_CLUSTERS];
+};
+
+static struct scpi_ops *scpi_ops;
+struct scpi_sensor scpi_temp_sensor;
+
+#define FRAC_BITS 10
+#define int_to_frac(x) ((x) << FRAC_BITS)
+#define frac_to_int(x) ((x) >> FRAC_BITS)
+
+/**
+ * mul_frac() - multiply two fixed-point numbers
+ * @x: first multiplicand
+ * @y: second multiplicand
+ *
+ * Return: the result of multiplying two fixed-point numbers. The
+ * result is also a fixed-point number.
+ */
+static inline s64 mul_frac(s64 x, s64 y)
+{
+ return (x * y) >> FRAC_BITS;
+}
+
+static unsigned long get_temperature_scale(unsigned long temp)
+{
+ int i, t_exp = 1, t_scale = 0;
+ int coeff[] = { 32000, 4700, -80, 2 }; /* * 1E6 */
+
+ for (i = 0; i < 4; i++) {
+ t_scale += coeff[i] * t_exp;
+ t_exp *= temp;
+ }
+
+ return t_scale / 1000; /* the value returned needs to be /1E3 */
+}
+
+static unsigned long get_voltage_scale(unsigned long u_volt)
+{
+ unsigned long m_volt = u_volt / 1000;
+ unsigned long v_scale;
+
+ v_scale = m_volt * m_volt * m_volt; /* = (m_V^3) / (900 ^ 3) = */
+
+ return v_scale / 1000000; /* the value returned needs to be /(1E3) */
+}
+
+/* voltage in uV */
+static int get_static_power(cpumask_t *cpumask, int interval,
+ unsigned long u_volt, u32 *static_power)
+{
+ unsigned long temperature, t_scale, v_scale;
+ u32 cpu_coeff;
+ int nr_cpus = cpumask_weight(cpumask);
+ enum cluster_type cluster =
+ topology_physical_package_id(cpumask_any(cpumask));
+
+ if (!scpi_temp_sensor.tzd)
+ return -ENODEV;
+
+ cpu_coeff = cluster_data[cluster].static_coeff;
+
+ /* temperature in mC */
+ temperature = scpi_temp_sensor.tzd->temperature / 1000;
+
+ t_scale = get_temperature_scale(temperature);
+ v_scale = get_voltage_scale(u_volt);
+
+ *static_power = nr_cpus * (cpu_coeff * t_scale * v_scale) / 1000000;
+
+ if (nr_cpus) {
+ u32 cache_coeff = cluster_data[cluster].cache_static_coeff;
+
+ /* cache leakage */
+ *static_power += (cache_coeff * v_scale * t_scale) / 1000000;
+ }
+
+ return 0;
+}
+
+static int get_temp_value(void *data, long *temp)
+{
+ struct scpi_sensor *sensor = (struct scpi_sensor *)data;
+ u32 val;
+ int ret;
+ unsigned long est_temp;
+
+ ret = scpi_ops->sensor_get_value(sensor->sensor_id, &val);
+ if (ret)
+ return ret;
+
+ if (!sensor->prev_temp)
+ sensor->prev_temp = val;
+
+ est_temp = mul_frac(sensor->alpha, val) +
+ mul_frac((int_to_frac(1) - sensor->alpha), sensor->prev_temp);
+
+ sensor->prev_temp = est_temp;
+ *temp = est_temp;
+
+ return 0;
+}
+
+static int get_temp_for_gpu(struct thermal_zone_device *tz, unsigned long *temp)
+{
+ struct scpi_sensor *sensor = (struct scpi_sensor *)tz->devdata;
+
+ *temp = sensor->tzd->temperature;
+
+ return 0;
+}
+
+static void update_debugfs(struct scpi_sensor *sensor_data)
+{
+ struct dentry *dentry_f, *filter_d;
+
+ filter_d = debugfs_create_dir("thermal_lpf_filter", NULL);
+ if (IS_ERR_OR_NULL(filter_d)) {
+ pr_warning("unable to create debugfs directory for the LPF filter\n");
+ return;
+ }
+
+ dentry_f = debugfs_create_u32("alpha", S_IWUSR | S_IRUGO, filter_d,
+ &sensor_data->alpha);
+ if (IS_ERR_OR_NULL(dentry_f)) {
+ pr_warn("Unable to create debugfsfile: alpha\n");
+ return;
+ }
+}
+
+static struct thermal_zone_device_ops gpu_dummy_tz_ops = {
+ .get_temp = get_temp_for_gpu,
+};
+
+static struct thermal_zone_params gpu_dummy_tzp = {
+ .governor_name = "user_space",
+};
+
+static int scpi_thermal_probe(struct platform_device *pdev)
+{
+ struct scpi_sensor *sensor_data = &scpi_temp_sensor;
+ struct device_node *np;
+ int sensor, cpu;
+ int i;
+
+ if (!cpufreq_frequency_get_table(0)) {
+ dev_info(&pdev->dev,
+ "Frequency table not initialized. Deferring probe...\n");
+ return -EPROBE_DEFER;
+ }
+
+ scpi_ops = get_scpi_ops();
+ if (!scpi_ops)
+ return -EIO;
+
+ platform_set_drvdata(pdev, sensor_data);
+
+ for_each_possible_cpu(cpu) {
+ int cluster_id = topology_physical_package_id(cpu);
+ if (cluster_id > NUM_CLUSTERS) {
+ pr_warn("Cluster id: %d > %d\n", cluster_id, NUM_CLUSTERS);
+ goto error;
+ }
+
+ cpumask_set_cpu(cpu, &sensor_data->cluster[cluster_id]);
+ }
+
+ for (i = 0; i < NUM_CLUSTERS; i++) {
+ char node[16];
+ enum cluster_type cluster =
+ topology_physical_package_id(cpumask_any(&sensor_data->cluster[i]));
+
+ snprintf(node, 16, "cluster%d", i);
+ np = of_find_node_by_name(NULL, node);
+
+ if (!np)
+ dev_info(&pdev->dev, "Node not found: %s\n", node);
+
+ sensor_data->cdevs[i] =
+ of_cpufreq_power_cooling_register(np,
+ &sensor_data->cluster[i],
+ cluster_data[cluster].dyn_coeff,
+ get_static_power);
+
+ if (IS_ERR(sensor_data->cdevs[i]))
+ dev_warn(&pdev->dev,
+ "Error registering cooling device: %d\n", i);
+ }
+
+ if ((sensor = scpi_ops->sensor_get_id(SOC_SENSOR)) < 0) {
+ dev_warn(&pdev->dev, "%s not found. ret=%d\n", SOC_SENSOR, sensor);
+ goto error;
+ }
+
+ sensor_data->sensor_id = (u16)sensor;
+ dev_info(&pdev->dev, "Probed %s sensor. Id=%hu\n", SOC_SENSOR, sensor_data->sensor_id);
+
+ /*
+ * alpha ~= 2 / (N + 1) with N the window of a rolling mean We
+ * use 8-bit fixed point arithmetic. For a rolling average of
+ * window 20, alpha = 2 / (20 + 1) ~= 0.09523809523809523 .
+ * In 8-bit fixed point arigthmetic, 0.09523809523809523 * 256
+ * ~= 24
+ */
+ sensor_data->alpha = 24;
+
+ sensor_data->tzd = thermal_zone_of_sensor_register(&pdev->dev,
+ sensor_data->sensor_id,
+ sensor_data,
+ get_temp_value, NULL);
+
+ if (IS_ERR(sensor_data->tzd)) {
+ dev_warn(&pdev->dev, "Error registering sensor: %p\n", sensor_data->tzd);
+ return PTR_ERR(sensor_data->tzd);
+ }
+
+ sensor_data->gpu_tzd = thermal_zone_device_register("gpu", 0, 0,
+ sensor_data,
+ &gpu_dummy_tz_ops,
+ &gpu_dummy_tzp,
+ 0, 0);
+ if (IS_ERR(sensor_data->gpu_tzd)) {
+ int ret = PTR_ERR(sensor_data->gpu_tzd);
+
+ dev_warn(&pdev->dev, "Error register gpu thermal zone: %d\n",
+ ret);
+ return ret;
+ }
+
+ update_debugfs(sensor_data);
+
+ thermal_zone_device_update(sensor_data->tzd);
+
+ return 0;
+
+error:
+ return -ENODEV;
+}
+
+static int scpi_thermal_remove(struct platform_device *pdev)
+{
+ struct scpi_sensor *sensor = platform_get_drvdata(pdev);
+
+ thermal_zone_device_unregister(sensor->tzd);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct of_device_id scpi_thermal_of_match[] = {
+ { .compatible = "arm,scpi-thermal" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, scpi_thermal_of_match);
+
+static struct platform_driver scpi_thermal_platdrv = {
+ .driver = {
+ .name = "scpi-thermal",
+ .owner = THIS_MODULE,
+ .of_match_table = scpi_thermal_of_match,
+ },
+ .probe = scpi_thermal_probe,
+ .remove = scpi_thermal_remove,
+};
+module_platform_driver(scpi_thermal_platdrv);
+
+MODULE_LICENSE("GPL");
diff --git a/linaro/configs/vexpress64.conf b/linaro/configs/vexpress64.conf
index b9e099e4f9a3..985a69edf656 100644
--- a/linaro/configs/vexpress64.conf
+++ b/linaro/configs/vexpress64.conf
@@ -59,3 +59,14 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_PM_DEVFREQ=y
+CONFIG_DEVFREQ_THERMAL=y
+CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_DEVFREQ_GOV_PERFORMANCE=y
+CONFIG_SCPI_THERMAL=y
+CONFIG_MALI_DEVFREQ=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y