From patchwork Fri Apr 7 22:00:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Moritz Fischer X-Patchwork-Id: 9670517 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2D34E60365 for ; Fri, 7 Apr 2017 22:01:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1C3B228663 for ; Fri, 7 Apr 2017 22:01:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 10F302866C; Fri, 7 Apr 2017 22:01:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E27242866A for ; Fri, 7 Apr 2017 22:01:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756438AbdDGWAu (ORCPT ); Fri, 7 Apr 2017 18:00:50 -0400 Received: from mail-pg0-f44.google.com ([74.125.83.44]:33925 "EHLO mail-pg0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755905AbdDGWAk (ORCPT ); Fri, 7 Apr 2017 18:00:40 -0400 Received: by mail-pg0-f44.google.com with SMTP id 21so77828401pgg.1 for ; Fri, 07 Apr 2017 15:00:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ettus-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=ipftk7V5kGaSuWw2z0OtPz7u6Rqwhhffz2cuqo4yFzM=; b=rE2T//lcJvv6MWPdRS2Mz74XDJCEuVev/cK3uc+52/JbYz1ezdDjQ2iUXqE4pGoqTV KsgxfOhnmzMQOvBxxBD3WcBaKylCj/+Kb1xA48oJ5vPt3Dcaf4G7EeGj4MbjYZ890WVT oSJz3kDlGifdj74rvWQ6ZaQOu3AIZzrTLDTz6wWiyPQox+78DpevS6+k6PxQDYdORj/7 GHBUUSMmpEII/uW0VYdBF1eQq0M/LO2Zl5zKNEpXH7BRSfkrJ9JSRdjBOFh5cS1JIWH2 tmSyLvmX/Dc5JpY9VeYGb/uMbYFYe9AWkopxwNL0L1hB0JVjgg9LeJa7+8O+QT0PQyXo qdJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=ipftk7V5kGaSuWw2z0OtPz7u6Rqwhhffz2cuqo4yFzM=; b=it41WZMjNm5RbZlMgVFWA5ioBulS/2RDRrwHsGaUW8wb7N4D5IPFzsPl5qn5vzhjp2 R7rrCmmjiSDyUcg5U6qnrPKIjDRphIHVeLYrElWMiORyJI04j8nbFKbumf6cGAvvSD3E lj+VXtyzAJ0+AfwDlZYILULkGpVU0QqFlkNEZWRnhGCcMUKHq/nDep5DXv/3iHVDWwdF HAnJXa+4qLbTYTJoevfb0PmiBI4ulYOfMcDecFsKOcXe4dZ+flF/7smGD4fRmgOf4b2J ZG/1il4gpxoBmlr4KLrv8LM1k12uq23rwlYQjZuSF6N8/RRVfGr08/EuNgAz5Rprkog0 oeMg== X-Gm-Message-State: AFeK/H2Q6p9ZwfjuOx3ywb/0Y1pVd1JqpKzKerofMRJx/1u/bFP9j6dxvzr+fOcGzYacINWI X-Received: by 10.84.241.199 with SMTP id t7mr53051777plm.48.1491602439450; Fri, 07 Apr 2017 15:00:39 -0700 (PDT) Received: from tyrael.amer.corp.natinst.com (207-114-172-147.static.twtelecom.net. [207.114.172.147]) by smtp.gmail.com with ESMTPSA id i185sm6285569pge.48.2017.04.07.15.00.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 07 Apr 2017 15:00:38 -0700 (PDT) From: Moritz Fischer To: linux-hwmon@vger.kernel.org Cc: moritz.fischer@ettus.com, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, lee.jones@linaro.org, olof@lixom.net, linux@roeck-us.net, jdelvare@suse.com, robh+dt@kernel.org, mark.rutland@arm.com, Moritz Fischer Subject: [PATCH 3/3] hwmon: cros-ec-hwmon: Add Chromium-EC HWMON driver Date: Fri, 7 Apr 2017 15:00:10 -0700 Message-Id: <1491602410-31518-3-git-send-email-moritz.fischer@ettus.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1491602410-31518-1-git-send-email-moritz.fischer@ettus.com> References: <1491602410-31518-1-git-send-email-moritz.fischer@ettus.com> Sender: linux-hwmon-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Moritz Fischer This adds a hwmon driver for the Chromium EC's fans and temperature sensors. Signed-off-by: Moritz Fischer --- This one still needs some work, but I figured some early feedback might not hurt. Specifically I was wondering if using the devm_hwmon_register_with_info() is preferable to the devm_hwmon_register_with_groups(). The EC has a bunch of additional features such as setting thermal limits etc, which I'd still like to add but I figured I'll get some feedback on what I got so far. Thanks, Moritz --- drivers/hwmon/Kconfig | 8 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/cros-ec-hwmon.c | 244 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 drivers/hwmon/cros-ec-hwmon.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 0649d53f3..3b9155f 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1254,6 +1254,14 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. +config SENSORS_CROS_EC + tristate "ChromeOS EC hwmon" + depends on MFD_CROS_EC + help + If you say yes here you get hwmon support that will expose the + ChromeOS internal sensors for fanspeed and temperature to the + Linux hwmon subsystem. + source drivers/hwmon/pmbus/Kconfig config SENSORS_PWM_FAN diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 5509edf..e59b5da 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -134,6 +134,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o +obj-$(CONFIG_SENSORS_CROS_EC) += cros-ec-hwmon.o obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o diff --git a/drivers/hwmon/cros-ec-hwmon.c b/drivers/hwmon/cros-ec-hwmon.c new file mode 100644 index 0000000..29d8b06 --- /dev/null +++ b/drivers/hwmon/cros-ec-hwmon.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2017, National Instruments Corp. + * + * Chromium EC Fan speed and temperature sensor driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; 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 +#include +#include +#include +#include +#include + +struct cros_ec_hwmon_priv { + struct cros_ec_device *ec; + struct device *hwmon_dev; + + struct attribute **attrs; + + struct attribute_group attr_group; + const struct attribute_group *groups[2]; +}; + +#define KELVIN_TO_MILLICELSIUS(x) (((x) - 273) * 1000) + +static int __cros_ec_hwmon_probe_fans(struct cros_ec_hwmon_priv *priv) +{ + int err, idx; + uint16_t data; + + for (idx = 0; idx < EC_FAN_SPEED_ENTRIES; idx++) { + err = cros_ec_read_mapped_mem16(priv->ec, + EC_MEMMAP_FAN + 2 * idx, + &data); + if (err) + return err; + + if (data == EC_FAN_SPEED_NOT_PRESENT) + break; + } + + return idx; +} + +static int __cros_ec_hwmon_probe_temps(struct cros_ec_hwmon_priv *priv) +{ + uint8_t data; + int err, idx; + + err = cros_ec_read_mapped_mem8(priv->ec, EC_MEMMAP_THERMAL_VERSION, + &data); + + /* if we have a read error, or EC_MEMMAP_THERMAL_VERSION is not set, + * most likely we don't have temperature sensors ... + */ + if (err || !data) + return 0; + + for (idx = 0; idx < EC_TEMP_SENSOR_ENTRIES; idx++) { + err = cros_ec_read_mapped_mem8(priv->ec, + EC_MEMMAP_TEMP_SENSOR + idx, + &data); + if (err) + return idx; + + /* this assumes that they're all good up to idx */ + switch (data) { + case EC_TEMP_SENSOR_NOT_PRESENT: + case EC_TEMP_SENSOR_ERROR: + case EC_TEMP_SENSOR_NOT_POWERED: + case EC_TEMP_SENSOR_NOT_CALIBRATED: + return idx; + default: + continue; + }; + } + + return idx; +} + +static ssize_t cros_ec_hwmon_read_fan_rpm(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + uint16_t data; + int err; + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct cros_ec_hwmon_priv *priv = dev_get_drvdata(dev); + + err = cros_ec_read_mapped_mem16(priv->ec, + EC_MEMMAP_FAN + 2 * sattr->index, + &data); + if (err) + return err; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t cros_ec_hwmon_read_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + uint8_t data; + int err, tmp; + + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct cros_ec_hwmon_priv *priv = dev_get_drvdata(dev); + + err = cros_ec_read_mapped_mem8(priv->ec, + EC_MEMMAP_TEMP_SENSOR + 1 * sattr->index, + &data); + if (err) + return err; + + switch (data) { + case EC_TEMP_SENSOR_NOT_PRESENT: + case EC_TEMP_SENSOR_ERROR: + case EC_TEMP_SENSOR_NOT_POWERED: + case EC_TEMP_SENSOR_NOT_CALIBRATED: + dev_info(priv->ec->dev, "Failure: result=%d\n", data); + return -EIO; + } + + /* make sure we don't overflow when adding offset*/ + tmp = data + EC_TEMP_SENSOR_OFFSET; + + return sprintf(buf, "%d\n", KELVIN_TO_MILLICELSIUS(tmp)); +} + +static int cros_ec_hwmon_probe(struct platform_device *pdev) +{ + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_hwmon_priv *ec_hwmon; + struct sensor_device_attribute *attr; + int num_fans, num_temps, i; + + ec_hwmon = devm_kzalloc(&pdev->dev, sizeof(*ec_hwmon), GFP_KERNEL); + if (!ec_hwmon) + return -ENOMEM; + ec_hwmon->ec = ec; + + num_fans = __cros_ec_hwmon_probe_fans(ec_hwmon); + if (num_fans < 0) + return num_fans; + + num_temps = __cros_ec_hwmon_probe_temps(ec_hwmon); + if (num_fans < 0) + return num_temps; + + ec_hwmon->attrs = devm_kzalloc(&pdev->dev, + sizeof(*ec_hwmon->attrs) * + (num_fans + num_temps + 1), + GFP_KERNEL); + if (!ec_hwmon->attrs) + return -ENOMEM; + + for (i = 0; i < num_fans; i++) { + attr = devm_kzalloc(&pdev->dev, sizeof(*attr), GFP_KERNEL); + if (!attr) + return -ENOMEM; + sysfs_attr_init(&attr->dev_attr.attr); + attr->dev_attr.attr.name = devm_kasprintf(&pdev->dev, + GFP_KERNEL, + "fan%d_input", + i); + if (!attr->dev_attr.attr.name) + return -ENOMEM; + + attr->dev_attr.show = cros_ec_hwmon_read_fan_rpm; + attr->dev_attr.attr.mode = S_IRUGO; + attr->index = i; + ec_hwmon->attrs[i] = &attr->dev_attr.attr; + + } + + for (i = 0; i < num_temps; i++) { + attr = devm_kzalloc(&pdev->dev, sizeof(*attr), GFP_KERNEL); + if (!attr) + return -ENOMEM; + sysfs_attr_init(&attr->dev_attr.attr); + attr->dev_attr.attr.name = devm_kasprintf(&pdev->dev, + GFP_KERNEL, + "temp%d_input", + i); + if (!attr->dev_attr.attr.name) + return -ENOMEM; + + attr->dev_attr.show = cros_ec_hwmon_read_temp; + attr->dev_attr.attr.mode = S_IRUGO; + attr->index = i; + ec_hwmon->attrs[i + num_fans] = &attr->dev_attr.attr; + + } + + ec_hwmon->attr_group.attrs = ec_hwmon->attrs; + ec_hwmon->groups[0] = &ec_hwmon->attr_group; + + ec_hwmon->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, + "ec_hwmon", ec_hwmon, ec_hwmon->groups); + + if (IS_ERR(ec_hwmon->hwmon_dev)) + return PTR_ERR(ec_hwmon->hwmon_dev); + + platform_set_drvdata(pdev, ec_hwmon); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id cros_ec_hwmon_of_match[] = { + { .compatible = "google,cros-ec-hwmon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cros_ec_hwmon_of_match); +#endif + +static struct platform_driver cros_ec_hwmon_driver = { + .probe = cros_ec_hwmon_probe, + .driver = { + .name = "cros-ec-hwmon", + .of_match_table = of_match_ptr(cros_ec_hwmon_of_match), + }, +}; +module_platform_driver(cros_ec_hwmon_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC Hardware Monitor driver"); +MODULE_ALIAS("platform:cros-ec-hwmon"); +MODULE_AUTHOR("Moritz Fischer ");