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