diff --git a/Documentation/hwmon/scpi-hwmon b/Documentation/hwmon/scpi-hwmon new file mode 100644 index 000000000000..4cfcdf2d5eab --- /dev/null +++ b/Documentation/hwmon/scpi-hwmon @@ -0,0 +1,33 @@ +Kernel driver scpi-hwmon +======================== + +Supported chips: + * Chips based on ARM System Control Processor Interface + Addresses scanned: - + Datasheet: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/index.html + +Author: Punit Agrawal + +Description +----------- + +This driver supports hardware monitoring for SoC's based on the ARM +System Control Processor (SCP) implementing the System Control +Processor Interface (SCPI). The following sensor types are supported +by the SCP - + + * temperature + * voltage + * current + * power + +The SCP interface provides an API to query the available sensors and +their values which are then exported to userspace by this driver. + +Usage Notes +----------- + +The driver relies on device tree node to indicate the presence of SCPI +support in the kernel. See +Documentation/devicetree/bindings/arm/arm,scpi.txt for details of the +devicetree node. \ No newline at end of file diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 500b262b89bb..4663693b58f6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -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 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9e0f3dd2841d..66e7a4715da7 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -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 diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c new file mode 100644 index 000000000000..c7d1d1423427 --- /dev/null +++ b/drivers/hwmon/scpi-hwmon.c @@ -0,0 +1,186 @@ +/* + * System Control and Power Interface(SCPI) based hwmon sensor driver + * + * Copyright (C) 2015 ARM Ltd. + * Punit Agrawal + * + * 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 +#include +#include +#include +#include +#include + +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_sensors { + struct scpi_ops *scpi_ops; + struct sensor_data *data; + struct attribute **attrs; + struct attribute_group group; + const struct attribute_group *groups[2]; +}; + +/* 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 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; + + hwdev = devm_hwmon_device_register_with_groups(dev, + "scpi_sensors", scpi_sensors, scpi_sensors->groups); + + return PTR_ERR_OR_ZERO(hwdev); +} + +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, +}; +module_platform_driver(scpi_hwmon_platdrv); + +MODULE_AUTHOR("Punit Agrawal "); +MODULE_DESCRIPTION("ARM SCPI HWMON interface driver"); +MODULE_LICENSE("GPL v2");