18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Lochnagar hardware monitoring features 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016-2019 Cirrus Logic, Inc. and 68c2ecf20Sopenharmony_ci * Cirrus Logic International Semiconductor Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Lucas Tanure <tanureal@opensource.cirrus.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/math64.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/lochnagar.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/lochnagar2_regs.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/regmap.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define LN2_MAX_NSAMPLE 1023 258c2ecf20Sopenharmony_ci#define LN2_SAMPLE_US 1670 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define LN2_CURR_UNITS 1000 288c2ecf20Sopenharmony_ci#define LN2_VOLT_UNITS 1000 298c2ecf20Sopenharmony_ci#define LN2_TEMP_UNITS 1000 308c2ecf20Sopenharmony_ci#define LN2_PWR_UNITS 1000000 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const char * const lochnagar_chan_names[] = { 338c2ecf20Sopenharmony_ci "DBVDD1", 348c2ecf20Sopenharmony_ci "1V8 DSP", 358c2ecf20Sopenharmony_ci "1V8 CDC", 368c2ecf20Sopenharmony_ci "VDDCORE DSP", 378c2ecf20Sopenharmony_ci "AVDD 1V8", 388c2ecf20Sopenharmony_ci "SYSVDD", 398c2ecf20Sopenharmony_ci "VDDCORE CDC", 408c2ecf20Sopenharmony_ci "MICVDD", 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct lochnagar_hwmon { 448c2ecf20Sopenharmony_ci struct regmap *regmap; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci long power_nsamples[ARRAY_SIZE(lochnagar_chan_names)]; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Lock to ensure only a single sensor is read at a time */ 498c2ecf20Sopenharmony_ci struct mutex sensor_lock; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cienum lochnagar_measure_mode { 538c2ecf20Sopenharmony_ci LN2_CURR = 0, 548c2ecf20Sopenharmony_ci LN2_VOLT, 558c2ecf20Sopenharmony_ci LN2_TEMP, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * float_to_long - Convert ieee754 reading from hardware to an integer 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * @data: Value read from the hardware 628c2ecf20Sopenharmony_ci * @precision: Units to multiply up to eg. 1000 = milli, 1000000 = micro 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Return: Converted integer reading 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * Depending on the measurement type the hardware returns an ieee754 678c2ecf20Sopenharmony_ci * floating point value in either volts, amps or celsius. This function 688c2ecf20Sopenharmony_ci * will convert that into an integer in a smaller unit such as micro-amps 698c2ecf20Sopenharmony_ci * or milli-celsius. The hardware does not return NaN, so consideration of 708c2ecf20Sopenharmony_ci * that is not required. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic long float_to_long(u32 data, u32 precision) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u64 man = data & 0x007FFFFF; 758c2ecf20Sopenharmony_ci int exp = ((data & 0x7F800000) >> 23) - 127 - 23; 768c2ecf20Sopenharmony_ci bool negative = data & 0x80000000; 778c2ecf20Sopenharmony_ci long result; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci man = (man + (1 << 23)) * precision; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (fls64(man) + exp > (int)sizeof(long) * 8 - 1) 828c2ecf20Sopenharmony_ci result = LONG_MAX; 838c2ecf20Sopenharmony_ci else if (exp < 0) 848c2ecf20Sopenharmony_ci result = (man + (1ull << (-exp - 1))) >> -exp; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci result = man << exp; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return negative ? -result : result; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int do_measurement(struct regmap *regmap, int chan, 928c2ecf20Sopenharmony_ci enum lochnagar_measure_mode mode, int nsamples) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci unsigned int val; 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci chan = 1 << (chan + LOCHNAGAR2_IMON_MEASURED_CHANNELS_SHIFT); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL1, 1008c2ecf20Sopenharmony_ci LOCHNAGAR2_IMON_ENA_MASK | chan | mode); 1018c2ecf20Sopenharmony_ci if (ret < 0) 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL2, nsamples); 1058c2ecf20Sopenharmony_ci if (ret < 0) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 1098c2ecf20Sopenharmony_ci LOCHNAGAR2_IMON_CONFIGURE_MASK); 1108c2ecf20Sopenharmony_ci if (ret < 0) 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val, 1148c2ecf20Sopenharmony_ci val & LOCHNAGAR2_IMON_DONE_MASK, 1158c2ecf20Sopenharmony_ci 1000, 10000); 1168c2ecf20Sopenharmony_ci if (ret < 0) 1178c2ecf20Sopenharmony_ci return ret; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 1208c2ecf20Sopenharmony_ci LOCHNAGAR2_IMON_MEASURE_MASK); 1218c2ecf20Sopenharmony_ci if (ret < 0) 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * Actual measurement time is ~1.67mS per sample, approximate this 1268c2ecf20Sopenharmony_ci * with a 1.5mS per sample msleep and then poll for success up to 1278c2ecf20Sopenharmony_ci * ~0.17mS * 1023 (LN2_MAX_NSAMPLES). Normally for smaller values 1288c2ecf20Sopenharmony_ci * of nsamples the poll will complete on the first loop due to 1298c2ecf20Sopenharmony_ci * other latency in the system. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci msleep((nsamples * 3) / 2); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val, 1348c2ecf20Sopenharmony_ci val & LOCHNAGAR2_IMON_DONE_MASK, 1358c2ecf20Sopenharmony_ci 5000, 200000); 1368c2ecf20Sopenharmony_ci if (ret < 0) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 0); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int request_data(struct regmap *regmap, int chan, u32 *data) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci unsigned int val; 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4, 1488c2ecf20Sopenharmony_ci LOCHNAGAR2_IMON_DATA_REQ_MASK | 1498c2ecf20Sopenharmony_ci chan << LOCHNAGAR2_IMON_CH_SEL_SHIFT); 1508c2ecf20Sopenharmony_ci if (ret < 0) 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL4, val, 1548c2ecf20Sopenharmony_ci val & LOCHNAGAR2_IMON_DATA_RDY_MASK, 1558c2ecf20Sopenharmony_ci 1000, 10000); 1568c2ecf20Sopenharmony_ci if (ret < 0) 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA1, &val); 1608c2ecf20Sopenharmony_ci if (ret < 0) 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci *data = val << 16; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA2, &val); 1668c2ecf20Sopenharmony_ci if (ret < 0) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci *data |= val; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4, 0); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int read_sensor(struct device *dev, int chan, 1758c2ecf20Sopenharmony_ci enum lochnagar_measure_mode mode, int nsamples, 1768c2ecf20Sopenharmony_ci unsigned int precision, long *val) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 1798c2ecf20Sopenharmony_ci struct regmap *regmap = priv->regmap; 1808c2ecf20Sopenharmony_ci u32 data; 1818c2ecf20Sopenharmony_ci int ret; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci mutex_lock(&priv->sensor_lock); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = do_measurement(regmap, chan, mode, nsamples); 1868c2ecf20Sopenharmony_ci if (ret < 0) { 1878c2ecf20Sopenharmony_ci dev_err(dev, "Failed to perform measurement: %d\n", ret); 1888c2ecf20Sopenharmony_ci goto error; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = request_data(regmap, chan, &data); 1928c2ecf20Sopenharmony_ci if (ret < 0) { 1938c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read measurement: %d\n", ret); 1948c2ecf20Sopenharmony_ci goto error; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci *val = float_to_long(data, precision); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cierror: 2008c2ecf20Sopenharmony_ci mutex_unlock(&priv->sensor_lock); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int read_power(struct device *dev, int chan, long *val) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 2088c2ecf20Sopenharmony_ci int nsamples = priv->power_nsamples[chan]; 2098c2ecf20Sopenharmony_ci u64 power; 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!strcmp("SYSVDD", lochnagar_chan_names[chan])) { 2138c2ecf20Sopenharmony_ci power = 5 * LN2_PWR_UNITS; 2148c2ecf20Sopenharmony_ci } else { 2158c2ecf20Sopenharmony_ci ret = read_sensor(dev, chan, LN2_VOLT, 1, LN2_PWR_UNITS, val); 2168c2ecf20Sopenharmony_ci if (ret < 0) 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci power = abs(*val); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci ret = read_sensor(dev, chan, LN2_CURR, nsamples, LN2_PWR_UNITS, val); 2238c2ecf20Sopenharmony_ci if (ret < 0) 2248c2ecf20Sopenharmony_ci return ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci power *= abs(*val); 2278c2ecf20Sopenharmony_ci power = DIV_ROUND_CLOSEST_ULL(power, LN2_PWR_UNITS); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (power > LONG_MAX) 2308c2ecf20Sopenharmony_ci *val = LONG_MAX; 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci *val = power; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic umode_t lochnagar_is_visible(const void *drvdata, 2388c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 2398c2ecf20Sopenharmony_ci u32 attr, int chan) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci switch (type) { 2428c2ecf20Sopenharmony_ci case hwmon_in: 2438c2ecf20Sopenharmony_ci if (!strcmp("SYSVDD", lochnagar_chan_names[chan])) 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case hwmon_power: 2478c2ecf20Sopenharmony_ci if (attr == hwmon_power_average_interval) 2488c2ecf20Sopenharmony_ci return 0644; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0444; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int lochnagar_read(struct device *dev, enum hwmon_sensor_types type, 2588c2ecf20Sopenharmony_ci u32 attr, int chan, long *val) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 2618c2ecf20Sopenharmony_ci int interval; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci switch (type) { 2648c2ecf20Sopenharmony_ci case hwmon_in: 2658c2ecf20Sopenharmony_ci return read_sensor(dev, chan, LN2_VOLT, 1, LN2_VOLT_UNITS, val); 2668c2ecf20Sopenharmony_ci case hwmon_curr: 2678c2ecf20Sopenharmony_ci return read_sensor(dev, chan, LN2_CURR, 1, LN2_CURR_UNITS, val); 2688c2ecf20Sopenharmony_ci case hwmon_temp: 2698c2ecf20Sopenharmony_ci return read_sensor(dev, chan, LN2_TEMP, 1, LN2_TEMP_UNITS, val); 2708c2ecf20Sopenharmony_ci case hwmon_power: 2718c2ecf20Sopenharmony_ci switch (attr) { 2728c2ecf20Sopenharmony_ci case hwmon_power_average: 2738c2ecf20Sopenharmony_ci return read_power(dev, chan, val); 2748c2ecf20Sopenharmony_ci case hwmon_power_average_interval: 2758c2ecf20Sopenharmony_ci interval = priv->power_nsamples[chan] * LN2_SAMPLE_US; 2768c2ecf20Sopenharmony_ci *val = DIV_ROUND_CLOSEST(interval, 1000); 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int lochnagar_read_string(struct device *dev, 2878c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, u32 attr, 2888c2ecf20Sopenharmony_ci int chan, const char **str) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci switch (type) { 2918c2ecf20Sopenharmony_ci case hwmon_in: 2928c2ecf20Sopenharmony_ci case hwmon_curr: 2938c2ecf20Sopenharmony_ci case hwmon_power: 2948c2ecf20Sopenharmony_ci *str = lochnagar_chan_names[chan]; 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci default: 2978c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int lochnagar_write(struct device *dev, enum hwmon_sensor_types type, 3028c2ecf20Sopenharmony_ci u32 attr, int chan, long val) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct lochnagar_hwmon *priv = dev_get_drvdata(dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (type != hwmon_power || attr != hwmon_power_average_interval) 3078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci val = clamp_t(long, val, 1, (LN2_MAX_NSAMPLE * LN2_SAMPLE_US) / 1000); 3108c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val * 1000, LN2_SAMPLE_US); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci priv->power_nsamples[chan] = val; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic const struct hwmon_ops lochnagar_ops = { 3188c2ecf20Sopenharmony_ci .is_visible = lochnagar_is_visible, 3198c2ecf20Sopenharmony_ci .read = lochnagar_read, 3208c2ecf20Sopenharmony_ci .read_string = lochnagar_read_string, 3218c2ecf20Sopenharmony_ci .write = lochnagar_write, 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *lochnagar_info[] = { 3258c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 3268c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, 3278c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 3288c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 3298c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 3308c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 3318c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 3328c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 3338c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL), 3348c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, 3358c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 3368c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 3378c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 3388c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 3398c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 3408c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 3418c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL), 3428c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3438c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3448c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3458c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3468c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3478c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3488c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3498c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3508c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3518c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3528c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3538c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3548c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3558c2ecf20Sopenharmony_ci HWMON_P_LABEL, 3568c2ecf20Sopenharmony_ci HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL | 3578c2ecf20Sopenharmony_ci HWMON_P_LABEL), 3588c2ecf20Sopenharmony_ci NULL 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info lochnagar_chip_info = { 3628c2ecf20Sopenharmony_ci .ops = &lochnagar_ops, 3638c2ecf20Sopenharmony_ci .info = lochnagar_info, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic const struct of_device_id lochnagar_of_match[] = { 3678c2ecf20Sopenharmony_ci { .compatible = "cirrus,lochnagar2-hwmon" }, 3688c2ecf20Sopenharmony_ci {} 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lochnagar_of_match); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int lochnagar_hwmon_probe(struct platform_device *pdev) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3758c2ecf20Sopenharmony_ci struct device *hwmon_dev; 3768c2ecf20Sopenharmony_ci struct lochnagar_hwmon *priv; 3778c2ecf20Sopenharmony_ci int i; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 3808c2ecf20Sopenharmony_ci if (!priv) 3818c2ecf20Sopenharmony_ci return -ENOMEM; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci mutex_init(&priv->sensor_lock); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci priv->regmap = dev_get_regmap(dev->parent, NULL); 3868c2ecf20Sopenharmony_ci if (!priv->regmap) { 3878c2ecf20Sopenharmony_ci dev_err(dev, "No register map found\n"); 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->power_nsamples); i++) 3928c2ecf20Sopenharmony_ci priv->power_nsamples[i] = 96; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(dev, "Lochnagar", priv, 3958c2ecf20Sopenharmony_ci &lochnagar_chip_info, 3968c2ecf20Sopenharmony_ci NULL); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic struct platform_driver lochnagar_hwmon_driver = { 4028c2ecf20Sopenharmony_ci .driver = { 4038c2ecf20Sopenharmony_ci .name = "lochnagar-hwmon", 4048c2ecf20Sopenharmony_ci .of_match_table = lochnagar_of_match, 4058c2ecf20Sopenharmony_ci }, 4068c2ecf20Sopenharmony_ci .probe = lochnagar_hwmon_probe, 4078c2ecf20Sopenharmony_ci}; 4088c2ecf20Sopenharmony_cimodule_platform_driver(lochnagar_hwmon_driver); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); 4118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Lochnagar hardware monitoring features"); 4128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 413