From patchwork Fri Aug 4 14:31:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sudeep Holla X-Patchwork-Id: 109411 Delivered-To: patch@linaro.org Received: by 10.140.101.6 with SMTP id t6csp2235027qge; Fri, 4 Aug 2017 07:32:53 -0700 (PDT) X-Received: by 10.98.195.26 with SMTP id v26mr2773403pfg.118.1501857172950; Fri, 04 Aug 2017 07:32:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1501857172; cv=none; d=google.com; s=arc-20160816; b=mphOnU7hJlBKHkQDI/whIpKqeYc3085a4cZjmTUWmizOEE0ODYwZMhyewIUfZo4Xns 1FFaIEkioOkrsEN2IJw3t77maX5p/C0Ut9oHmI55TPtNNCMXShPr6fGb7TAQXZ1gU35T laYesE9wvyd8eYapS+OB9GsMd+fu8B+5eZybqsErSq2jHYCUi8cMQfLFO4MBS6UJsrCF 4G2+XZt2gd4HXsa9ezj90prLbH8UL17CwwXOfptrgOM0KZVuQzJhOBMNDo9CBF8O3hPo kNT4JQH6/jViE4AqknrnU0Qq7VHn2Ag6smUyTwB67FwTohGRHiDEcOpWMiSb/oN7LcMl 0slQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=dG/TR9/cD3OFRuHUrl73f/1oTDK1AYDreSwU0Idh2bk=; b=fhOEC79KYYxcfaTdSdGysTDNwhpYsXWrtFrXHfRZ4WkYP+5oi8ygyIVRaspMYdMzqf 43qWNDSb4YfNxs3KWkpspQlFVDJZIjsQ8cINR2TQ2+aCFozf8fiDSi9FTAtjFUFz9ggM iov6VmE0CZ27lipeenCEnIVVuA9z4IMM8pK9qWy8s01ypdQsB+Wu8giA/PuPvgUfvqLx J2Ts0mEIKqKwbW/ZkL5f4FgOm9Pk6YL/+mQwSWu2nffQe0hGUqwC6VUiWoKjXKGrUooK 4sIvd+94dlLX6QctbKqY2GukI3chr8DU0TAfdzauCozHiAdojxY2IXOIRWPkSRU8BjCj SlMw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 64si1209750ply.493.2017.08.04.07.32.52; Fri, 04 Aug 2017 07:32:52 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752789AbdHDOcs (ORCPT + 25 others); Fri, 4 Aug 2017 10:32:48 -0400 Received: from foss.arm.com ([217.140.101.70]:54454 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752594AbdHDOco (ORCPT ); Fri, 4 Aug 2017 10:32:44 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id DF76B1713; Fri, 4 Aug 2017 07:32:43 -0700 (PDT) Received: from e107155-lin.cambridge.arm.com (unknown [10.1.210.28]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 79B2C3F3E1; Fri, 4 Aug 2017 07:32:41 -0700 (PDT) From: Sudeep Holla To: ALKML , LKML , DTML Cc: Sudeep Holla , Roy Franz , Harb Abdulhamid , Nishanth Menon , Arnd Bergmann , Loc Ho , Alexey Klimov , Ryan Harkin , Jassi Brar , Guenter Roeck , linux-hwmon@vger.kernel.org Subject: [PATCH v2 16/18] hwmon: add support for sensors exported via ARM SCMI Date: Fri, 4 Aug 2017 15:31:42 +0100 Message-Id: <1501857104-11279-17-git-send-email-sudeep.holla@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1501857104-11279-1-git-send-email-sudeep.holla@arm.com> References: <1501857104-11279-1-git-send-email-sudeep.holla@arm.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Create a driver to add support for SoC sensors exported by the System Control Processor (SCP) via the System Control and Management Interface (SCMI). The supported sensor types is one of voltage, temperature, current, and power. The sensor labels and values provided by the SCP are exported via the hwmon sysfs interface. Cc: Guenter Roeck Cc: linux-hwmon@vger.kernel.org Signed-off-by: Sudeep Holla --- drivers/hwmon/Kconfig | 12 +++ drivers/hwmon/Makefile | 1 + drivers/hwmon/scmi-hwmon.c | 261 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 drivers/hwmon/scmi-hwmon.c -- 2.7.4 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5ef2814345ef..2bb63af8d674 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -321,6 +321,18 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_ARM_SCMI + tristate "ARM SCMI Sensors" + depends on ARM_SCMI_PROTOCOL + depends on THERMAL || !THERMAL_OF + help + This driver provides support for temperature, voltage, current + and power sensors available on SCMI based platforms. The actual + number and type of sensors exported depend on the platform. + + This driver can also be built as a module. If so, the module + will be called scmi-hwmon. + config SENSORS_ARM_SCPI tristate "ARM SCPI Sensors" depends on ARM_SCPI_PROTOCOL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d4641a9f16c1..02c3783c319f 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_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c new file mode 100644 index 000000000000..f38c9708d99c --- /dev/null +++ b/drivers/hwmon/scmi-hwmon.c @@ -0,0 +1,261 @@ +/* + * System Control and Management Interface(SCMI) based hwmon sensor driver + * + * Copyright (C) 2017 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 +#include + +struct sensor_data { + const struct scmi_sensor_info *info; + struct device_attribute dev_attr_input; + struct device_attribute dev_attr_label; + char input[20]; + char label[20]; +}; + +struct scmi_thermal_zone { + int sensor_id; + struct scmi_sensors *scmi_sensors; +}; + +struct scmi_sensors { + const struct scmi_handle *handle; + struct sensor_data *data; + struct list_head thermal_zones; + struct attribute **attrs; + struct attribute_group group; + const struct attribute_group *groups[2]; +}; + +static int scmi_read_temp(void *dev, int *temp) +{ + struct scmi_thermal_zone *zone = dev; + struct scmi_sensors *scmi_sensors = zone->scmi_sensors; + const struct scmi_handle *handle = scmi_sensors->handle; + struct sensor_data *sensor = &scmi_sensors->data[zone->sensor_id]; + u64 value; + int ret; + + ret = handle->sensor_ops->reading_get(handle, sensor->info->id, + false, &value); + if (ret) + return ret; + + *temp = value; + return 0; +} + +/* hwmon callback functions */ +static ssize_t +scmi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev); + const struct scmi_handle *handle = scmi_sensors->handle; + struct sensor_data *sensor; + u64 value; + int ret; + + sensor = container_of(attr, struct sensor_data, dev_attr_input); + + ret = handle->sensor_ops->reading_get(handle, sensor->info->id, + false, &value); + if (ret) + return ret; + + return sprintf(buf, "%llu\n", value); +} + +static ssize_t +scmi_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 struct thermal_zone_of_device_ops scmi_sensor_ops = { + .get_temp = scmi_read_temp, +}; + +static int scmi_hwmon_probe(struct platform_device *pdev) +{ + int idx; + u16 nr_sensors, i; + int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0; + int num_energy = 0; + struct device *hwdev, *dev = &pdev->dev; + struct scmi_sensors *scmi_sensors; + const struct scmi_handle *handle = devm_scmi_handle_get(dev); + + if (IS_ERR_OR_NULL(handle) || !handle->sensor_ops) + return -EPROBE_DEFER; + + nr_sensors = handle->sensor_ops->count_get(handle); + if (!nr_sensors) + return -EIO; + + scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL); + if (!scmi_sensors) + return -ENOMEM; + + scmi_sensors->data = devm_kcalloc(dev, nr_sensors, + sizeof(*scmi_sensors->data), + GFP_KERNEL); + if (!scmi_sensors->data) + return -ENOMEM; + + scmi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1, + sizeof(*scmi_sensors->attrs), + GFP_KERNEL); + if (!scmi_sensors->attrs) + return -ENOMEM; + + scmi_sensors->handle = handle; + + for (i = 0, idx = 0; i < nr_sensors; i++) { + struct sensor_data *sensor = &scmi_sensors->data[idx]; + + sensor->info = handle->sensor_ops->info_get(handle, i); + if (!sensor->info) + return PTR_ERR(sensor->info); + + switch (sensor->info->type) { + case TEMPERATURE_C: + 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; + case ENERGY: + snprintf(sensor->input, sizeof(sensor->input), + "energy%d_input", num_energy + 1); + snprintf(sensor->label, sizeof(sensor->input), + "energy%d_label", num_energy + 1); + num_energy++; + break; + default: + continue; + } + + sensor->dev_attr_input.attr.mode = S_IRUGO; + sensor->dev_attr_input.show = scmi_show_sensor; + sensor->dev_attr_input.attr.name = sensor->input; + + sensor->dev_attr_label.attr.mode = S_IRUGO; + sensor->dev_attr_label.show = scmi_show_label; + sensor->dev_attr_label.attr.name = sensor->label; + + scmi_sensors->attrs[idx << 1] = &sensor->dev_attr_input.attr; + scmi_sensors->attrs[(idx << 1) + 1] = + &sensor->dev_attr_label.attr; + + sysfs_attr_init(scmi_sensors->attrs[idx << 1]); + sysfs_attr_init(scmi_sensors->attrs[(idx << 1) + 1]); + idx++; + } + + scmi_sensors->group.attrs = scmi_sensors->attrs; + scmi_sensors->groups[0] = &scmi_sensors->group; + + platform_set_drvdata(pdev, scmi_sensors); + + hwdev = devm_hwmon_device_register_with_groups(dev, "scmi_sensors", + scmi_sensors, + scmi_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(&scmi_sensors->thermal_zones); + for (i = 0; i < nr_sensors; i++) { + struct sensor_data *sensor = &scmi_sensors->data[i]; + const struct scmi_sensor_info *info = sensor->info; + struct thermal_zone_device *z; + struct scmi_thermal_zone *zone; + + if (info->type != TEMPERATURE_C) + continue; + + zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL); + if (!zone) + return -ENOMEM; + + zone->sensor_id = i; + zone->scmi_sensors = scmi_sensors; + z = devm_thermal_zone_of_sensor_register(dev, info->id, zone, + &scmi_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(z)) { + devm_kfree(dev, zone); + continue; + } + } + + return 0; +} + +static struct platform_driver scmi_hwmon_platdrv = { + .driver = { + .name = "scmi-hwmon", + }, + .probe = scmi_hwmon_probe, +}; +module_platform_driver(scmi_hwmon_platdrv); + +MODULE_AUTHOR("Punit Agrawal "); +MODULE_DESCRIPTION("ARM SCMI HWMON interface driver"); +MODULE_LICENSE("GPL v2");