14cdb5621SLucas Tanure // SPDX-License-Identifier: GPL-2.0
24cdb5621SLucas Tanure /*
34cdb5621SLucas Tanure * Lochnagar hardware monitoring features
44cdb5621SLucas Tanure *
54cdb5621SLucas Tanure * Copyright (c) 2016-2019 Cirrus Logic, Inc. and
64cdb5621SLucas Tanure * Cirrus Logic International Semiconductor Ltd.
74cdb5621SLucas Tanure *
84cdb5621SLucas Tanure * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
94cdb5621SLucas Tanure */
104cdb5621SLucas Tanure
114cdb5621SLucas Tanure #include <linux/delay.h>
124cdb5621SLucas Tanure #include <linux/hwmon.h>
134cdb5621SLucas Tanure #include <linux/hwmon-sysfs.h>
144cdb5621SLucas Tanure #include <linux/math64.h>
154cdb5621SLucas Tanure #include <linux/mfd/lochnagar.h>
164cdb5621SLucas Tanure #include <linux/mfd/lochnagar2_regs.h>
174cdb5621SLucas Tanure #include <linux/module.h>
184cdb5621SLucas Tanure #include <linux/of.h>
194cdb5621SLucas Tanure #include <linux/platform_device.h>
204cdb5621SLucas Tanure #include <linux/regmap.h>
214cdb5621SLucas Tanure
224cdb5621SLucas Tanure #define LN2_MAX_NSAMPLE 1023
234cdb5621SLucas Tanure #define LN2_SAMPLE_US 1670
244cdb5621SLucas Tanure
254cdb5621SLucas Tanure #define LN2_CURR_UNITS 1000
264cdb5621SLucas Tanure #define LN2_VOLT_UNITS 1000
274cdb5621SLucas Tanure #define LN2_TEMP_UNITS 1000
284cdb5621SLucas Tanure #define LN2_PWR_UNITS 1000000
294cdb5621SLucas Tanure
304cdb5621SLucas Tanure static const char * const lochnagar_chan_names[] = {
314cdb5621SLucas Tanure "DBVDD1",
324cdb5621SLucas Tanure "1V8 DSP",
334cdb5621SLucas Tanure "1V8 CDC",
344cdb5621SLucas Tanure "VDDCORE DSP",
354cdb5621SLucas Tanure "AVDD 1V8",
364cdb5621SLucas Tanure "SYSVDD",
374cdb5621SLucas Tanure "VDDCORE CDC",
384cdb5621SLucas Tanure "MICVDD",
394cdb5621SLucas Tanure };
404cdb5621SLucas Tanure
414cdb5621SLucas Tanure struct lochnagar_hwmon {
424cdb5621SLucas Tanure struct regmap *regmap;
434cdb5621SLucas Tanure
444cdb5621SLucas Tanure long power_nsamples[ARRAY_SIZE(lochnagar_chan_names)];
454cdb5621SLucas Tanure
464cdb5621SLucas Tanure /* Lock to ensure only a single sensor is read at a time */
474cdb5621SLucas Tanure struct mutex sensor_lock;
484cdb5621SLucas Tanure };
494cdb5621SLucas Tanure
504cdb5621SLucas Tanure enum lochnagar_measure_mode {
514cdb5621SLucas Tanure LN2_CURR = 0,
524cdb5621SLucas Tanure LN2_VOLT,
534cdb5621SLucas Tanure LN2_TEMP,
544cdb5621SLucas Tanure };
554cdb5621SLucas Tanure
564cdb5621SLucas Tanure /**
574cdb5621SLucas Tanure * float_to_long - Convert ieee754 reading from hardware to an integer
584cdb5621SLucas Tanure *
594cdb5621SLucas Tanure * @data: Value read from the hardware
604cdb5621SLucas Tanure * @precision: Units to multiply up to eg. 1000 = milli, 1000000 = micro
614cdb5621SLucas Tanure *
624cdb5621SLucas Tanure * Return: Converted integer reading
634cdb5621SLucas Tanure *
644cdb5621SLucas Tanure * Depending on the measurement type the hardware returns an ieee754
654cdb5621SLucas Tanure * floating point value in either volts, amps or celsius. This function
664cdb5621SLucas Tanure * will convert that into an integer in a smaller unit such as micro-amps
674cdb5621SLucas Tanure * or milli-celsius. The hardware does not return NaN, so consideration of
684cdb5621SLucas Tanure * that is not required.
694cdb5621SLucas Tanure */
float_to_long(u32 data,u32 precision)704cdb5621SLucas Tanure static long float_to_long(u32 data, u32 precision)
714cdb5621SLucas Tanure {
724cdb5621SLucas Tanure u64 man = data & 0x007FFFFF;
734cdb5621SLucas Tanure int exp = ((data & 0x7F800000) >> 23) - 127 - 23;
744cdb5621SLucas Tanure bool negative = data & 0x80000000;
754cdb5621SLucas Tanure long result;
764cdb5621SLucas Tanure
774cdb5621SLucas Tanure man = (man + (1 << 23)) * precision;
784cdb5621SLucas Tanure
794cdb5621SLucas Tanure if (fls64(man) + exp > (int)sizeof(long) * 8 - 1)
804cdb5621SLucas Tanure result = LONG_MAX;
814cdb5621SLucas Tanure else if (exp < 0)
824cdb5621SLucas Tanure result = (man + (1ull << (-exp - 1))) >> -exp;
834cdb5621SLucas Tanure else
844cdb5621SLucas Tanure result = man << exp;
854cdb5621SLucas Tanure
864cdb5621SLucas Tanure return negative ? -result : result;
874cdb5621SLucas Tanure }
884cdb5621SLucas Tanure
do_measurement(struct regmap * regmap,int chan,enum lochnagar_measure_mode mode,int nsamples)894cdb5621SLucas Tanure static int do_measurement(struct regmap *regmap, int chan,
904cdb5621SLucas Tanure enum lochnagar_measure_mode mode, int nsamples)
914cdb5621SLucas Tanure {
924cdb5621SLucas Tanure unsigned int val;
934cdb5621SLucas Tanure int ret;
944cdb5621SLucas Tanure
954cdb5621SLucas Tanure chan = 1 << (chan + LOCHNAGAR2_IMON_MEASURED_CHANNELS_SHIFT);
964cdb5621SLucas Tanure
974cdb5621SLucas Tanure ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL1,
984cdb5621SLucas Tanure LOCHNAGAR2_IMON_ENA_MASK | chan | mode);
994cdb5621SLucas Tanure if (ret < 0)
1004cdb5621SLucas Tanure return ret;
1014cdb5621SLucas Tanure
1024cdb5621SLucas Tanure ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL2, nsamples);
1034cdb5621SLucas Tanure if (ret < 0)
1044cdb5621SLucas Tanure return ret;
1054cdb5621SLucas Tanure
1064cdb5621SLucas Tanure ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3,
1074cdb5621SLucas Tanure LOCHNAGAR2_IMON_CONFIGURE_MASK);
1084cdb5621SLucas Tanure if (ret < 0)
1094cdb5621SLucas Tanure return ret;
1104cdb5621SLucas Tanure
1114cdb5621SLucas Tanure ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val,
1124cdb5621SLucas Tanure val & LOCHNAGAR2_IMON_DONE_MASK,
1134cdb5621SLucas Tanure 1000, 10000);
1144cdb5621SLucas Tanure if (ret < 0)
1154cdb5621SLucas Tanure return ret;
1164cdb5621SLucas Tanure
1174cdb5621SLucas Tanure ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3,
1184cdb5621SLucas Tanure LOCHNAGAR2_IMON_MEASURE_MASK);
1194cdb5621SLucas Tanure if (ret < 0)
1204cdb5621SLucas Tanure return ret;
1214cdb5621SLucas Tanure
1224cdb5621SLucas Tanure /*
1234cdb5621SLucas Tanure * Actual measurement time is ~1.67mS per sample, approximate this
1244cdb5621SLucas Tanure * with a 1.5mS per sample msleep and then poll for success up to
1254cdb5621SLucas Tanure * ~0.17mS * 1023 (LN2_MAX_NSAMPLES). Normally for smaller values
1264cdb5621SLucas Tanure * of nsamples the poll will complete on the first loop due to
1274cdb5621SLucas Tanure * other latency in the system.
1284cdb5621SLucas Tanure */
1294cdb5621SLucas Tanure msleep((nsamples * 3) / 2);
1304cdb5621SLucas Tanure
1314cdb5621SLucas Tanure ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val,
1324cdb5621SLucas Tanure val & LOCHNAGAR2_IMON_DONE_MASK,
1334cdb5621SLucas Tanure 5000, 200000);
1344cdb5621SLucas Tanure if (ret < 0)
1354cdb5621SLucas Tanure return ret;
1364cdb5621SLucas Tanure
1374cdb5621SLucas Tanure return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 0);
1384cdb5621SLucas Tanure }
1394cdb5621SLucas Tanure
request_data(struct regmap * regmap,int chan,u32 * data)1404cdb5621SLucas Tanure static int request_data(struct regmap *regmap, int chan, u32 *data)
1414cdb5621SLucas Tanure {
1424cdb5621SLucas Tanure unsigned int val;
1434cdb5621SLucas Tanure int ret;
1444cdb5621SLucas Tanure
1454cdb5621SLucas Tanure ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4,
1464cdb5621SLucas Tanure LOCHNAGAR2_IMON_DATA_REQ_MASK |
1474cdb5621SLucas Tanure chan << LOCHNAGAR2_IMON_CH_SEL_SHIFT);
1484cdb5621SLucas Tanure if (ret < 0)
1494cdb5621SLucas Tanure return ret;
1504cdb5621SLucas Tanure
1514cdb5621SLucas Tanure ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL4, val,
1524cdb5621SLucas Tanure val & LOCHNAGAR2_IMON_DATA_RDY_MASK,
1534cdb5621SLucas Tanure 1000, 10000);
1544cdb5621SLucas Tanure if (ret < 0)
1554cdb5621SLucas Tanure return ret;
1564cdb5621SLucas Tanure
1574cdb5621SLucas Tanure ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA1, &val);
1584cdb5621SLucas Tanure if (ret < 0)
1594cdb5621SLucas Tanure return ret;
1604cdb5621SLucas Tanure
1614cdb5621SLucas Tanure *data = val << 16;
1624cdb5621SLucas Tanure
1634cdb5621SLucas Tanure ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA2, &val);
1644cdb5621SLucas Tanure if (ret < 0)
1654cdb5621SLucas Tanure return ret;
1664cdb5621SLucas Tanure
1674cdb5621SLucas Tanure *data |= val;
1684cdb5621SLucas Tanure
1694cdb5621SLucas Tanure return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4, 0);
1704cdb5621SLucas Tanure }
1714cdb5621SLucas Tanure
read_sensor(struct device * dev,int chan,enum lochnagar_measure_mode mode,int nsamples,unsigned int precision,long * val)1724cdb5621SLucas Tanure static int read_sensor(struct device *dev, int chan,
1734cdb5621SLucas Tanure enum lochnagar_measure_mode mode, int nsamples,
1744cdb5621SLucas Tanure unsigned int precision, long *val)
1754cdb5621SLucas Tanure {
1764cdb5621SLucas Tanure struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
1774cdb5621SLucas Tanure struct regmap *regmap = priv->regmap;
1784cdb5621SLucas Tanure u32 data;
1794cdb5621SLucas Tanure int ret;
1804cdb5621SLucas Tanure
1814cdb5621SLucas Tanure mutex_lock(&priv->sensor_lock);
1824cdb5621SLucas Tanure
1834cdb5621SLucas Tanure ret = do_measurement(regmap, chan, mode, nsamples);
1844cdb5621SLucas Tanure if (ret < 0) {
1854cdb5621SLucas Tanure dev_err(dev, "Failed to perform measurement: %d\n", ret);
1864cdb5621SLucas Tanure goto error;
1874cdb5621SLucas Tanure }
1884cdb5621SLucas Tanure
1894cdb5621SLucas Tanure ret = request_data(regmap, chan, &data);
1904cdb5621SLucas Tanure if (ret < 0) {
1914cdb5621SLucas Tanure dev_err(dev, "Failed to read measurement: %d\n", ret);
1924cdb5621SLucas Tanure goto error;
1934cdb5621SLucas Tanure }
1944cdb5621SLucas Tanure
1954cdb5621SLucas Tanure *val = float_to_long(data, precision);
1964cdb5621SLucas Tanure
1974cdb5621SLucas Tanure error:
1984cdb5621SLucas Tanure mutex_unlock(&priv->sensor_lock);
1994cdb5621SLucas Tanure
2004cdb5621SLucas Tanure return ret;
2014cdb5621SLucas Tanure }
2024cdb5621SLucas Tanure
read_power(struct device * dev,int chan,long * val)2034cdb5621SLucas Tanure static int read_power(struct device *dev, int chan, long *val)
2044cdb5621SLucas Tanure {
2054cdb5621SLucas Tanure struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
2064cdb5621SLucas Tanure int nsamples = priv->power_nsamples[chan];
2074cdb5621SLucas Tanure u64 power;
2084cdb5621SLucas Tanure int ret;
2094cdb5621SLucas Tanure
2104cdb5621SLucas Tanure if (!strcmp("SYSVDD", lochnagar_chan_names[chan])) {
2114cdb5621SLucas Tanure power = 5 * LN2_PWR_UNITS;
2124cdb5621SLucas Tanure } else {
2134cdb5621SLucas Tanure ret = read_sensor(dev, chan, LN2_VOLT, 1, LN2_PWR_UNITS, val);
2144cdb5621SLucas Tanure if (ret < 0)
2154cdb5621SLucas Tanure return ret;
2164cdb5621SLucas Tanure
2174cdb5621SLucas Tanure power = abs(*val);
2184cdb5621SLucas Tanure }
2194cdb5621SLucas Tanure
2204cdb5621SLucas Tanure ret = read_sensor(dev, chan, LN2_CURR, nsamples, LN2_PWR_UNITS, val);
2214cdb5621SLucas Tanure if (ret < 0)
2224cdb5621SLucas Tanure return ret;
2234cdb5621SLucas Tanure
2244cdb5621SLucas Tanure power *= abs(*val);
2254cdb5621SLucas Tanure power = DIV_ROUND_CLOSEST_ULL(power, LN2_PWR_UNITS);
2264cdb5621SLucas Tanure
2274cdb5621SLucas Tanure if (power > LONG_MAX)
2284cdb5621SLucas Tanure *val = LONG_MAX;
2294cdb5621SLucas Tanure else
2304cdb5621SLucas Tanure *val = power;
2314cdb5621SLucas Tanure
2324cdb5621SLucas Tanure return 0;
2334cdb5621SLucas Tanure }
2344cdb5621SLucas Tanure
lochnagar_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int chan)2354cdb5621SLucas Tanure static umode_t lochnagar_is_visible(const void *drvdata,
2364cdb5621SLucas Tanure enum hwmon_sensor_types type,
2374cdb5621SLucas Tanure u32 attr, int chan)
2384cdb5621SLucas Tanure {
2394cdb5621SLucas Tanure switch (type) {
2404cdb5621SLucas Tanure case hwmon_in:
2414cdb5621SLucas Tanure if (!strcmp("SYSVDD", lochnagar_chan_names[chan]))
2424cdb5621SLucas Tanure return 0;
2434cdb5621SLucas Tanure break;
2444cdb5621SLucas Tanure case hwmon_power:
2454cdb5621SLucas Tanure if (attr == hwmon_power_average_interval)
2464cdb5621SLucas Tanure return 0644;
2474cdb5621SLucas Tanure break;
2484cdb5621SLucas Tanure default:
2494cdb5621SLucas Tanure break;
2504cdb5621SLucas Tanure }
2514cdb5621SLucas Tanure
2524cdb5621SLucas Tanure return 0444;
2534cdb5621SLucas Tanure }
2544cdb5621SLucas Tanure
lochnagar_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int chan,long * val)2554cdb5621SLucas Tanure static int lochnagar_read(struct device *dev, enum hwmon_sensor_types type,
2564cdb5621SLucas Tanure u32 attr, int chan, long *val)
2574cdb5621SLucas Tanure {
2584cdb5621SLucas Tanure struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
2594cdb5621SLucas Tanure int interval;
2604cdb5621SLucas Tanure
2614cdb5621SLucas Tanure switch (type) {
2624cdb5621SLucas Tanure case hwmon_in:
2634cdb5621SLucas Tanure return read_sensor(dev, chan, LN2_VOLT, 1, LN2_VOLT_UNITS, val);
2644cdb5621SLucas Tanure case hwmon_curr:
2654cdb5621SLucas Tanure return read_sensor(dev, chan, LN2_CURR, 1, LN2_CURR_UNITS, val);
2664cdb5621SLucas Tanure case hwmon_temp:
2674cdb5621SLucas Tanure return read_sensor(dev, chan, LN2_TEMP, 1, LN2_TEMP_UNITS, val);
2684cdb5621SLucas Tanure case hwmon_power:
2694cdb5621SLucas Tanure switch (attr) {
2704cdb5621SLucas Tanure case hwmon_power_average:
2714cdb5621SLucas Tanure return read_power(dev, chan, val);
2724cdb5621SLucas Tanure case hwmon_power_average_interval:
2734cdb5621SLucas Tanure interval = priv->power_nsamples[chan] * LN2_SAMPLE_US;
2744cdb5621SLucas Tanure *val = DIV_ROUND_CLOSEST(interval, 1000);
2754cdb5621SLucas Tanure return 0;
2764cdb5621SLucas Tanure default:
2774cdb5621SLucas Tanure return -EOPNOTSUPP;
2784cdb5621SLucas Tanure }
2794cdb5621SLucas Tanure default:
2804cdb5621SLucas Tanure return -EOPNOTSUPP;
2814cdb5621SLucas Tanure }
2824cdb5621SLucas Tanure }
2834cdb5621SLucas Tanure
lochnagar_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int chan,const char ** str)2844cdb5621SLucas Tanure static int lochnagar_read_string(struct device *dev,
2854cdb5621SLucas Tanure enum hwmon_sensor_types type, u32 attr,
2864cdb5621SLucas Tanure int chan, const char **str)
2874cdb5621SLucas Tanure {
2884cdb5621SLucas Tanure switch (type) {
2894cdb5621SLucas Tanure case hwmon_in:
2904cdb5621SLucas Tanure case hwmon_curr:
2914cdb5621SLucas Tanure case hwmon_power:
2924cdb5621SLucas Tanure *str = lochnagar_chan_names[chan];
2934cdb5621SLucas Tanure return 0;
2944cdb5621SLucas Tanure default:
2954cdb5621SLucas Tanure return -EOPNOTSUPP;
2964cdb5621SLucas Tanure }
2974cdb5621SLucas Tanure }
2984cdb5621SLucas Tanure
lochnagar_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int chan,long val)2994cdb5621SLucas Tanure static int lochnagar_write(struct device *dev, enum hwmon_sensor_types type,
3004cdb5621SLucas Tanure u32 attr, int chan, long val)
3014cdb5621SLucas Tanure {
3024cdb5621SLucas Tanure struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
3034cdb5621SLucas Tanure
3044cdb5621SLucas Tanure if (type != hwmon_power || attr != hwmon_power_average_interval)
3054cdb5621SLucas Tanure return -EOPNOTSUPP;
3064cdb5621SLucas Tanure
3074cdb5621SLucas Tanure val = clamp_t(long, val, 1, (LN2_MAX_NSAMPLE * LN2_SAMPLE_US) / 1000);
3084cdb5621SLucas Tanure val = DIV_ROUND_CLOSEST(val * 1000, LN2_SAMPLE_US);
3094cdb5621SLucas Tanure
3104cdb5621SLucas Tanure priv->power_nsamples[chan] = val;
3114cdb5621SLucas Tanure
3124cdb5621SLucas Tanure return 0;
3134cdb5621SLucas Tanure }
3144cdb5621SLucas Tanure
3154cdb5621SLucas Tanure static const struct hwmon_ops lochnagar_ops = {
3164cdb5621SLucas Tanure .is_visible = lochnagar_is_visible,
3174cdb5621SLucas Tanure .read = lochnagar_read,
3184cdb5621SLucas Tanure .read_string = lochnagar_read_string,
3194cdb5621SLucas Tanure .write = lochnagar_write,
3204cdb5621SLucas Tanure };
3214cdb5621SLucas Tanure
322*bf36b752SKrzysztof Kozlowski static const struct hwmon_channel_info * const lochnagar_info[] = {
3234cdb5621SLucas Tanure HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
3244cdb5621SLucas Tanure HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL,
3254cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL,
3264cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL,
3274cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL,
3284cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL,
3294cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL,
3304cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL,
3314cdb5621SLucas Tanure HWMON_I_INPUT | HWMON_I_LABEL),
3324cdb5621SLucas Tanure HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL,
3334cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL,
3344cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL,
3354cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL,
3364cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL,
3374cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL,
3384cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL,
3394cdb5621SLucas Tanure HWMON_C_INPUT | HWMON_C_LABEL),
3404cdb5621SLucas Tanure HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3414cdb5621SLucas Tanure HWMON_P_LABEL,
3424cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3434cdb5621SLucas Tanure HWMON_P_LABEL,
3444cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3454cdb5621SLucas Tanure HWMON_P_LABEL,
3464cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3474cdb5621SLucas Tanure HWMON_P_LABEL,
3484cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3494cdb5621SLucas Tanure HWMON_P_LABEL,
3504cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3514cdb5621SLucas Tanure HWMON_P_LABEL,
3524cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3534cdb5621SLucas Tanure HWMON_P_LABEL,
3544cdb5621SLucas Tanure HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
3554cdb5621SLucas Tanure HWMON_P_LABEL),
3564cdb5621SLucas Tanure NULL
3574cdb5621SLucas Tanure };
3584cdb5621SLucas Tanure
3594cdb5621SLucas Tanure static const struct hwmon_chip_info lochnagar_chip_info = {
3604cdb5621SLucas Tanure .ops = &lochnagar_ops,
3614cdb5621SLucas Tanure .info = lochnagar_info,
3624cdb5621SLucas Tanure };
3634cdb5621SLucas Tanure
3644cdb5621SLucas Tanure static const struct of_device_id lochnagar_of_match[] = {
3654cdb5621SLucas Tanure { .compatible = "cirrus,lochnagar2-hwmon" },
3664cdb5621SLucas Tanure {}
3674cdb5621SLucas Tanure };
3684cdb5621SLucas Tanure MODULE_DEVICE_TABLE(of, lochnagar_of_match);
3694cdb5621SLucas Tanure
lochnagar_hwmon_probe(struct platform_device * pdev)3704cdb5621SLucas Tanure static int lochnagar_hwmon_probe(struct platform_device *pdev)
3714cdb5621SLucas Tanure {
3724cdb5621SLucas Tanure struct device *dev = &pdev->dev;
3734cdb5621SLucas Tanure struct device *hwmon_dev;
3744cdb5621SLucas Tanure struct lochnagar_hwmon *priv;
3754cdb5621SLucas Tanure int i;
3764cdb5621SLucas Tanure
3774cdb5621SLucas Tanure priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
3784cdb5621SLucas Tanure if (!priv)
3794cdb5621SLucas Tanure return -ENOMEM;
3804cdb5621SLucas Tanure
3814cdb5621SLucas Tanure mutex_init(&priv->sensor_lock);
3824cdb5621SLucas Tanure
3834cdb5621SLucas Tanure priv->regmap = dev_get_regmap(dev->parent, NULL);
3844cdb5621SLucas Tanure if (!priv->regmap) {
3854cdb5621SLucas Tanure dev_err(dev, "No register map found\n");
3864cdb5621SLucas Tanure return -EINVAL;
3874cdb5621SLucas Tanure }
3884cdb5621SLucas Tanure
3894cdb5621SLucas Tanure for (i = 0; i < ARRAY_SIZE(priv->power_nsamples); i++)
3904cdb5621SLucas Tanure priv->power_nsamples[i] = 96;
3914cdb5621SLucas Tanure
3924cdb5621SLucas Tanure hwmon_dev = devm_hwmon_device_register_with_info(dev, "Lochnagar", priv,
3934cdb5621SLucas Tanure &lochnagar_chip_info,
3944cdb5621SLucas Tanure NULL);
3954cdb5621SLucas Tanure
3964cdb5621SLucas Tanure return PTR_ERR_OR_ZERO(hwmon_dev);
3974cdb5621SLucas Tanure }
3984cdb5621SLucas Tanure
3994cdb5621SLucas Tanure static struct platform_driver lochnagar_hwmon_driver = {
4004cdb5621SLucas Tanure .driver = {
4014cdb5621SLucas Tanure .name = "lochnagar-hwmon",
4024cdb5621SLucas Tanure .of_match_table = lochnagar_of_match,
4034cdb5621SLucas Tanure },
4044cdb5621SLucas Tanure .probe = lochnagar_hwmon_probe,
4054cdb5621SLucas Tanure };
4064cdb5621SLucas Tanure module_platform_driver(lochnagar_hwmon_driver);
4074cdb5621SLucas Tanure
4084cdb5621SLucas Tanure MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
4094cdb5621SLucas Tanure MODULE_DESCRIPTION("Lochnagar hardware monitoring features");
4104cdb5621SLucas Tanure MODULE_LICENSE("GPL");
411