From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D638C43381 for ; Sun, 24 Feb 2019 21:36:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C993E2084D for ; Sun, 24 Feb 2019 21:36:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QVccsFg9" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726554AbfBXVgo (ORCPT ); Sun, 24 Feb 2019 16:36:44 -0500 Received: from mail-wm1-f67.google.com ([209.85.128.67]:53774 "EHLO mail-wm1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725991AbfBXVgo (ORCPT ); Sun, 24 Feb 2019 16:36:44 -0500 Received: by mail-wm1-f67.google.com with SMTP id e74so6257736wmg.3 for ; Sun, 24 Feb 2019 13:36:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:from:to:cc:references:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=e7TtQYKRuLRP22zW450f9ypegUM+jXjI52i2aqfEs/c=; b=QVccsFg9KKzcfEzJkKPhnGR8BTK9xEVu0J1Mwb9CjZPHo0IYknOcxNHTKhgZa65vdH 0hOIrGGWoAFAtsEkDVIvbdwuNWSMucHDQwIlSjwev2goQ0/rPPdqikUexg9/CKsQVEc0 DAUYuRYSIqS4IBqBfzwLHvlat7JBg3zrh7zaj9uGwn6SEY851C9rcI4EF+vXIeQaVgH3 CPLPZg7iMETBVw3fYz6xduXBKWe8hRfhGFZJYIOVBXHJ85XIhexzESAGi0ukaXMw/7Iw QaOVCTzd42d7dyWZcnkkmvTdxzDTznpuCjASVDW+Nd4TaKhAFAC1PFXpNxA/g7lnCQFV hqRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:references:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=e7TtQYKRuLRP22zW450f9ypegUM+jXjI52i2aqfEs/c=; b=SRi9KVKDjaYUIKvIPR65JqNqtw1T6+k6N46UmI3IKky+2LcuaiH+8OSqNru6+dNw/1 /CAGhMwwXQXrnsXvpwP/bHnPoGo7TsJGdq+pS/VcIiIHV0BCEhxAGZddGI/y2gKuqy0q ut57oVvE1o9r8NeZRvGrsZ9oWRWB7DMTpHMgS0qBbTH1MpKm63QPM9HmaN4tU9PM166H 85nkqHdvdWcMJOTs5T9/qE7aBN45x6tK84xqTsvteHaeHpmLo1+jMld6TT02rGpl55t1 mQaJxU5tZM5QIVpS8eNIWYM4Dis62vl+5lL/xcytx3tfCnNNO1AwJS0lDE4mCM3KF5pw omfw== X-Gm-Message-State: AHQUAubLivqtUBfUcZJL6ydyCiQGsFjJC8ECrcYZg6GtfqfwZZL/GeB9 T7bp3A03YPxCZ9QfdDc9t9fQriQZ X-Google-Smtp-Source: AHgI3IZccGxOf2yZx6IsHlHSnFCiW/KPsq0amgRttLEgHf4Y4El5QjH1tB+9edBEg+ZCtHDqlvz1Jg== X-Received: by 2002:a1c:14e:: with SMTP id 75mr8161584wmb.48.1551044200707; Sun, 24 Feb 2019 13:36:40 -0800 (PST) Received: from ?IPv6:2003:ea:8bf1:e200:1133:81d:72d3:c59f? (p200300EA8BF1E2001133081D72D3C59F.dip0.t-ipconnect.de. [2003:ea:8bf1:e200:1133:81d:72d3:c59f]) by smtp.googlemail.com with ESMTPSA id z1sm5429596wrw.28.2019.02.24.13.36.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 24 Feb 2019 13:36:40 -0800 (PST) Subject: [PATCH net-next v2 2/2] net: phy: aquantia: add hwmon support From: Heiner Kallweit To: Andrew Lunn , Florian Fainelli , David Miller Cc: "netdev@vger.kernel.org" References: Message-ID: <2b46ac62-db6f-b72c-e81b-c6fbf063ab4b@gmail.com> Date: Sun, 24 Feb 2019 22:36:31 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101 Thunderbird/60.5.1 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This adds HWMON support for the temperature sensor and the related alarms on the 107/108/109 chips. This patch is based on work from Nikita and Andrew. I added: - support for changing alarm thresholds via sysfs - move HWMON code to a separate source file to improve maintainability - smaller changes like using IS_REACHABLE instead of ifdef (avoids problems if PHY driver is built in and HWMON is a module) v2: - remove struct aqr_priv - rename header file to aquantia.h Signed-off-by: Nikita Yushchenko Signed-off-by: Andrew Lunn Signed-off-by: Heiner Kallweit --- drivers/net/phy/Makefile | 2 +- drivers/net/phy/aquantia.h | 16 ++ drivers/net/phy/aquantia_hwmon.c | 245 +++++++++++++++++++++++++++++++ drivers/net/phy/aquantia_main.c | 4 + 4 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 drivers/net/phy/aquantia.h create mode 100644 drivers/net/phy/aquantia_hwmon.c diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index b0845adaf..c48596626 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -45,7 +45,7 @@ sfp-obj-$(CONFIG_SFP) += sfp-bus.o obj-y += $(sfp-obj-y) $(sfp-obj-m) obj-$(CONFIG_AMD_PHY) += amd.o -aquantia-objs += aquantia_main.o +aquantia-objs += aquantia_main.o aquantia_hwmon.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o obj-$(CONFIG_ASIX_PHY) += asix.o obj-$(CONFIG_AT803X_PHY) += at803x.o diff --git a/drivers/net/phy/aquantia.h b/drivers/net/phy/aquantia.h new file mode 100644 index 000000000..5a16caab7 --- /dev/null +++ b/drivers/net/phy/aquantia.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 + * HWMON driver for Aquantia PHY + * + * Author: Nikita Yushchenko + * Author: Andrew Lunn + * Author: Heiner Kallweit + */ + +#include +#include + +#if IS_REACHABLE(CONFIG_HWMON) +int aqr_hwmon_probe(struct phy_device *phydev); +#else +static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } +#endif diff --git a/drivers/net/phy/aquantia_hwmon.c b/drivers/net/phy/aquantia_hwmon.c new file mode 100644 index 000000000..9c77d149e --- /dev/null +++ b/drivers/net/phy/aquantia_hwmon.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* HWMON driver for Aquantia PHY + * + * Author: Nikita Yushchenko + * Author: Andrew Lunn + * Author: Heiner Kallweit + */ + +#include +#include +#include +#include + +#include "aquantia.h" + +/* Vendor specific 1, MDIO_MMD_VEND2 */ +#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 +#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 +#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 +#define VEND1_THERMAL_STAT1 0xc820 +#define VEND1_THERMAL_STAT2 0xc821 +#define VEND1_THERMAL_STAT2_VALID BIT(0) +#define VEND1_GENERAL_STAT1 0xc830 +#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) +#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) +#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) +#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) + +#if IS_REACHABLE(CONFIG_HWMON) + +static umode_t aqr_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_lcrit_alarm: + case hwmon_temp_crit_alarm: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_lcrit: + case hwmon_temp_crit: + return 0644; + default: + return 0; + } +} + +static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value) +{ + int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); + + if (temp < 0) + return temp; + + /* register value is 2's complement with LSB = 1/256th degree Celsius */ + if (temp > 0x8000) + temp -= 0x10000; + + *value = temp * 1000 / 256; + + return 0; +} + +static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value) +{ + int temp; + + if (value >= 128000 || value < -128000) + return -ERANGE; + + temp = value * 256 / 1000; + + if (temp < 0) + temp += 0x10000; + + return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, temp); +} + +static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value) +{ + int reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GENERAL_STAT1); + + if (reg < 0) + return reg; + + *value = !!(reg & bit); + + return 0; +} + +static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *value) +{ + struct phy_device *phydev = dev_get_drvdata(dev); + int reg; + + if (type != hwmon_temp) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: + reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, + VEND1_THERMAL_STAT2); + if (reg < 0) + return reg; + if (!(reg & VEND1_THERMAL_STAT2_VALID)) + return -EIO; + + return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value); + + case hwmon_temp_lcrit: + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, + value); + case hwmon_temp_min: + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, + value); + case hwmon_temp_max: + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, + value); + case hwmon_temp_crit: + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, + value); + case hwmon_temp_lcrit_alarm: + return aqr_hwmon_status1(phydev, + VEND1_GENERAL_STAT1_LOW_TEMP_FAIL, + value); + case hwmon_temp_min_alarm: + return aqr_hwmon_status1(phydev, + VEND1_GENERAL_STAT1_LOW_TEMP_WARN, + value); + case hwmon_temp_max_alarm: + return aqr_hwmon_status1(phydev, + VEND1_GENERAL_STAT1_HIGH_TEMP_WARN, + value); + case hwmon_temp_crit_alarm: + return aqr_hwmon_status1(phydev, + VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL, + value); + default: + return -EOPNOTSUPP; + } +} + +static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long value) +{ + struct phy_device *phydev = dev_get_drvdata(dev); + + if (type != hwmon_temp) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_lcrit: + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, + value); + case hwmon_temp_min: + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, + value); + case hwmon_temp_max: + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, + value); + case hwmon_temp_crit: + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, + value); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops aqr_hwmon_ops = { + .is_visible = aqr_hwmon_is_visible, + .read = aqr_hwmon_read, + .write = aqr_hwmon_write, +}; + +static u32 aqr_hwmon_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0, +}; + +static const struct hwmon_channel_info aqr_hwmon_chip = { + .type = hwmon_chip, + .config = aqr_hwmon_chip_config, +}; + +static u32 aqr_hwmon_temp_config[] = { + HWMON_T_INPUT | + HWMON_T_MAX | HWMON_T_MIN | + HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | + HWMON_T_CRIT | HWMON_T_LCRIT | + HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, + 0, +}; + +static const struct hwmon_channel_info aqr_hwmon_temp = { + .type = hwmon_temp, + .config = aqr_hwmon_temp_config, +}; + +static const struct hwmon_channel_info *aqr_hwmon_info[] = { + &aqr_hwmon_chip, + &aqr_hwmon_temp, + NULL, +}; + +static const struct hwmon_chip_info aqr_hwmon_chip_info = { + .ops = &aqr_hwmon_ops, + .info = aqr_hwmon_info, +}; + +int aqr_hwmon_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct device *hwmon_dev; + char *hwmon_name; + int i, j; + + hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!hwmon_name) + return -ENOMEM; + + for (i = j = 0; hwmon_name[i]; i++) { + if (isalnum(hwmon_name[i])) { + if (i != j) + hwmon_name[j] = hwmon_name[i]; + j++; + } + } + hwmon_name[j] = '\0'; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, + phydev, &aqr_hwmon_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +#endif diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 0f0eb5682..37218e5d7 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -12,6 +12,8 @@ #include #include +#include "aquantia.h" + #define PHY_ID_AQ1202 0x03a1b445 #define PHY_ID_AQ2104 0x03a1b460 #define PHY_ID_AQR105 0x03a1b4a2 @@ -231,6 +233,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR107", .aneg_done = genphy_c45_aneg_done, .get_features = genphy_c45_pma_read_abilities, + .probe = aqr_hwmon_probe, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .ack_interrupt = aqr_ack_interrupt, @@ -241,6 +244,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQCS109", .aneg_done = genphy_c45_aneg_done, .get_features = genphy_c45_pma_read_abilities, + .probe = aqr_hwmon_probe, .config_init = aqcs109_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, -- 2.20.1