Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: aquantia: add hwmon support

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
v3:
- add conditional compiling of aquantia_hwmon.c
- improve converting sensor register values to/from long
- add helper aqr_hwmon_test_bit

Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Heiner Kallweit and committed by
David S. Miller
fb470f70 b4e6a102

+273
+3
drivers/net/phy/Makefile
··· 46 46 47 47 obj-$(CONFIG_AMD_PHY) += amd.o 48 48 aquantia-objs += aquantia_main.o 49 + ifdef CONFIG_HWMON 50 + aquantia-objs += aquantia_hwmon.o 51 + endif 49 52 obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o 50 53 obj-$(CONFIG_ASIX_PHY) += asix.o 51 54 obj-$(CONFIG_AT803X_PHY) += at803x.o
+16
drivers/net/phy/aquantia.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 2 + * HWMON driver for Aquantia PHY 3 + * 4 + * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com> 5 + * Author: Andrew Lunn <andrew@lunn.ch> 6 + * Author: Heiner Kallweit <hkallweit1@gmail.com> 7 + */ 8 + 9 + #include <linux/device.h> 10 + #include <linux/phy.h> 11 + 12 + #if IS_REACHABLE(CONFIG_HWMON) 13 + int aqr_hwmon_probe(struct phy_device *phydev); 14 + #else 15 + static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } 16 + #endif
+250
drivers/net/phy/aquantia_hwmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* HWMON driver for Aquantia PHY 3 + * 4 + * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com> 5 + * Author: Andrew Lunn <andrew@lunn.ch> 6 + * Author: Heiner Kallweit <hkallweit1@gmail.com> 7 + */ 8 + 9 + #include <linux/phy.h> 10 + #include <linux/device.h> 11 + #include <linux/ctype.h> 12 + #include <linux/hwmon.h> 13 + 14 + #include "aquantia.h" 15 + 16 + /* Vendor specific 1, MDIO_MMD_VEND2 */ 17 + #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 18 + #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 19 + #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 20 + #define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 21 + #define VEND1_THERMAL_STAT1 0xc820 22 + #define VEND1_THERMAL_STAT2 0xc821 23 + #define VEND1_THERMAL_STAT2_VALID BIT(0) 24 + #define VEND1_GENERAL_STAT1 0xc830 25 + #define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) 26 + #define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) 27 + #define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) 28 + #define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) 29 + 30 + #if IS_REACHABLE(CONFIG_HWMON) 31 + 32 + static umode_t aqr_hwmon_is_visible(const void *data, 33 + enum hwmon_sensor_types type, 34 + u32 attr, int channel) 35 + { 36 + if (type != hwmon_temp) 37 + return 0; 38 + 39 + switch (attr) { 40 + case hwmon_temp_input: 41 + case hwmon_temp_min_alarm: 42 + case hwmon_temp_max_alarm: 43 + case hwmon_temp_lcrit_alarm: 44 + case hwmon_temp_crit_alarm: 45 + return 0444; 46 + case hwmon_temp_min: 47 + case hwmon_temp_max: 48 + case hwmon_temp_lcrit: 49 + case hwmon_temp_crit: 50 + return 0644; 51 + default: 52 + return 0; 53 + } 54 + } 55 + 56 + static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value) 57 + { 58 + int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); 59 + 60 + if (temp < 0) 61 + return temp; 62 + 63 + /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */ 64 + *value = (s16)temp * 1000 / 256; 65 + 66 + return 0; 67 + } 68 + 69 + static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value) 70 + { 71 + int temp; 72 + 73 + if (value >= 128000 || value < -128000) 74 + return -ERANGE; 75 + 76 + temp = value * 256 / 1000; 77 + 78 + /* temp is in s16 range and we're interested in lower 16 bits only */ 79 + return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp); 80 + } 81 + 82 + static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit) 83 + { 84 + int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); 85 + 86 + if (val < 0) 87 + return val; 88 + 89 + return !!(val & bit); 90 + } 91 + 92 + static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value) 93 + { 94 + int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit); 95 + 96 + if (val < 0) 97 + return val; 98 + 99 + *value = val; 100 + 101 + return 0; 102 + } 103 + 104 + static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 105 + u32 attr, int channel, long *value) 106 + { 107 + struct phy_device *phydev = dev_get_drvdata(dev); 108 + int reg; 109 + 110 + if (type != hwmon_temp) 111 + return -EOPNOTSUPP; 112 + 113 + switch (attr) { 114 + case hwmon_temp_input: 115 + reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2, 116 + VEND1_THERMAL_STAT2_VALID); 117 + if (reg < 0) 118 + return reg; 119 + if (!reg) 120 + return -EBUSY; 121 + 122 + return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value); 123 + 124 + case hwmon_temp_lcrit: 125 + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, 126 + value); 127 + case hwmon_temp_min: 128 + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, 129 + value); 130 + case hwmon_temp_max: 131 + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, 132 + value); 133 + case hwmon_temp_crit: 134 + return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, 135 + value); 136 + case hwmon_temp_lcrit_alarm: 137 + return aqr_hwmon_status1(phydev, 138 + VEND1_GENERAL_STAT1_LOW_TEMP_FAIL, 139 + value); 140 + case hwmon_temp_min_alarm: 141 + return aqr_hwmon_status1(phydev, 142 + VEND1_GENERAL_STAT1_LOW_TEMP_WARN, 143 + value); 144 + case hwmon_temp_max_alarm: 145 + return aqr_hwmon_status1(phydev, 146 + VEND1_GENERAL_STAT1_HIGH_TEMP_WARN, 147 + value); 148 + case hwmon_temp_crit_alarm: 149 + return aqr_hwmon_status1(phydev, 150 + VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL, 151 + value); 152 + default: 153 + return -EOPNOTSUPP; 154 + } 155 + } 156 + 157 + static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 158 + u32 attr, int channel, long value) 159 + { 160 + struct phy_device *phydev = dev_get_drvdata(dev); 161 + 162 + if (type != hwmon_temp) 163 + return -EOPNOTSUPP; 164 + 165 + switch (attr) { 166 + case hwmon_temp_lcrit: 167 + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, 168 + value); 169 + case hwmon_temp_min: 170 + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, 171 + value); 172 + case hwmon_temp_max: 173 + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, 174 + value); 175 + case hwmon_temp_crit: 176 + return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, 177 + value); 178 + default: 179 + return -EOPNOTSUPP; 180 + } 181 + } 182 + 183 + static const struct hwmon_ops aqr_hwmon_ops = { 184 + .is_visible = aqr_hwmon_is_visible, 185 + .read = aqr_hwmon_read, 186 + .write = aqr_hwmon_write, 187 + }; 188 + 189 + static u32 aqr_hwmon_chip_config[] = { 190 + HWMON_C_REGISTER_TZ, 191 + 0, 192 + }; 193 + 194 + static const struct hwmon_channel_info aqr_hwmon_chip = { 195 + .type = hwmon_chip, 196 + .config = aqr_hwmon_chip_config, 197 + }; 198 + 199 + static u32 aqr_hwmon_temp_config[] = { 200 + HWMON_T_INPUT | 201 + HWMON_T_MAX | HWMON_T_MIN | 202 + HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | 203 + HWMON_T_CRIT | HWMON_T_LCRIT | 204 + HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, 205 + 0, 206 + }; 207 + 208 + static const struct hwmon_channel_info aqr_hwmon_temp = { 209 + .type = hwmon_temp, 210 + .config = aqr_hwmon_temp_config, 211 + }; 212 + 213 + static const struct hwmon_channel_info *aqr_hwmon_info[] = { 214 + &aqr_hwmon_chip, 215 + &aqr_hwmon_temp, 216 + NULL, 217 + }; 218 + 219 + static const struct hwmon_chip_info aqr_hwmon_chip_info = { 220 + .ops = &aqr_hwmon_ops, 221 + .info = aqr_hwmon_info, 222 + }; 223 + 224 + int aqr_hwmon_probe(struct phy_device *phydev) 225 + { 226 + struct device *dev = &phydev->mdio.dev; 227 + struct device *hwmon_dev; 228 + char *hwmon_name; 229 + int i, j; 230 + 231 + hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); 232 + if (!hwmon_name) 233 + return -ENOMEM; 234 + 235 + for (i = j = 0; hwmon_name[i]; i++) { 236 + if (isalnum(hwmon_name[i])) { 237 + if (i != j) 238 + hwmon_name[j] = hwmon_name[i]; 239 + j++; 240 + } 241 + } 242 + hwmon_name[j] = '\0'; 243 + 244 + hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, 245 + phydev, &aqr_hwmon_chip_info, NULL); 246 + 247 + return PTR_ERR_OR_ZERO(hwmon_dev); 248 + } 249 + 250 + #endif
+4
drivers/net/phy/aquantia_main.c
··· 12 12 #include <linux/delay.h> 13 13 #include <linux/phy.h> 14 14 15 + #include "aquantia.h" 16 + 15 17 #define PHY_ID_AQ1202 0x03a1b445 16 18 #define PHY_ID_AQ2104 0x03a1b460 17 19 #define PHY_ID_AQR105 0x03a1b4a2 ··· 233 231 .name = "Aquantia AQR107", 234 232 .aneg_done = genphy_c45_aneg_done, 235 233 .get_features = genphy_c45_pma_read_abilities, 234 + .probe = aqr_hwmon_probe, 236 235 .config_aneg = aqr_config_aneg, 237 236 .config_intr = aqr_config_intr, 238 237 .ack_interrupt = aqr_ack_interrupt, ··· 244 241 .name = "Aquantia AQCS109", 245 242 .aneg_done = genphy_c45_aneg_done, 246 243 .get_features = genphy_c45_pma_read_abilities, 244 + .probe = aqr_hwmon_probe, 247 245 .config_init = aqcs109_config_init, 248 246 .config_aneg = aqr_config_aneg, 249 247 .config_intr = aqr_config_intr,