aquantia_hwmon.c (5981B)
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 32static 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 56static 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 69static 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 82static 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 92static 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 104static 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 157static 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 183static 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 189static u32 aqr_hwmon_chip_config[] = { 190 HWMON_C_REGISTER_TZ, 191 0, 192}; 193 194static const struct hwmon_channel_info aqr_hwmon_chip = { 195 .type = hwmon_chip, 196 .config = aqr_hwmon_chip_config, 197}; 198 199static 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 208static const struct hwmon_channel_info aqr_hwmon_temp = { 209 .type = hwmon_temp, 210 .config = aqr_hwmon_temp_config, 211}; 212 213static const struct hwmon_channel_info *aqr_hwmon_info[] = { 214 &aqr_hwmon_chip, 215 &aqr_hwmon_temp, 216 NULL, 217}; 218 219static const struct hwmon_chip_info aqr_hwmon_chip_info = { 220 .ops = &aqr_hwmon_ops, 221 .info = aqr_hwmon_info, 222}; 223 224int 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