From patchwork Mon Jan 16 21:13:39 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: eajames.ibm@gmail.com X-Patchwork-Id: 9519491 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 A0462600C5 for ; Mon, 16 Jan 2017 21:14:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 90B3428111 for ; Mon, 16 Jan 2017 21:14:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 852C0281F9; Mon, 16 Jan 2017 21:14:53 +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_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, 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 B113528111 for ; Mon, 16 Jan 2017 21:14:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751490AbdAPVOW (ORCPT ); Mon, 16 Jan 2017 16:14:22 -0500 Received: from mail-io0-f196.google.com ([209.85.223.196]:34712 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751438AbdAPVOS (ORCPT ); Mon, 16 Jan 2017 16:14:18 -0500 Received: by mail-io0-f196.google.com with SMTP id c80so13845830iod.1; Mon, 16 Jan 2017 13:14:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=WQ2IPHV2/VtPd2sGuj30bwEuqQXXWt2Xg/QgJXE+ZkU=; b=D11XJasVJ6O0TwLqDWTmMSQjkW2JcTg7sT/IAw6PXAUFVKil7V9maUcL3DPIXxCqDv 5dDUNlK1egPKz6+VnA+BK19eJ2lIBu9bns7byU1b7Av7mkPRbZZ9x3CgzQbI2Q6pUTjo fH/kv8WY4RRWAy4GhrZ7kWLOddPBt0nNS0Xfx5zyBReV5q11vewUW+ekKuQ8Na3KGQUl FJ7eMzByTJKhg/UXYBK9E/HlqWCIRB5zCCrr5wJ9/KNkrvG7fVlKIeowJQ0NGD/ad7Mf 6kPq1OT/sbhqzoc4FRbBvO2BYC8U6L5fD3IoFw2wUeusC22cI/TIoACyUIbPiyWOaSTQ GUJw== 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=WQ2IPHV2/VtPd2sGuj30bwEuqQXXWt2Xg/QgJXE+ZkU=; b=QYygzzSRe6Pp5kBvI0MK+xMfyA0MC3Q5U4UmgTrVJ9x9uWuiNg7YDhUFXJ+z0KXMF6 Ry8Z/XW7ING80j/SGCc/0QNUrmF70uND7Pt9xclNr8qpJeE5Ts72mdIjV8vClSdk+Sjz lYfuwO09SzrvB4dtLxEgModz0Wy9p0msZ95jbWrZ7DUtt0TOkAwcG76Y50aZZqQfR6bm 8s77qjtxzcr7/ZtlKLOX4L85kg3HH2LAU0mm7TtFA399HGXdlhqlw8ih8emgAMDhhfrN M6B2rmhXkmSS8m71lSVbEzoCUlmg6ohOQK3WDGLjwQKR4DXju+PkpBUsQYCXywgJ1WU3 50Yg== X-Gm-Message-State: AIkVDXLGEc5No4AfAzdLlIjgywiz/WrtTq0EoKl/cX8fUrET1g6pdo0JawHjyuetzXzeNg== X-Received: by 10.107.32.199 with SMTP id g190mr31335941iog.178.1484601257579; Mon, 16 Jan 2017 13:14:17 -0800 (PST) Received: from eajames-austin-w350.austin.ibm.com ([32.97.110.55]) by smtp.gmail.com with ESMTPSA id i62sm6220452itb.12.2017.01.16.13.14.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 16 Jan 2017 13:14:16 -0800 (PST) From: eajames.ibm@gmail.com To: linux@roeck-us.net Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org, jdelvare@suse.com, corbet@lwn.net, mark.rutland@arm.com, robh+dt@kernel.org, wsa@the-dreams.de, andrew@aj.id.au, joel@jms.id.au, benh@kernel.crashing.org, "Edward A. James" Subject: [PATCH linux v3 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures Date: Mon, 16 Jan 2017 15:13:39 -0600 Message-Id: <1484601219-30196-7-git-send-email-eajames.ibm@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1484601219-30196-1-git-send-email-eajames.ibm@gmail.com> References: <1484601219-30196-1-git-send-email-eajames.ibm@gmail.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: "Edward A. James" Add functions to parse the data structures that are specific to the OCC on the POWER9 processor. These are the sensor data structures, including temperature, frequency, power, and "caps." Signed-off-by: Edward A. James Signed-off-by: Andrew Jeffery --- Documentation/hwmon/occ | 3 + drivers/hwmon/occ/occ_p9.c | 308 +++++++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/occ/occ_p9.h | 30 +++++ 3 files changed, 341 insertions(+) create mode 100644 drivers/hwmon/occ/occ_p9.c create mode 100644 drivers/hwmon/occ/occ_p9.h diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ index 143951e..6cea853 100644 --- a/Documentation/hwmon/occ +++ b/Documentation/hwmon/occ @@ -34,6 +34,9 @@ number of data structures, such as command format, response headers, and the like, are also defined in this specification, and are common to both POWER8 and POWER9 OCCs. +There is currently no public P9 OCC specification, and the data structures +defined in the POWER9 OCC driver are subject to change. + sysfs Entries ------------- diff --git a/drivers/hwmon/occ/occ_p9.c b/drivers/hwmon/occ/occ_p9.c new file mode 100644 index 0000000..d99a026 --- /dev/null +++ b/drivers/hwmon/occ/occ_p9.c @@ -0,0 +1,308 @@ +/* + * occ_p9.c - OCC hwmon driver + * + * This file contains the Power9-specific methods and data structures for + * the OCC hwmon driver. + * + * Copyright 2016 IBM Corp. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 "occ.h" +#include "occ_p9.h" + +/* P9 OCC sensor data format */ +struct p9_temp_sensor { + u32 sensor_id; + u8 fru_type; + u8 value; +}; + +struct p9_freq_sensor { + u32 sensor_id; + u16 value; +}; + +struct p9_power_sensor { + u32 sensor_id; + u8 function_id; + u8 apss_channel; + u16 reserved; + u32 update_tag; + u64 accumulator; + u16 value; +}; + +struct p9_caps_sensor { + u16 curr_powercap; + u16 curr_powerreading; + u16 norm_powercap; + u16 max_powercap; + u16 min_powercap; + u16 user_powerlimit; + u8 user_powerlimit_source; +}; + +static const u32 p9_sensor_hwmon_configs[MAX_OCC_SENSOR_TYPE] = { + HWMON_I_INPUT | HWMON_I_LABEL, /* freq: value | label */ + /* temp: value | label | fru_type */ + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_TYPE, + /* power: value | label | accum[0] | accum[1] | update_tag | + * (function_id | (apss_channel << 8)) + */ + HWMON_P_INPUT | HWMON_P_LABEL | HWMON_P_AVERAGE_MIN | + HWMON_P_AVERAGE_MAX | HWMON_P_AVERAGE_INTERVAL | + HWMON_P_RESET_HISTORY, + /* caps: curr | max | min | norm | user | source */ + HWMON_P_CAP | HWMON_P_CAP_MAX | HWMON_P_CAP_MIN | HWMON_P_MAX | + HWMON_P_ALARM | HWMON_P_CAP_ALARM, +}; + +void p9_parse_sensor(u8 *data, void *sensor, int sensor_type, int off, + int snum) +{ + switch (sensor_type) { + case FREQ: + { + struct p9_freq_sensor *fs = + &(((struct p9_freq_sensor *)sensor)[snum]); + + fs->sensor_id = be32_to_cpu(get_unaligned((u32 *)&data[off])); + fs->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 4])); + } + break; + case TEMP: + { + struct p9_temp_sensor *ts = + &(((struct p9_temp_sensor *)sensor)[snum]); + + ts->sensor_id = be32_to_cpu(get_unaligned((u32 *)&data[off])); + fs->fru_type = data[off + 4]; + fs->value = data[off + 5]; + } + break; + case POWER: + { + struct p9_power_sensor *ps = + &(((struct p9_power_sensor *)sensor)[snum]); + + ps->sensor_id = be32_to_cpu(get_unaligned((u32 *)&data[off])); + ps->function_id = data[off + 4]; + ps->apss_channel = data[off + 5]; + ps->update_tag = + be32_to_cpu(get_unaligned((u32 *)&data[off + 8])); + ps->accumulator = + be64_to_cpu(get_unaligned((u64 *)&data[off + 12])); + ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 20])); + } + break; + case CAPS: + { + struct p9_caps_sensor *cs = + &(((struct p9_caps_sensor *)sensor)[snum]); + + cs->curr_powercap = + be16_to_cpu(get_unaligned((u16 *)&data[off])); + cs->curr_powerreading = + be16_to_cpu(get_unaligned((u16 *)&data[off + 2])); + cs->norm_powercap = + be16_to_cpu(get_unaligned((u16 *)&data[off + 4])); + cs->max_powercap = + be16_to_cpu(get_unaligned((u16 *)&data[off + 6])); + cs->min_powercap = + be16_to_cpu(get_unaligned((u16 *)&data[off + 8])); + cs->user_powerlimit = + be16_to_cpu(get_unaligned((u16 *)&data[off + 10])); + cs->user_powerlimit_source = data[off + 12]; + } + break; + }; +} + +void *p9_alloc_sensor(int sensor_type, int num_sensors) +{ + switch (sensor_type) { + case FREQ: + return kcalloc(num_sensors, sizeof(struct p9_freq_sensor), + GFP_KERNEL); + case TEMP: + return kcalloc(num_sensors, sizeof(struct p9_temp_sensor), + GFP_KERNEL); + case POWER: + return kcalloc(num_sensors, sizeof(struct p9_power_sensor), + GFP_KERNEL); + case CAPS: + return kcalloc(num_sensors, sizeof(struct p9_caps_sensor), + GFP_KERNEL); + default: + return NULL; + } +} + +int p9_get_sensor(struct occ *driver, int sensor_type, int sensor_num, + u32 hwmon, long *val) +{ + int rc = 0; + void *sensor; + + if (sensor_type == POWER) { + if (hwmon == hwmon_power_cap || hwmon == hwmon_power_cap_max || + hwmon == hwmon_power_cap_min || hwmon == hwmon_power_max || + hwmon == hwmon_power_alarm || + hwmon == hwmon_power_cap_alarm) + sensor_type = CAPS; + } + + sensor = occ_get_sensor(driver, sensor_type); + if (!sensor) + return -ENODEV; + + switch (sensor_type) { + case FREQ: + { + struct p9_freq_sensor *fs = + &(((struct p9_freq_sensor *)sensor)[snum]); + + switch (hwmon) { + case hwmon_in_input: + *val = fs->value; + break; + case hwmon_in_label: + *val = fs->sensor_id; + break; + default: + rc = -EOPNOTSUPP; + } + } + break; + case TEMP: + { + struct p9_temp_sensor *ts = + &(((struct p9_temp_sensor *)sensor)[snum]); + + switch (hwmon) { + case hwmon_temp_input: + *val = ts->value; + break; + case hwmon_temp_type: + *val = ts->fru_type; + break; + case hwmon_temp_label: + *val = ts->sensor_id; + break; + default: + rc = -EOPNOTSUPP; + } + } + break; + case POWER: + { + struct p9_power_sensor *ps = + &(((struct p9_power_sensor *)sensor)[snum]); + + switch (hwmon) { + case hwmon_power_input: + *val = ps->value; + break; + case hwmon_power_label: + *val = ps->sensor_id; + break; + case hwmon_power_average_min: + *val = ((u32 *)(&ps->accumulator))[0]; + break; + case hwmon_power_average_max: + *val = ((u32 *)(&ps->accumulator))[1]; + break; + case hwmon_power_average_interval: + *val = ps->update_tag; + break; + case hwmon_power_reset_history: + *val = ps->function_id | (ps->apss_channel << 8); + break; + default: + rc = -EOPNOTSUPP; + } + } + break; + case CAPS: + { + struct p9_caps_sensor *cs = + &(((struct p9_caps_sensor *)sensor)[snum]); + + switch (hwmon) { + case hwmon_power_cap: + *val = cs->curr_powercap; + break; + case hwmon_power_cap_max: + *val = cs->max_powercap; + break; + case hwmon_power_cap_min: + *val = cs->min_powercap; + break; + case hwmon_power_max: + *val = cs->norm_powercap; + break; + case hwmon_power_alarm: + *val = cs->user_powerlimit; + break; + case hwmon_power_cap_alarm: + *val = cs->user_powerlimit_source; + break; + default: + rc = -EOPNOTSUPP; + } + } + break; + default: + rc = -EINVAL; + } + + return rc; +} + +static const struct occ_ops p9_ops = { + .parse_sensor = p9_parse_sensor, + .alloc_sensor = p9_alloc_sensor, + .get_sensor = p9_get_sensor, +}; + +static const struct occ_config p9_config = { + .command_addr = 0xFFFBE000, + .response_addr = 0xFFFBF000, +}; + +const u32 *p9_get_sensor_hwmon_configs() +{ + return p9_sensor_hwmon_configs; +} +EXPORT_SYMBOL(p9_get_sensor_hwmon_configs); + +struct occ *p9_occ_start(struct device *dev, void *bus, + struct occ_bus_ops *bus_ops) +{ + return occ_start(dev, bus, bus_ops, &p9_ops, &p9_config); +} +EXPORT_SYMBOL(p9_occ_start); + +MODULE_AUTHOR("Eddie James "); +MODULE_DESCRIPTION("P9 OCC sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/occ/occ_p9.h b/drivers/hwmon/occ/occ_p9.h new file mode 100644 index 0000000..18ca16a --- /dev/null +++ b/drivers/hwmon/occ/occ_p9.h @@ -0,0 +1,30 @@ +/* + * occ_p9.h - OCC hwmon driver + * + * This file contains Power9-specific function prototypes + * + * Copyright 2016 IBM Corp. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __OCC_P9_H__ +#define __OCC_P9_H__ + +#include "scom.h" + +struct device; + +const u32 *p9_get_sensor_hwmon_configs(void); +struct occ *p9_occ_start(struct device *dev, void *bus, + struct occ_bus_ops *bus_ops); + +#endif /* __OCC_P9_H__ */