xref: /linux/drivers/hwmon/lochnagar-hwmon.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
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