Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp2510422imm; Thu, 19 Jul 2018 23:14:05 -0700 (PDT) X-Google-Smtp-Source: AAOMgpcLmsrgR3qq3joO85EcN5v6hzG4dzeawb1mo0VagTIzGYQmbV+I03qu1jlFLBe9dZPFrXMd X-Received: by 2002:a17:902:1682:: with SMTP id h2-v6mr767999plh.327.1532067245100; Thu, 19 Jul 2018 23:14:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1532067245; cv=none; d=google.com; s=arc-20160816; b=zCSwd2Y++E8G23HGRRbCpS912VKoEO93en+hqP5RjDVD93DLxJhO7pIhLLQSMo964s sKR+wNz7M2hvd8DjgjW7Nkj58VeFW6xlNBtECkWlzIxOHDjGCqzs2lGHUK0wvMiSqHlr DAvhM2wIai+anTjc1CGtK6Y0uJE1+kyKaL7a455yD3PhAkK4N7kD138appU2WIhEQFJe 0yDZConbCagKP9zcq9V2UJfa3LpPy8MGuRV6ZMpaAXaMb133jHRbEjUFceBeVItfqjyO kNCZWyauiMtyKmtyjphw0It+qsgE1MRrtCbu3EzZDOh4hlbmkjEjTMS6BInY4yf7Yf6B qqog== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:references:in-reply-to:date :subject:cc:to:from:arc-authentication-results; bh=lK1q21IBuaAo1d/vCzutbZmgG008G1lG82mpYH2e97k=; b=T8rNA9K3O71UqA9h3G3dr7UjVHWR6XFiyhZ66+Cwl6n+7VDtscvqOFWUEzU9fI9ozT FQlugymCdHwlzwWKlM0BHe/LwtGs+cYinwTu5vGxE8c75JQnClSq8L6RYxf4gNkWqTGG 9U6zkeaidOoN9OmOd13tZB6S2jIRLCTi8uwQHTIsyhVlbftqK0jIIFxAvhJ5UBNhl09n sy1GEuXX/LEqG3IEX26SZ9jDg/vCl7FtMIOXIE5EpUB2zoBatt9zorj24u89dnHpKa/0 qcC1gZBEOF2P7gcPsyWw00XgttUjmHLO4ZR3TxTtZ/rcr8Siuoci3LSptMJfNBgxS90d OYYw== 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ibm.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p16-v6si1189878pgc.82.2018.07.19.23.13.50; Thu, 19 Jul 2018 23:14:05 -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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ibm.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727825AbeGTG7Y (ORCPT + 99 others); Fri, 20 Jul 2018 02:59:24 -0400 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:37490 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727755AbeGTG7X (ORCPT ); Fri, 20 Jul 2018 02:59:23 -0400 Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.22/8.16.0.22) with SMTP id w6K6AdZ0078549 for ; Fri, 20 Jul 2018 02:12:46 -0400 Received: from e06smtp03.uk.ibm.com (e06smtp03.uk.ibm.com [195.75.94.99]) by mx0b-001b2d01.pphosted.com with ESMTP id 2kb98va4aq-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Fri, 20 Jul 2018 02:12:46 -0400 Received: from localhost by e06smtp03.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 20 Jul 2018 07:12:44 +0100 Received: from b06cxnps4074.portsmouth.uk.ibm.com (9.149.109.196) by e06smtp03.uk.ibm.com (192.168.101.133) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Fri, 20 Jul 2018 07:12:42 +0100 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id w6K6Cf6237355586 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 20 Jul 2018 06:12:41 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 35F0611C050; Fri, 20 Jul 2018 09:12:58 +0100 (BST) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 1A24711C04A; Fri, 20 Jul 2018 09:12:56 +0100 (BST) Received: from oc4502181600.ibm.com (unknown [9.79.210.147]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP; Fri, 20 Jul 2018 09:12:55 +0100 (BST) From: Shilpasri G Bhat To: mpe@ellerman.id.au, linux@roeck-us.net Cc: linuxppc-dev@lists.ozlabs.org, linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org, ego@linux.vnet.ibm.com, stewart@linux.ibm.com, Shilpasri G Bhat Subject: [PATCH v7 2/2] hwmon: ibmpowernv: Add attributes to enable/disable sensor groups Date: Fri, 20 Jul 2018 11:42:23 +0530 X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1532067143-22022-1-git-send-email-shilpa.bhat@linux.vnet.ibm.com> References: <1532067143-22022-1-git-send-email-shilpa.bhat@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 18072006-0012-0000-0000-0000028C728D X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 18072006-0013-0000-0000-000020BE3ECE Message-Id: <1532067143-22022-3-git-send-email-shilpa.bhat@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:,, definitions=2018-07-20_02:,, signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1806210000 definitions=main-1807200074 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org OPAL firmware provides the facility for some groups of sensors to be enabled/disabled at runtime to give the user the option of using the system resources for collecting these sensors or not. For example, on POWER9 systems, the On Chip Controller (OCC) gathers various system and chip level sensors and maintains their values in main memory. This patch provides support for enabling/disabling the sensor groups like power, temperature, current and voltage. Signed-off-by: Shilpasri G Bhat [stewart@linux.vnet.ibm.com: Commit message] --- Changes from v6: - Updated the commit message as per Stewart's suggestion - Use the compatible DT property instead of the path to find the node Documentation/hwmon/ibmpowernv | 43 ++++++- drivers/hwmon/ibmpowernv.c | 249 +++++++++++++++++++++++++++++++++++------ 2 files changed, 258 insertions(+), 34 deletions(-) diff --git a/Documentation/hwmon/ibmpowernv b/Documentation/hwmon/ibmpowernv index 8826ba2..5646825 100644 --- a/Documentation/hwmon/ibmpowernv +++ b/Documentation/hwmon/ibmpowernv @@ -33,9 +33,48 @@ fanX_input Measured RPM value. fanX_min Threshold RPM for alert generation. fanX_fault 0: No fail condition 1: Failing fan + tempX_input Measured ambient temperature. tempX_max Threshold ambient temperature for alert generation. -inX_input Measured power supply voltage +tempX_highest Historical maximum temperature +tempX_lowest Historical minimum temperature +tempX_enable Enable/disable all temperature sensors belonging to the + sub-group. In POWER9, this attribute corresponds to + each OCC. Using this attribute each OCC can be asked to + disable/enable all of its temperature sensors. + 1: Enable + 0: Disable + +inX_input Measured power supply voltage (millivolt) inX_fault 0: No fail condition. 1: Failing power supply. -power1_input System power consumption (microWatt) +inX_highest Historical maximum voltage +inX_lowest Historical minimum voltage +inX_enable Enable/disable all voltage sensors belonging to the + sub-group. In POWER9, this attribute corresponds to + each OCC. Using this attribute each OCC can be asked to + disable/enable all of its voltage sensors. + 1: Enable + 0: Disable + +powerX_input Power consumption (microWatt) +powerX_input_highest Historical maximum power +powerX_input_lowest Historical minimum power +powerX_enable Enable/disable all power sensors belonging to the + sub-group. In POWER9, this attribute corresponds to + each OCC. Using this attribute each OCC can be asked to + disable/enable all of its power sensors. + 1: Enable + 0: Disable + +currX_input Measured current (milliampere) +currX_highest Historical maximum current +currX_lowest Historical minimum current +currX_enable Enable/disable all current sensors belonging to the + sub-group. In POWER9, this attribute corresponds to + each OCC. Using this attribute each OCC can be asked to + disable/enable all of its current sensors. + 1: Enable + 0: Disable + +energyX_input Cumulative energy (microJoule) diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index f829dad..99afbf7 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -90,11 +90,20 @@ struct sensor_data { char label[MAX_LABEL_LEN]; char name[MAX_ATTR_LEN]; struct device_attribute dev_attr; + struct sensor_group_data *sgrp_data; +}; + +struct sensor_group_data { + struct mutex mutex; + u32 gid; + bool enable; }; struct platform_data { const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1]; + struct sensor_group_data *sgrp_data; u32 sensors_count; /* Total count of sensors from each group */ + u32 nr_sensor_groups; /* Total number of sensor groups */ }; static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, @@ -105,6 +114,9 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, ssize_t ret; u64 x; + if (sdata->sgrp_data && !sdata->sgrp_data->enable) + return -ENODATA; + ret = opal_get_sensor_data_u64(sdata->id, &x); if (ret) @@ -120,6 +132,46 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%llu\n", x); } +static ssize_t show_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_data *sdata = container_of(devattr, struct sensor_data, + dev_attr); + + return sprintf(buf, "%u\n", sdata->sgrp_data->enable); +} + +static ssize_t store_enable(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_data *sdata = container_of(devattr, struct sensor_data, + dev_attr); + struct sensor_group_data *sgrp_data = sdata->sgrp_data; + bool data; + int ret; + + ret = kstrtobool(buf, &data); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&sgrp_data->mutex); + if (ret) + return ret; + + if (data != sgrp_data->enable) { + ret = sensor_group_enable(sgrp_data->gid, data); + if (!ret) + sgrp_data->enable = data; + } + + if (!ret) + ret = count; + + mutex_unlock(&sgrp_data->mutex); + return ret; +} + static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -292,12 +344,126 @@ static u32 get_sensor_hwmon_index(struct sensor_data *sdata, return ++sensor_groups[sdata->type].hwmon_index; } +static int init_sensor_group_data(struct platform_device *pdev, + struct platform_data *pdata) +{ + struct sensor_group_data *sgrp_data; + struct device_node *groups, *sgrp; + enum sensors type; + int count = 0, ret = 0; + + groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); + if (!groups) + return ret; + + for_each_child_of_node(groups, sgrp) { + type = get_sensor_type(sgrp); + if (type != MAX_SENSOR_TYPE) + pdata->nr_sensor_groups++; + } + + if (!pdata->nr_sensor_groups) + goto out; + + sgrp_data = devm_kcalloc(&pdev->dev, pdata->nr_sensor_groups, + sizeof(*sgrp_data), GFP_KERNEL); + if (!sgrp_data) { + ret = -ENOMEM; + goto out; + } + + for_each_child_of_node(groups, sgrp) { + const __be32 *phandles; + int len, gid; + + type = get_sensor_type(sgrp); + if (type == MAX_SENSOR_TYPE) + continue; + + if (of_property_read_u32(sgrp, "sensor-group-id", &gid)) + continue; + + phandles = of_get_property(sgrp, "sensors", &len); + if (!phandles) + continue; + + len /= sizeof(u32); + if (!len) + continue; + + sensor_groups[type].attr_count++; + sgrp_data[count].gid = gid; + mutex_init(&sgrp_data[count].mutex); + sgrp_data[count++].enable = false; + } + + pdata->sgrp_data = sgrp_data; +out: + of_node_put(groups); + return ret; +} + +static struct sensor_group_data *get_sensor_group(struct platform_data *pdata, + struct device_node *node, + enum sensors gtype) +{ + struct sensor_group_data *sgrp_data = pdata->sgrp_data; + struct device_node *groups, *sgrp; + + groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); + if (!groups) + return NULL; + + for_each_child_of_node(groups, sgrp) { + const __be32 *phandles; + int len, gid, i; + enum sensors type; + + type = get_sensor_type(sgrp); + if (type != gtype) + continue; + + if (of_property_read_u32(sgrp, "sensor-group-id", &gid)) + continue; + + phandles = of_get_property(sgrp, "sensors", &len); + if (!phandles) + continue; + + len /= sizeof(u32); + if (!len) + continue; + + while (--len >= 0) + if (be32_to_cpu(phandles[len]) == node->phandle) + break; + + if (len < 0) + continue; + + for (i = 0; i < pdata->nr_sensor_groups; i++) + if (gid == sgrp_data[i].gid) { + of_node_put(sgrp); + of_node_put(groups); + return &sgrp_data[i]; + } + } + + of_node_put(groups); + return NULL; +} + static int populate_attr_groups(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; struct device_node *opal, *np; enum sensors type; + int ret; + + ret = init_sensor_group_data(pdev, pdata); + if (ret) + return ret; opal = of_find_node_by_path("/ibm,opal/sensors"); for_each_child_of_node(opal, np) { @@ -344,7 +510,10 @@ static int populate_attr_groups(struct platform_device *pdev) static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, ssize_t (*show)(struct device *dev, struct device_attribute *attr, - char *buf)) + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) { snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s", sensor_groups[sdata->type].name, sdata->hwmon_index, @@ -352,23 +521,33 @@ static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, sysfs_attr_init(&sdata->dev_attr.attr); sdata->dev_attr.attr.name = sdata->name; - sdata->dev_attr.attr.mode = S_IRUGO; sdata->dev_attr.show = show; + if (store) { + sdata->dev_attr.store = store; + sdata->dev_attr.attr.mode = 0664; + } else { + sdata->dev_attr.attr.mode = 0444; + } } static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid, const char *attr_name, enum sensors type, const struct attribute_group *pgroup, + struct sensor_group_data *sgrp_data, ssize_t (*show)(struct device *dev, struct device_attribute *attr, - char *buf)) + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) { sdata->id = sid; sdata->type = type; sdata->opal_index = od; sdata->hwmon_index = hd; - create_hwmon_attr(sdata, attr_name, show); + create_hwmon_attr(sdata, attr_name, show, store); pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr; + sdata->sgrp_data = sgrp_data; } static char *get_max_attr(enum sensors type) @@ -403,24 +582,23 @@ static int create_device_attrs(struct platform_device *pdev) const struct attribute_group **pgroups = pdata->attr_groups; struct device_node *opal, *np; struct sensor_data *sdata; - u32 sensor_id; - enum sensors type; u32 count = 0; - int err = 0; + u32 group_attr_id[MAX_SENSOR_TYPE] = {0}; - opal = of_find_node_by_path("/ibm,opal/sensors"); sdata = devm_kcalloc(&pdev->dev, pdata->sensors_count, sizeof(*sdata), GFP_KERNEL); - if (!sdata) { - err = -ENOMEM; - goto exit_put_node; - } + if (!sdata) + return -ENOMEM; + opal = of_find_node_by_path("/ibm,opal/sensors"); for_each_child_of_node(opal, np) { + struct sensor_group_data *sgrp_data; const char *attr_name; - u32 opal_index; + u32 opal_index, hw_id; + u32 sensor_id; const char *label; + enum sensors type; if (np->name == NULL) continue; @@ -456,14 +634,12 @@ static int create_device_attrs(struct platform_device *pdev) opal_index = INVALID_INDEX; } - sdata[count].opal_index = opal_index; - sdata[count].hwmon_index = - get_sensor_hwmon_index(&sdata[count], sdata, count); - - create_hwmon_attr(&sdata[count], attr_name, show_sensor); - - pgroups[type]->attrs[sensor_groups[type].attr_count++] = - &sdata[count++].dev_attr.attr; + hw_id = get_sensor_hwmon_index(&sdata[count], sdata, count); + sgrp_data = get_sensor_group(pdata, np, type); + populate_sensor(&sdata[count], opal_index, hw_id, sensor_id, + attr_name, type, pgroups[type], sgrp_data, + show_sensor, NULL); + count++; if (!of_property_read_string(np, "label", &label)) { /* @@ -474,35 +650,43 @@ static int create_device_attrs(struct platform_device *pdev) */ make_sensor_label(np, &sdata[count], label); - populate_sensor(&sdata[count], opal_index, - sdata[count - 1].hwmon_index, + populate_sensor(&sdata[count], opal_index, hw_id, sensor_id, "label", type, pgroups[type], - show_label); + NULL, show_label, NULL); count++; } if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) { attr_name = get_max_attr(type); - populate_sensor(&sdata[count], opal_index, - sdata[count - 1].hwmon_index, + populate_sensor(&sdata[count], opal_index, hw_id, sensor_id, attr_name, type, - pgroups[type], show_sensor); + pgroups[type], sgrp_data, show_sensor, + NULL); count++; } if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) { attr_name = get_min_attr(type); - populate_sensor(&sdata[count], opal_index, - sdata[count - 1].hwmon_index, + populate_sensor(&sdata[count], opal_index, hw_id, sensor_id, attr_name, type, - pgroups[type], show_sensor); + pgroups[type], sgrp_data, show_sensor, + NULL); + count++; + } + + if (sgrp_data && !sgrp_data->enable) { + sgrp_data->enable = true; + hw_id = ++group_attr_id[type]; + populate_sensor(&sdata[count], opal_index, hw_id, + sgrp_data->gid, "enable", type, + pgroups[type], sgrp_data, show_enable, + store_enable); count++; } } -exit_put_node: of_node_put(opal); - return err; + return 0; } static int ibmpowernv_probe(struct platform_device *pdev) @@ -517,6 +701,7 @@ static int ibmpowernv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pdata); pdata->sensors_count = 0; + pdata->nr_sensor_groups = 0; err = populate_attr_groups(pdev); if (err) return err; -- 1.8.3.1