1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Lochnagar hardware monitoring features 4 * 5 * Copyright (c) 2016-2019 Cirrus Logic, Inc. and 6 * Cirrus Logic International Semiconductor Ltd. 7 * 8 * Author: Lucas Tanure <tanureal@opensource.cirrus.com> 9 */ 10 11 #include <linux/delay.h> 12 #include <linux/hwmon.h> 13 #include <linux/hwmon-sysfs.h> 14 #include <linux/math64.h> 15 #include <linux/mfd/lochnagar.h> 16 #include <linux/mfd/lochnagar2_regs.h> 17 #include <linux/module.h> 18 #include <linux/of.h> 19 #include <linux/platform_device.h> 20 #include <linux/regmap.h> 21 22 #define LN2_MAX_NSAMPLE 1023 23 #define LN2_SAMPLE_US 1670 24 25 #define LN2_CURR_UNITS 1000 26 #define LN2_VOLT_UNITS 1000 27 #define LN2_TEMP_UNITS 1000 28 #define LN2_PWR_UNITS 1000000 29 30 static const char * const lochnagar_chan_names[] = { 31 "DBVDD1", 32 "1V8 DSP", 33 "1V8 CDC", 34 "VDDCORE DSP", 35 "AVDD 1V8", 36 "SYSVDD", 37 "VDDCORE CDC", 38 "MICVDD", 39 }; 40 41 struct lochnagar_hwmon { 42 struct regmap *regmap; 43 44 long power_nsamples[ARRAY_SIZE(lochnagar_chan_names)]; 45 46 /* Lock to ensure only a single sensor is read at a time */ 47 struct mutex sensor_lock; 48 }; 49 50 enum lochnagar_measure_mode { 51 LN2_CURR = 0, 52 LN2_VOLT, 53 LN2_TEMP, 54 }; 55 56 /** 57 * float_to_long - Convert ieee754 reading from hardware to an integer 58 * 59 * @data: Value read from the hardware 60 * @precision: Units to multiply up to eg. 1000 = milli, 1000000 = micro 61 * 62 * Return: Converted integer reading 63 * 64 * Depending on the measurement type the hardware returns an ieee754 65 * floating point value in either volts, amps or celsius. This function 66 * will convert that into an integer in a smaller unit such as micro-amps 67 * or milli-celsius. The hardware does not return NaN, so consideration of 68 * that is not required. 69 */ 70 static long float_to_long(u32 data, u32 precision) 71 { 72 u64 man = data & 0x007FFFFF; 73 int exp = ((data & 0x7F800000) >> 23) - 127 - 23; 74 bool negative = data & 0x80000000; 75 long result; 76 77 man = (man + (1 << 23)) * precision; 78 79 if (fls64(man) + exp > (int)sizeof(long) * 8 - 1) 80 result = LONG_MAX; 81 else if (exp < 0) 82 result = (man + (1ull << (-exp - 1))) >> -exp; 83 else 84 result = man << exp; 85 86 return negative ? -result : result; 87 } 88 89 static int do_measurement(struct regmap *regmap, int chan, 90 enum lochnagar_measure_mode mode, int nsamples) 91 { 92 unsigned int val; 93 int ret; 94 95 chan = 1 << (chan + LOCHNAGAR2_IMON_MEASURED_CHANNELS_SHIFT); 96 97 ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL1, 98 LOCHNAGAR2_IMON_ENA_MASK | chan | mode); 99 if (ret < 0) 100 return ret; 101 102 ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL2, nsamples); 103 if (ret < 0) 104 return ret; 105 106 ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 107 LOCHNAGAR2_IMON_CONFIGURE_MASK); 108 if (ret < 0) 109 return ret; 110 111 ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val, 112 val & LOCHNAGAR2_IMON_DONE_MASK, 113 1000, 10000); 114 if (ret < 0) 115 return ret; 116 117 ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 118 LOCHNAGAR2_IMON_MEASURE_MASK); 119 if (ret < 0) 120 return ret; 121 122 /* 123 * Actual measurement time is ~1.67mS per sample, approximate this 124 * with a 1.5mS per sample msleep and then poll for success up to 125 * ~0.17mS * 1023 (LN2_MAX_NSAMPLES). Normally for smaller values 126 * of nsamples the poll will complete on the first loop due to 127 * other latency in the system. 128 */ 129 msleep((nsamples * 3) / 2); 130 131 ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val, 132 val & LOCHNAGAR2_IMON_DONE_MASK, 133 5000, 200000); 134 if (ret < 0) 135 return ret; 136 137 return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 0); 138 } 139 140 static int request_data(struct regmap *regmap, int chan, u32 *data) 141 { 142 unsigned int val; 143 int ret; 144 145 ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4, 146 LOCHNAGAR2_IMON_DATA_REQ_MASK | 147 chan << LOCHNAGAR2_IMON_CH_SEL_SHIFT); 148 if (ret < 0) 149 return ret; 150 151 ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL4, val, 152 val & LOCHNAGAR2_IMON_DATA_RDY_MASK, 153 1000, 10000); 154 if (ret < 0) 155 return ret; 156 157 ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA1, &val); 158 if (ret < 0) 159 return ret; 160 161 *data = val << 16; 162 163 ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA2, &val); 164 if (ret < 0) 165 return ret; 166 167 *data |= val; 168 169 return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4, 0); 170 } 171 172 static int read_sensor(struct device *dev, int chan, 173 enum lochnagar_measure_mode mode, int nsamples, 174 unsigned int precision, long *val) 175 { 176 struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 177 struct regmap *regmap = priv->regmap; 178 u32 data; 179 int ret; 180 181 mutex_lock(&priv->sensor_lock); 182 183 ret = do_measurement(regmap, chan, mode, nsamples); 184 if (ret < 0) { 185 dev_err(dev, "Failed to perform measurement: %d\n", ret); 186 goto error; 187 } 188 189 ret = request_data(regmap, chan, &data); 190 if (ret < 0) { 191 dev_err(dev, "Failed to read measurement: %d\n", ret); 192 goto error; 193 } 194 195 *val = float_to_long(data, precision); 196 197 error: 198 mutex_unlock(&priv->sensor_lock); 199 200 return ret; 201 } 202 203 static int read_power(struct device *dev, int chan, long *val) 204 { 205 struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 206 int nsamples = priv->power_nsamples[chan]; 207 u64 power; 208 int ret; 209 210 if (!strcmp("SYSVDD", lochnagar_chan_names[chan])) { 211 power = 5 * LN2_PWR_UNITS; 212 } else { 213 ret = read_sensor(dev, chan, LN2_VOLT, 1, LN2_PWR_UNITS, val); 214 if (ret < 0) 215 return ret; 216 217 power = abs(*val); 218 } 219 220 ret = read_sensor(dev, chan, LN2_CURR, nsamples, LN2_PWR_UNITS, val); 221 if (ret < 0) 222 return ret; 223 224 power *= abs(*val); 225 power = DIV_ROUND_CLOSEST_ULL(power, LN2_PWR_UNITS); 226 227 if (power > LONG_MAX) 228 *val = LONG_MAX; 229 else 230 *val = power; 231 232 return 0; 233 } 234 235 static umode_t lochnagar_is_visible(const void *drvdata, 236 enum hwmon_sensor_types type, 237 u32 attr, int chan) 238 { 239 switch (type) { 240 case hwmon_in: 241 if (!strcmp("SYSVDD", lochnagar_chan_names[chan])) 242 return 0; 243 break; 244 case hwmon_power: 245 if (attr == hwmon_power_average_interval) 246 return 0644; 247 break; 248 default: 249 break; 250 } 251 252 return 0444; 253 } 254 255 static int lochnagar_read(struct device *dev, enum hwmon_sensor_types type, 256 u32 attr, int chan, long *val) 257 { 258 struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 259 int interval; 260 261 switch (type) { 262 case hwmon_in: 263 return read_sensor(dev, chan, LN2_VOLT, 1, LN2_VOLT_UNITS, val); 264 case hwmon_curr: 265 return read_sensor(dev, chan, LN2_CURR, 1, LN2_CURR_UNITS, val); 266 case hwmon_temp: 267 return read_sensor(dev, chan, LN2_TEMP, 1, LN2_TEMP_UNITS, val); 268 case hwmon_power: 269 switch (attr) { 270 case hwmon_power_average: 271 return read_power(dev, chan, val); 272 case hwmon_power_average_interval: 273 interval = priv->power_nsamples[chan] * LN2_SAMPLE_US; 274 *val = DIV_ROUND_CLOSEST(interval, 1000); 275 return 0; 276 default: 277 return -EOPNOTSUPP; 278 } 279 default: 280 return -EOPNOTSUPP; 281 } 282 } 283 284 static int lochnagar_read_string(struct device *dev, 285 enum hwmon_sensor_types type, u32 attr, 286 int chan, const char **str) 287 { 288 switch (type) { 289 case hwmon_in: 290 case hwmon_curr: 291 case hwmon_power: 292 *str = lochnagar_chan_names[chan]; 293 return 0; 294 default: 295 return -EOPNOTSUPP; 296 } 297 } 298 299 static int lochnagar_write(struct device *dev, enum hwmon_sensor_types type, 300 u32 attr, int chan, long val) 301 { 302 struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 303 304 if (type != hwmon_power || attr != hwmon_power_average_interval) 305 return -EOPNOTSUPP; 306 307 val = clamp_t(long, val, 1, (LN2_MAX_NSAMPLE * LN2_SAMPLE_US) / 1000); 308 val = DIV_ROUND_CLOSEST(val * 1000, LN2_SAMPLE_US); 309 310 priv->power_nsamples[chan] = val; 311 312 return 0; 313 } 314 315 static const struct hwmon_ops lochnagar_ops = { 316 .is_visible = lochnagar_is_visible, 317 .read = lochnagar_read, 318 .read_string = lochnagar_read_string, 319 .write = lochnagar_write, 320 }; 321 322 static const struct hwmon_channel_info * const lochnagar_info[] = { 323 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 324 HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, 325 HWMON_I_INPUT | HWMON_I_LABEL, 326 HWMON_I_INPUT | HWMON_I_LABEL, 327 HWMON_I_INPUT | HWMON_I_LABEL, 328 HWMON_I_INPUT | HWMON_I_LABEL, 329 HWMON_I_INPUT | HWMON_I_LABEL, 330 HWMON_I_INPUT | HWMON_I_LABEL, 331 HWMON_I_INPUT | HWMON_I_LABEL), 332 HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, 333 HWMON_C_INPUT | HWMON_C_LABEL, 334 HWMON_C_INPUT | HWMON_C_LABEL, 335 HWMON_C_INPUT | HWMON_C_LABEL, 336 HWMON_C_INPUT | HWMON_C_LABEL, 337 HWMON_C_INPUT | HWMON_C_LABEL, 338 HWMON_C_INPUT | HWMON_C_LABEL, 339 HWMON_C_INPUT | HWMON_C_LABEL), 340 HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 341 HWMON_P_LABEL, 342 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 343 HWMON_P_LABEL, 344 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 345 HWMON_P_LABEL, 346 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 347 HWMON_P_LABEL, 348 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 349 HWMON_P_LABEL, 350 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 351 HWMON_P_LABEL, 352 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 353 HWMON_P_LABEL, 354 HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 355 HWMON_P_LABEL), 356 NULL 357 }; 358 359 static const struct hwmon_chip_info lochnagar_chip_info = { 360 .ops = &lochnagar_ops, 361 .info = lochnagar_info, 362 }; 363 364 static const struct of_device_id lochnagar_of_match[] = { 365 { .compatible = "cirrus,lochnagar2-hwmon" }, 366 {} 367 }; 368 MODULE_DEVICE_TABLE(of, lochnagar_of_match); 369 370 static int lochnagar_hwmon_probe(struct platform_device *pdev) 371 { 372 struct device *dev = &pdev->dev; 373 struct device *hwmon_dev; 374 struct lochnagar_hwmon *priv; 375 int i; 376 377 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 378 if (!priv) 379 return -ENOMEM; 380 381 mutex_init(&priv->sensor_lock); 382 383 priv->regmap = dev_get_regmap(dev->parent, NULL); 384 if (!priv->regmap) { 385 dev_err(dev, "No register map found\n"); 386 return -EINVAL; 387 } 388 389 for (i = 0; i < ARRAY_SIZE(priv->power_nsamples); i++) 390 priv->power_nsamples[i] = 96; 391 392 hwmon_dev = devm_hwmon_device_register_with_info(dev, "Lochnagar", priv, 393 &lochnagar_chip_info, 394 NULL); 395 396 return PTR_ERR_OR_ZERO(hwmon_dev); 397 } 398 399 static struct platform_driver lochnagar_hwmon_driver = { 400 .driver = { 401 .name = "lochnagar-hwmon", 402 .of_match_table = lochnagar_of_match, 403 }, 404 .probe = lochnagar_hwmon_probe, 405 }; 406 module_platform_driver(lochnagar_hwmon_driver); 407 408 MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); 409 MODULE_DESCRIPTION("Lochnagar hardware monitoring features"); 410 MODULE_LICENSE("GPL"); 411