diff options
author | Jon Medhurst <tixy@linaro.org> | 2015-08-04 16:13:51 +0100 |
---|---|---|
committer | Jon Medhurst <tixy@linaro.org> | 2015-08-04 16:13:51 +0100 |
commit | a26a830786c1b0864f554af76fdcf15aec2115ab (patch) | |
tree | ca406c7b7b8cf2b7f75d9d3b60e92d2a5beeee52 | |
parent | eac86f4dfd7d5705101f93c0e19ba7c799b03bc9 (diff) | |
parent | 3a2859d44938473bca16ab5b09e4db2d5392fcff (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.dts | 58 | ||||
-rw-r--r-- | drivers/thermal/Kconfig | 8 | ||||
-rw-r--r-- | drivers/thermal/Makefile | 1 | ||||
-rw-r--r-- | drivers/thermal/scpi-thermal.c | 308 | ||||
-rw-r--r-- | linaro/configs/vexpress64.conf | 11 |
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 |