mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
ARM System Control and Power Interface(SCPI) support
It adds support for the following features provided by SCP firmware using different subsystems in Linux: 1. SCPI mailbox protocol driver which using mailbox framework 2. Clocks provided by SCP using clock framework 3. CPU DVFS(cpufreq) using existing arm-big-little driver 4. SCPI based sensors including temperature sensors -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWF7ifAAoJEABBurwxfuKYAfMP/34ka/n4+U/aPQXzStNIwr3v Nme9WSf3mUPv26MstRDrWRYi1G2WLOTlc196MpdIt6m6QLOjxzEl3tSq5ILrj7yN KoLojtISmu/pbhVcJN5fllxgpcJzufLoEWBa5T/Y/4GoIhh1NCYa82QpNgzPmsMd rPCkYHqwT6I3sIS+/mbDkGA/QnwJ2qtJ8sp3+fL+dyJbI7Aa1zJZP6ectPsxK22+ HFoFTY45rdFv/ojZZFZL8E/gcblYwRWKzIgwdASHuDXxIhd/IPwjrex2Iyv75AQK zusRQ5Xv82GaYWHVa9GXmZqXkTsvBg4AJwc4Uq2JdB0qOi2a4tc8PkK7Ts5YdHgS YVGxbY1POtMBi2bJUjsviMY7dGR3I+iEXJTYnbPnkVa+GTv8/FViVmOOLQnnBF4R fN5FN0vfuL6zaQzOPYLGx3SuEHix3ko2DCAcMg6idIxuBHArlJuS7XKECWdHuc0+ +qn6Iqf8YSKIZ1zrWMggqY/sXuxjtABUBXe3jP3iTKQh8h+9SLfN3wgQM4GFJJcB gNfvk3Hl5aPFy/7gsgSDlaYbhGKPwTup+R8Fqd6nSBQO+rpRXvQQftwigYQiIEcE IiOS3BntVQWjoVr9WIifguf6rHG1ZoSMTHdtVVEaqsspT/OGJyq/ynEFJYSFqcqX NRPdQJNuoXGolGhyoWxD =9u+p -----END PGP SIGNATURE----- Merge tag 'arm-scpi-for-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into next/drivers Merge "ARM System Control and Power Interface(SCPI) support" from Sudeep Holla It adds support for the following features provided by SCP firmware using different subsystems in Linux: 1. SCPI mailbox protocol driver which using mailbox framework 2. Clocks provided by SCP using clock framework 3. CPU DVFS(cpufreq) using existing arm-big-little driver 4. SCPI based sensors including temperature sensors * tag 'arm-scpi-for-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: hwmon: Support thermal zones registration for SCP temperature sensors hwmon: Support sensors exported via ARM SCP interface firmware: arm_scpi: Extend to support sensors Documentation: add DT bindings for ARM SCPI sensors cpufreq: arm_big_little: add SCPI interface driver clk: scpi: add support for cpufreq virtual device clk: add support for clocks provided by SCP(System Control Processor) firmware: add support for ARM System Control and Power Interface(SCPI) protocol Documentation: add DT binding for ARM System Control and Power Interface(SCPI) protocol
This commit is contained in:
commit
c049adc9fd
16 changed files with 1868 additions and 0 deletions
|
|
@ -321,6 +321,14 @@ config SENSORS_APPLESMC
|
|||
Say Y here if you have an applicable laptop and want to experience
|
||||
the awesome power of applesmc.
|
||||
|
||||
config SENSORS_ARM_SCPI
|
||||
tristate "ARM SCPI Sensors"
|
||||
depends on ARM_SCPI_PROTOCOL
|
||||
help
|
||||
This driver provides support for temperature, voltage, current
|
||||
and power sensors available on ARM Ltd's SCP based platforms. The
|
||||
actual number and type of sensors exported depend on the platform.
|
||||
|
||||
config SENSORS_ASB100
|
||||
tristate "Asus ASB100 Bach"
|
||||
depends on X86 && I2C
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
|
|||
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
|
||||
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
|
||||
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
||||
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
|
||||
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||
|
|
|
|||
288
drivers/hwmon/scpi-hwmon.c
Normal file
288
drivers/hwmon/scpi-hwmon.c
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* System Control and Power Interface(SCPI) based hwmon sensor driver
|
||||
*
|
||||
* Copyright (C) 2015 ARM Ltd.
|
||||
* Punit Agrawal <punit.agrawal@arm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scpi_protocol.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
struct sensor_data {
|
||||
struct scpi_sensor_info info;
|
||||
struct device_attribute dev_attr_input;
|
||||
struct device_attribute dev_attr_label;
|
||||
char input[20];
|
||||
char label[20];
|
||||
};
|
||||
|
||||
struct scpi_thermal_zone {
|
||||
struct list_head list;
|
||||
int sensor_id;
|
||||
struct scpi_sensors *scpi_sensors;
|
||||
struct thermal_zone_device *tzd;
|
||||
};
|
||||
|
||||
struct scpi_sensors {
|
||||
struct scpi_ops *scpi_ops;
|
||||
struct sensor_data *data;
|
||||
struct list_head thermal_zones;
|
||||
struct attribute **attrs;
|
||||
struct attribute_group group;
|
||||
const struct attribute_group *groups[2];
|
||||
};
|
||||
|
||||
static int scpi_read_temp(void *dev, int *temp)
|
||||
{
|
||||
struct scpi_thermal_zone *zone = dev;
|
||||
struct scpi_sensors *scpi_sensors = zone->scpi_sensors;
|
||||
struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
|
||||
struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id];
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*temp = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hwmon callback functions */
|
||||
static ssize_t
|
||||
scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev);
|
||||
struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
|
||||
struct sensor_data *sensor;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
sensor = container_of(attr, struct sensor_data, dev_attr_input);
|
||||
|
||||
ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", value);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_data *sensor;
|
||||
|
||||
sensor = container_of(attr, struct sensor_data, dev_attr_label);
|
||||
|
||||
return sprintf(buf, "%s\n", sensor->info.name);
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_thermal_zones(struct platform_device *pdev,
|
||||
struct scpi_sensors *scpi_sensors)
|
||||
{
|
||||
struct list_head *pos;
|
||||
|
||||
list_for_each(pos, &scpi_sensors->thermal_zones) {
|
||||
struct scpi_thermal_zone *zone;
|
||||
|
||||
zone = list_entry(pos, struct scpi_thermal_zone, list);
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, zone->tzd);
|
||||
}
|
||||
}
|
||||
|
||||
static struct thermal_zone_of_device_ops scpi_sensor_ops = {
|
||||
.get_temp = scpi_read_temp,
|
||||
};
|
||||
|
||||
static int scpi_hwmon_probe(struct platform_device *pdev)
|
||||
{
|
||||
u16 nr_sensors, i;
|
||||
int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
|
||||
struct scpi_ops *scpi_ops;
|
||||
struct device *hwdev, *dev = &pdev->dev;
|
||||
struct scpi_sensors *scpi_sensors;
|
||||
int ret;
|
||||
|
||||
scpi_ops = get_scpi_ops();
|
||||
if (!scpi_ops)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = scpi_ops->sensor_get_capability(&nr_sensors);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!nr_sensors)
|
||||
return -ENODEV;
|
||||
|
||||
scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL);
|
||||
if (!scpi_sensors)
|
||||
return -ENOMEM;
|
||||
|
||||
scpi_sensors->data = devm_kcalloc(dev, nr_sensors,
|
||||
sizeof(*scpi_sensors->data), GFP_KERNEL);
|
||||
if (!scpi_sensors->data)
|
||||
return -ENOMEM;
|
||||
|
||||
scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1,
|
||||
sizeof(*scpi_sensors->attrs), GFP_KERNEL);
|
||||
if (!scpi_sensors->attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
scpi_sensors->scpi_ops = scpi_ops;
|
||||
|
||||
for (i = 0; i < nr_sensors; i++) {
|
||||
struct sensor_data *sensor = &scpi_sensors->data[i];
|
||||
|
||||
ret = scpi_ops->sensor_get_info(i, &sensor->info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (sensor->info.class) {
|
||||
case TEMPERATURE:
|
||||
snprintf(sensor->input, sizeof(sensor->input),
|
||||
"temp%d_input", num_temp + 1);
|
||||
snprintf(sensor->label, sizeof(sensor->input),
|
||||
"temp%d_label", num_temp + 1);
|
||||
num_temp++;
|
||||
break;
|
||||
case VOLTAGE:
|
||||
snprintf(sensor->input, sizeof(sensor->input),
|
||||
"in%d_input", num_volt);
|
||||
snprintf(sensor->label, sizeof(sensor->input),
|
||||
"in%d_label", num_volt);
|
||||
num_volt++;
|
||||
break;
|
||||
case CURRENT:
|
||||
snprintf(sensor->input, sizeof(sensor->input),
|
||||
"curr%d_input", num_current + 1);
|
||||
snprintf(sensor->label, sizeof(sensor->input),
|
||||
"curr%d_label", num_current + 1);
|
||||
num_current++;
|
||||
break;
|
||||
case POWER:
|
||||
snprintf(sensor->input, sizeof(sensor->input),
|
||||
"power%d_input", num_power + 1);
|
||||
snprintf(sensor->label, sizeof(sensor->input),
|
||||
"power%d_label", num_power + 1);
|
||||
num_power++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sensor->dev_attr_input.attr.mode = S_IRUGO;
|
||||
sensor->dev_attr_input.show = scpi_show_sensor;
|
||||
sensor->dev_attr_input.attr.name = sensor->input;
|
||||
|
||||
sensor->dev_attr_label.attr.mode = S_IRUGO;
|
||||
sensor->dev_attr_label.show = scpi_show_label;
|
||||
sensor->dev_attr_label.attr.name = sensor->label;
|
||||
|
||||
scpi_sensors->attrs[i << 1] = &sensor->dev_attr_input.attr;
|
||||
scpi_sensors->attrs[(i << 1) + 1] = &sensor->dev_attr_label.attr;
|
||||
|
||||
sysfs_attr_init(scpi_sensors->attrs[i << 1]);
|
||||
sysfs_attr_init(scpi_sensors->attrs[(i << 1) + 1]);
|
||||
}
|
||||
|
||||
scpi_sensors->group.attrs = scpi_sensors->attrs;
|
||||
scpi_sensors->groups[0] = &scpi_sensors->group;
|
||||
|
||||
platform_set_drvdata(pdev, scpi_sensors);
|
||||
|
||||
hwdev = devm_hwmon_device_register_with_groups(dev,
|
||||
"scpi_sensors", scpi_sensors, scpi_sensors->groups);
|
||||
|
||||
if (IS_ERR(hwdev))
|
||||
return PTR_ERR(hwdev);
|
||||
|
||||
/*
|
||||
* Register the temperature sensors with the thermal framework
|
||||
* to allow their usage in setting up the thermal zones from
|
||||
* device tree.
|
||||
*
|
||||
* NOTE: Not all temperature sensors maybe used for thermal
|
||||
* control
|
||||
*/
|
||||
INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
|
||||
for (i = 0; i < nr_sensors; i++) {
|
||||
struct sensor_data *sensor = &scpi_sensors->data[i];
|
||||
struct scpi_thermal_zone *zone;
|
||||
|
||||
if (sensor->info.class != TEMPERATURE)
|
||||
continue;
|
||||
|
||||
zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
|
||||
if (!zone) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_tzd;
|
||||
}
|
||||
|
||||
zone->sensor_id = i;
|
||||
zone->scpi_sensors = scpi_sensors;
|
||||
zone->tzd = thermal_zone_of_sensor_register(dev, i, zone,
|
||||
&scpi_sensor_ops);
|
||||
/*
|
||||
* The call to thermal_zone_of_sensor_register returns
|
||||
* an error for sensors that are not associated with
|
||||
* any thermal zones or if the thermal subsystem is
|
||||
* not configured.
|
||||
*/
|
||||
if (IS_ERR(zone->tzd)) {
|
||||
devm_kfree(dev, zone);
|
||||
continue;
|
||||
}
|
||||
list_add(&zone->list, &scpi_sensors->thermal_zones);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_tzd:
|
||||
unregister_thermal_zones(pdev, scpi_sensors);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scpi_hwmon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct scpi_sensors *scpi_sensors = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_thermal_zones(pdev, scpi_sensors);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id scpi_of_match[] = {
|
||||
{.compatible = "arm,scpi-sensors"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver scpi_hwmon_platdrv = {
|
||||
.driver = {
|
||||
.name = "scpi-hwmon",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = scpi_of_match,
|
||||
},
|
||||
.probe = scpi_hwmon_probe,
|
||||
.remove = scpi_hwmon_remove,
|
||||
};
|
||||
module_platform_driver(scpi_hwmon_platdrv);
|
||||
|
||||
MODULE_AUTHOR("Punit Agrawal <[email protected]>");
|
||||
MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Loading…
Reference in a new issue