xref: /openbmc/linux/drivers/hwmon/adm9240.c (revision 1975d167)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28d5d45fbSJean Delvare /*
38d5d45fbSJean Delvare  * adm9240.c	Part of lm_sensors, Linux kernel modules for hardware
48d5d45fbSJean Delvare  *		monitoring
58d5d45fbSJean Delvare  *
68d5d45fbSJean Delvare  * Copyright (C) 1999	Frodo Looijaard <frodol@dds.nl>
78d5d45fbSJean Delvare  *			Philip Edelbrock <phil@netroedge.com>
88d5d45fbSJean Delvare  * Copyright (C) 2003	Michiel Rook <michiel@grendelproject.nl>
92ca7b961SGrant Coady  * Copyright (C) 2005	Grant Coady <gcoady.lk@gmail.com> with valuable
108d5d45fbSJean Delvare  *				guidance from Jean Delvare
118d5d45fbSJean Delvare  *
128d5d45fbSJean Delvare  * Driver supports	Analog Devices		ADM9240
138d5d45fbSJean Delvare  *			Dallas Semiconductor	DS1780
148d5d45fbSJean Delvare  *			National Semiconductor	LM81
158d5d45fbSJean Delvare  *
168d5d45fbSJean Delvare  * ADM9240 is the reference, DS1780 and LM81 are register compatibles
178d5d45fbSJean Delvare  *
188d5d45fbSJean Delvare  * Voltage	Six inputs are scaled by chip, VID also reported
198d5d45fbSJean Delvare  * Temperature	Chip temperature to 0.5'C, maximum and max_hysteris
208d5d45fbSJean Delvare  * Fans		2 fans, low speed alarm, automatic fan clock divider
218d5d45fbSJean Delvare  * Alarms	16-bit map of active alarms
228d5d45fbSJean Delvare  * Analog Out	0..1250 mV output
238d5d45fbSJean Delvare  *
240de2b244SJean Delvare  * Chassis Intrusion: clear CI latch with 'echo 0 > intrusion0_alarm'
258d5d45fbSJean Delvare  *
268d5d45fbSJean Delvare  * Test hardware: Intel SE440BX-2 desktop motherboard --Grant
278d5d45fbSJean Delvare  *
288d5d45fbSJean Delvare  * LM81 extended temp reading not implemented
298d5d45fbSJean Delvare  */
308d5d45fbSJean Delvare 
31124b7e34SGuenter Roeck #include <linux/bits.h>
328d5d45fbSJean Delvare #include <linux/init.h>
338d5d45fbSJean Delvare #include <linux/module.h>
348d5d45fbSJean Delvare #include <linux/slab.h>
358d5d45fbSJean Delvare #include <linux/i2c.h>
36c7461a66SGrant Coady #include <linux/hwmon-sysfs.h>
37943b0830SMark M. Hoffman #include <linux/hwmon.h>
38303760b4SJean Delvare #include <linux/hwmon-vid.h>
39943b0830SMark M. Hoffman #include <linux/err.h>
409a61bf63SIngo Molnar #include <linux/mutex.h>
41df885d91SChris Packham #include <linux/regmap.h>
428d5d45fbSJean Delvare 
438d5d45fbSJean Delvare /* Addresses to scan */
4425e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
458d5d45fbSJean Delvare 					I2C_CLIENT_END };
468d5d45fbSJean Delvare 
47e5e9f44cSJean Delvare enum chips { adm9240, ds1780, lm81 };
488d5d45fbSJean Delvare 
498d5d45fbSJean Delvare /* ADM9240 registers */
508d5d45fbSJean Delvare #define ADM9240_REG_MAN_ID		0x3e
518d5d45fbSJean Delvare #define ADM9240_REG_DIE_REV		0x3f
528d5d45fbSJean Delvare #define ADM9240_REG_CONFIG		0x40
538d5d45fbSJean Delvare 
548d5d45fbSJean Delvare #define ADM9240_REG_IN(nr)		(0x20 + (nr))   /* 0..5 */
558d5d45fbSJean Delvare #define ADM9240_REG_IN_MAX(nr)		(0x2b + (nr) * 2)
568d5d45fbSJean Delvare #define ADM9240_REG_IN_MIN(nr)		(0x2c + (nr) * 2)
578d5d45fbSJean Delvare #define ADM9240_REG_FAN(nr)		(0x28 + (nr))   /* 0..1 */
588d5d45fbSJean Delvare #define ADM9240_REG_FAN_MIN(nr)		(0x3b + (nr))
598d5d45fbSJean Delvare #define ADM9240_REG_INT(nr)		(0x41 + (nr))
608d5d45fbSJean Delvare #define ADM9240_REG_INT_MASK(nr)	(0x43 + (nr))
618d5d45fbSJean Delvare #define ADM9240_REG_TEMP		0x27
62c7461a66SGrant Coady #define ADM9240_REG_TEMP_MAX(nr)	(0x39 + (nr)) /* 0, 1 = high, hyst */
638d5d45fbSJean Delvare #define ADM9240_REG_ANALOG_OUT		0x19
648d5d45fbSJean Delvare #define ADM9240_REG_CHASSIS_CLEAR	0x46
658d5d45fbSJean Delvare #define ADM9240_REG_VID_FAN_DIV		0x47
668d5d45fbSJean Delvare #define ADM9240_REG_I2C_ADDR		0x48
678d5d45fbSJean Delvare #define ADM9240_REG_VID4		0x49
688d5d45fbSJean Delvare #define ADM9240_REG_TEMP_CONF		0x4b
698d5d45fbSJean Delvare 
708d5d45fbSJean Delvare /* generalised scaling with integer rounding */
SCALE(long val,int mul,int div)718d5d45fbSJean Delvare static inline int SCALE(long val, int mul, int div)
728d5d45fbSJean Delvare {
738d5d45fbSJean Delvare 	if (val < 0)
748d5d45fbSJean Delvare 		return (val * mul - div / 2) / div;
758d5d45fbSJean Delvare 	else
768d5d45fbSJean Delvare 		return (val * mul + div / 2) / div;
778d5d45fbSJean Delvare }
788d5d45fbSJean Delvare 
798d5d45fbSJean Delvare /* adm9240 internally scales voltage measurements */
808d5d45fbSJean Delvare static const u16 nom_mv[] = { 2500, 2700, 3300, 5000, 12000, 2700 };
818d5d45fbSJean Delvare 
IN_FROM_REG(u8 reg,int n)828d5d45fbSJean Delvare static inline unsigned int IN_FROM_REG(u8 reg, int n)
838d5d45fbSJean Delvare {
848d5d45fbSJean Delvare 	return SCALE(reg, nom_mv[n], 192);
858d5d45fbSJean Delvare }
868d5d45fbSJean Delvare 
IN_TO_REG(unsigned long val,int n)878d5d45fbSJean Delvare static inline u8 IN_TO_REG(unsigned long val, int n)
888d5d45fbSJean Delvare {
890fb620c4SGuenter Roeck 	val = clamp_val(val, 0, nom_mv[n] * 255 / 192);
900fb620c4SGuenter Roeck 	return SCALE(val, 192, nom_mv[n]);
918d5d45fbSJean Delvare }
928d5d45fbSJean Delvare 
938d5d45fbSJean Delvare /* temperature range: -40..125, 127 disables temperature alarm */
TEMP_TO_REG(long val)948d5d45fbSJean Delvare static inline s8 TEMP_TO_REG(long val)
958d5d45fbSJean Delvare {
960fb620c4SGuenter Roeck 	val = clamp_val(val, -40000, 127000);
970fb620c4SGuenter Roeck 	return SCALE(val, 1, 1000);
988d5d45fbSJean Delvare }
998d5d45fbSJean Delvare 
1008d5d45fbSJean Delvare /* two fans, each with low fan speed limit */
FAN_FROM_REG(u8 reg,u8 div)1018d5d45fbSJean Delvare static inline unsigned int FAN_FROM_REG(u8 reg, u8 div)
1028d5d45fbSJean Delvare {
1038d5d45fbSJean Delvare 	if (!reg) /* error */
1048d5d45fbSJean Delvare 		return -1;
1058d5d45fbSJean Delvare 
1068d5d45fbSJean Delvare 	if (reg == 255)
1078d5d45fbSJean Delvare 		return 0;
1088d5d45fbSJean Delvare 
1098d5d45fbSJean Delvare 	return SCALE(1350000, 1, reg * div);
1108d5d45fbSJean Delvare }
1118d5d45fbSJean Delvare 
1128d5d45fbSJean Delvare /* analog out 0..1250mV */
AOUT_TO_REG(unsigned long val)1138d5d45fbSJean Delvare static inline u8 AOUT_TO_REG(unsigned long val)
1148d5d45fbSJean Delvare {
1150fb620c4SGuenter Roeck 	val = clamp_val(val, 0, 1250);
1160fb620c4SGuenter Roeck 	return SCALE(val, 255, 1250);
1178d5d45fbSJean Delvare }
1188d5d45fbSJean Delvare 
AOUT_FROM_REG(u8 reg)1198d5d45fbSJean Delvare static inline unsigned int AOUT_FROM_REG(u8 reg)
1208d5d45fbSJean Delvare {
1218d5d45fbSJean Delvare 	return SCALE(reg, 1250, 255);
1228d5d45fbSJean Delvare }
1238d5d45fbSJean Delvare 
1248d5d45fbSJean Delvare /* per client data */
1258d5d45fbSJean Delvare struct adm9240_data {
1264f427dcbSGuenter Roeck 	struct device *dev;
127df885d91SChris Packham 	struct regmap *regmap;
1289a61bf63SIngo Molnar 	struct mutex update_lock;
1298d5d45fbSJean Delvare 
1308d5d45fbSJean Delvare 	u8 fan_div[2];		/* rw	fan1_div, read-only accessor */
1318d5d45fbSJean Delvare 	u8 vrm;			/* --	vrm set on startup, no accessor */
1328d5d45fbSJean Delvare };
1338d5d45fbSJean Delvare 
1344341fc3fSAxel Lin /* write new fan div, callers must hold data->update_lock */
adm9240_write_fan_div(struct adm9240_data * data,int channel,u8 fan_div)135124b7e34SGuenter Roeck static int adm9240_write_fan_div(struct adm9240_data *data, int channel, u8 fan_div)
1364341fc3fSAxel Lin {
137124b7e34SGuenter Roeck 	unsigned int reg, old, shift = (channel + 2) * 2;
138df885d91SChris Packham 	int err;
1394341fc3fSAxel Lin 
140df885d91SChris Packham 	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &reg);
141df885d91SChris Packham 	if (err < 0)
142df885d91SChris Packham 		return err;
1434341fc3fSAxel Lin 	old = (reg >> shift) & 3;
1444341fc3fSAxel Lin 	reg &= ~(3 << shift);
1454341fc3fSAxel Lin 	reg |= (fan_div << shift);
146df885d91SChris Packham 	err = regmap_write(data->regmap, ADM9240_REG_VID_FAN_DIV, reg);
147df885d91SChris Packham 	if (err < 0)
148df885d91SChris Packham 		return err;
1494f427dcbSGuenter Roeck 	dev_dbg(data->dev,
150124b7e34SGuenter Roeck 		"fan%d clock divider changed from %lu to %lu\n",
151124b7e34SGuenter Roeck 		channel + 1, BIT(old), BIT(fan_div));
152df885d91SChris Packham 
153df885d91SChris Packham 	return 0;
1544341fc3fSAxel Lin }
1554341fc3fSAxel Lin 
1568d5d45fbSJean Delvare /*
1578d5d45fbSJean Delvare  * set fan speed low limit:
1588d5d45fbSJean Delvare  *
1598d5d45fbSJean Delvare  * - value is zero: disable fan speed low limit alarm
1608d5d45fbSJean Delvare  *
1618d5d45fbSJean Delvare  * - value is below fan speed measurement range: enable fan speed low
1628d5d45fbSJean Delvare  *   limit alarm to be asserted while fan speed too slow to measure
1638d5d45fbSJean Delvare  *
1648d5d45fbSJean Delvare  * - otherwise: select fan clock divider to suit fan speed low limit,
1658d5d45fbSJean Delvare  *   measurement code may adjust registers to ensure fan speed reading
1668d5d45fbSJean Delvare  */
adm9240_fan_min_write(struct adm9240_data * data,int channel,long val)167124b7e34SGuenter Roeck static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long val)
1688d5d45fbSJean Delvare {
1698d5d45fbSJean Delvare 	u8 new_div;
170124b7e34SGuenter Roeck 	u8 fan_min;
171c387e4ebSGuenter Roeck 	int err;
172c387e4ebSGuenter Roeck 
1739a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
1748d5d45fbSJean Delvare 
1758d5d45fbSJean Delvare 	if (!val) {
176124b7e34SGuenter Roeck 		fan_min = 255;
177124b7e34SGuenter Roeck 		new_div = data->fan_div[channel];
1788d5d45fbSJean Delvare 
179124b7e34SGuenter Roeck 		dev_dbg(data->dev, "fan%u low limit set disabled\n", channel + 1);
1808d5d45fbSJean Delvare 	} else if (val < 1350000 / (8 * 254)) {
1818d5d45fbSJean Delvare 		new_div = 3;
182124b7e34SGuenter Roeck 		fan_min = 254;
1838d5d45fbSJean Delvare 
1844f427dcbSGuenter Roeck 		dev_dbg(data->dev, "fan%u low limit set minimum %u\n",
185124b7e34SGuenter Roeck 			channel + 1, FAN_FROM_REG(254, BIT(new_div)));
1868d5d45fbSJean Delvare 	} else {
1878d5d45fbSJean Delvare 		unsigned int new_min = 1350000 / val;
1888d5d45fbSJean Delvare 
1898d5d45fbSJean Delvare 		new_div = 0;
1908d5d45fbSJean Delvare 		while (new_min > 192 && new_div < 3) {
1918d5d45fbSJean Delvare 			new_div++;
1928d5d45fbSJean Delvare 			new_min /= 2;
1938d5d45fbSJean Delvare 		}
1948d5d45fbSJean Delvare 		if (!new_min) /* keep > 0 */
1958d5d45fbSJean Delvare 			new_min++;
1968d5d45fbSJean Delvare 
197124b7e34SGuenter Roeck 		fan_min = new_min;
1988d5d45fbSJean Delvare 
1994f427dcbSGuenter Roeck 		dev_dbg(data->dev, "fan%u low limit set fan speed %u\n",
200124b7e34SGuenter Roeck 			channel + 1, FAN_FROM_REG(new_min, BIT(new_div)));
2018d5d45fbSJean Delvare 	}
2028d5d45fbSJean Delvare 
203124b7e34SGuenter Roeck 	if (new_div != data->fan_div[channel]) {
204124b7e34SGuenter Roeck 		data->fan_div[channel] = new_div;
205124b7e34SGuenter Roeck 		adm9240_write_fan_div(data, channel, new_div);
2068d5d45fbSJean Delvare 	}
207124b7e34SGuenter Roeck 	err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min);
2088d5d45fbSJean Delvare 
2099a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
210124b7e34SGuenter Roeck 
211124b7e34SGuenter Roeck 	return err;
2128d5d45fbSJean Delvare }
2138d5d45fbSJean Delvare 
cpu0_vid_show(struct device * dev,struct device_attribute * attr,char * buf)214b5751116SJulia Lawall static ssize_t cpu0_vid_show(struct device *dev,
215e415e48bSJean Delvare 			     struct device_attribute *attr, char *buf)
2168d5d45fbSJean Delvare {
217124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
218124b7e34SGuenter Roeck 	unsigned int regval;
219124b7e34SGuenter Roeck 	int err;
220124b7e34SGuenter Roeck 	u8 vid;
2216a8cdd14SChris Packham 
222124b7e34SGuenter Roeck 	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &regval);
223124b7e34SGuenter Roeck 	if (err < 0)
224124b7e34SGuenter Roeck 		return err;
225124b7e34SGuenter Roeck 	vid = regval & 0x0f;
226124b7e34SGuenter Roeck 	err = regmap_read(data->regmap, ADM9240_REG_VID4, &regval);
227124b7e34SGuenter Roeck 	if (err < 0)
228124b7e34SGuenter Roeck 		return err;
229124b7e34SGuenter Roeck 	vid |= (regval & 1) << 4;
230124b7e34SGuenter Roeck 	return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));
2318d5d45fbSJean Delvare }
232b5751116SJulia Lawall static DEVICE_ATTR_RO(cpu0_vid);
2338d5d45fbSJean Delvare 
aout_output_show(struct device * dev,struct device_attribute * attr,char * buf)234b5751116SJulia Lawall static ssize_t aout_output_show(struct device *dev,
235e415e48bSJean Delvare 				struct device_attribute *attr, char *buf)
2368d5d45fbSJean Delvare {
237124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
238124b7e34SGuenter Roeck 	unsigned int regval;
239124b7e34SGuenter Roeck 	int err;
2406a8cdd14SChris Packham 
241124b7e34SGuenter Roeck 	err = regmap_read(data->regmap, ADM9240_REG_ANALOG_OUT, &regval);
242124b7e34SGuenter Roeck 	if (err)
243124b7e34SGuenter Roeck 		return err;
2446a8cdd14SChris Packham 
245124b7e34SGuenter Roeck 	return sprintf(buf, "%d\n", AOUT_FROM_REG(regval));
2468d5d45fbSJean Delvare }
2478d5d45fbSJean Delvare 
aout_output_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)248b5751116SJulia Lawall static ssize_t aout_output_store(struct device *dev,
249e415e48bSJean Delvare 				 struct device_attribute *attr,
250e415e48bSJean Delvare 				 const char *buf, size_t count)
2518d5d45fbSJean Delvare {
252715f69beSAxel Lin 	struct adm9240_data *data = dev_get_drvdata(dev);
253c387e4ebSGuenter Roeck 	long val;
254c387e4ebSGuenter Roeck 	int err;
255c387e4ebSGuenter Roeck 
256c387e4ebSGuenter Roeck 	err = kstrtol(buf, 10, &val);
257c387e4ebSGuenter Roeck 	if (err)
258c387e4ebSGuenter Roeck 		return err;
2598d5d45fbSJean Delvare 
260124b7e34SGuenter Roeck 	err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, AOUT_TO_REG(val));
261df885d91SChris Packham 	return err < 0 ? err : count;
2628d5d45fbSJean Delvare }
263b5751116SJulia Lawall static DEVICE_ATTR_RW(aout_output);
2648d5d45fbSJean Delvare 
265715f69beSAxel Lin static struct attribute *adm9240_attrs[] = {
266681c6f7aSMark M. Hoffman 	&dev_attr_aout_output.attr,
267681c6f7aSMark M. Hoffman 	&dev_attr_cpu0_vid.attr,
268681c6f7aSMark M. Hoffman 	NULL
269681c6f7aSMark M. Hoffman };
270681c6f7aSMark M. Hoffman 
271715f69beSAxel Lin ATTRIBUTE_GROUPS(adm9240);
272681c6f7aSMark M. Hoffman 
2738d5d45fbSJean Delvare /*** sensor chip detect and driver install ***/
2748d5d45fbSJean Delvare 
2757fae8283SJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
adm9240_detect(struct i2c_client * new_client,struct i2c_board_info * info)276310ec792SJean Delvare static int adm9240_detect(struct i2c_client *new_client,
2777fae8283SJean Delvare 			  struct i2c_board_info *info)
2788d5d45fbSJean Delvare {
2797fae8283SJean Delvare 	struct i2c_adapter *adapter = new_client->adapter;
2808d5d45fbSJean Delvare 	const char *name = "";
2817fae8283SJean Delvare 	int address = new_client->addr;
2828d5d45fbSJean Delvare 	u8 man_id, die_rev;
2838d5d45fbSJean Delvare 
2848d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
2857fae8283SJean Delvare 		return -ENODEV;
2868d5d45fbSJean Delvare 
2878d5d45fbSJean Delvare 	/* verify chip: reg address should match i2c address */
28869698bdeSGuenter Roeck 	if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) != address)
2897fae8283SJean Delvare 		return -ENODEV;
2908d5d45fbSJean Delvare 
2918d5d45fbSJean Delvare 	/* check known chip manufacturer */
29252df6440SJean Delvare 	man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID);
29369698bdeSGuenter Roeck 	if (man_id == 0x23)
29452df6440SJean Delvare 		name = "adm9240";
29569698bdeSGuenter Roeck 	else if (man_id == 0xda)
29652df6440SJean Delvare 		name = "ds1780";
29769698bdeSGuenter Roeck 	else if (man_id == 0x01)
29852df6440SJean Delvare 		name = "lm81";
29969698bdeSGuenter Roeck 	else
3007fae8283SJean Delvare 		return -ENODEV;
3018d5d45fbSJean Delvare 
3028d5d45fbSJean Delvare 	/* successful detect, print chip info */
30352df6440SJean Delvare 	die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV);
3048d5d45fbSJean Delvare 	dev_info(&adapter->dev, "found %s revision %u\n",
3058d5d45fbSJean Delvare 		 man_id == 0x23 ? "ADM9240" :
3068d5d45fbSJean Delvare 		 man_id == 0xda ? "DS1780" : "LM81", die_rev);
3078d5d45fbSJean Delvare 
308124b7e34SGuenter Roeck 	strscpy(info->type, name, I2C_NAME_SIZE);
3098d5d45fbSJean Delvare 
3107fae8283SJean Delvare 	return 0;
3117fae8283SJean Delvare }
3127fae8283SJean Delvare 
adm9240_init_client(struct adm9240_data * data)3134f427dcbSGuenter Roeck static int adm9240_init_client(struct adm9240_data *data)
3144341fc3fSAxel Lin {
315124b7e34SGuenter Roeck 	unsigned int regval;
316df885d91SChris Packham 	u8 conf, mode;
317df885d91SChris Packham 	int err;
318df885d91SChris Packham 
319df885d91SChris Packham 	err = regmap_raw_read(data->regmap, ADM9240_REG_CONFIG, &conf, 1);
320df885d91SChris Packham 	if (err < 0)
321df885d91SChris Packham 		return err;
322df885d91SChris Packham 	err = regmap_raw_read(data->regmap, ADM9240_REG_TEMP_CONF, &mode, 1);
323df885d91SChris Packham 	if (err < 0)
324df885d91SChris Packham 		return err;
325df885d91SChris Packham 	mode &= 3;
3264341fc3fSAxel Lin 
3274341fc3fSAxel Lin 	data->vrm = vid_which_vrm(); /* need this to report vid as mV */
3284341fc3fSAxel Lin 
3294f427dcbSGuenter Roeck 	dev_info(data->dev, "Using VRM: %d.%d\n", data->vrm / 10,
3304341fc3fSAxel Lin 		 data->vrm % 10);
3314341fc3fSAxel Lin 
3324341fc3fSAxel Lin 	if (conf & 1) { /* measurement cycle running: report state */
3334341fc3fSAxel Lin 
3344f427dcbSGuenter Roeck 		dev_info(data->dev, "status: config 0x%02x mode %u\n",
3354341fc3fSAxel Lin 			 conf, mode);
3364341fc3fSAxel Lin 
3374341fc3fSAxel Lin 	} else { /* cold start: open limits before starting chip */
3384341fc3fSAxel Lin 		int i;
3394341fc3fSAxel Lin 
3404341fc3fSAxel Lin 		for (i = 0; i < 6; i++) {
341df885d91SChris Packham 			err = regmap_write(data->regmap,
3424341fc3fSAxel Lin 					   ADM9240_REG_IN_MIN(i), 0);
343df885d91SChris Packham 			if (err < 0)
344df885d91SChris Packham 				return err;
345df885d91SChris Packham 			err = regmap_write(data->regmap,
3464341fc3fSAxel Lin 					   ADM9240_REG_IN_MAX(i), 255);
347df885d91SChris Packham 			if (err < 0)
348df885d91SChris Packham 				return err;
3494341fc3fSAxel Lin 		}
35010d09773SChris Packham 		for (i = 0; i < 2; i++) {
351df885d91SChris Packham 			err = regmap_write(data->regmap,
35210d09773SChris Packham 					   ADM9240_REG_FAN_MIN(i), 255);
353df885d91SChris Packham 			if (err < 0)
354df885d91SChris Packham 				return err;
35510d09773SChris Packham 		}
35610d09773SChris Packham 		for (i = 0; i < 2; i++) {
357df885d91SChris Packham 			err = regmap_write(data->regmap,
35810d09773SChris Packham 					   ADM9240_REG_TEMP_MAX(i), 127);
359df885d91SChris Packham 			if (err < 0)
360df885d91SChris Packham 				return err;
36110d09773SChris Packham 		}
3624341fc3fSAxel Lin 
3634341fc3fSAxel Lin 		/* start measurement cycle */
364df885d91SChris Packham 		err = regmap_write(data->regmap, ADM9240_REG_CONFIG, 1);
365df885d91SChris Packham 		if (err < 0)
366df885d91SChris Packham 			return err;
3674341fc3fSAxel Lin 
3684f427dcbSGuenter Roeck 		dev_info(data->dev,
3694341fc3fSAxel Lin 			 "cold start: config was 0x%02x mode %u\n", conf, mode);
3704341fc3fSAxel Lin 	}
371df885d91SChris Packham 
372124b7e34SGuenter Roeck 	/* read fan divs */
373124b7e34SGuenter Roeck 	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &regval);
374124b7e34SGuenter Roeck 	if (err < 0)
375124b7e34SGuenter Roeck 		return err;
376124b7e34SGuenter Roeck 	data->fan_div[0] = (regval >> 4) & 3;
377124b7e34SGuenter Roeck 	data->fan_div[1] = (regval >> 6) & 3;
378df885d91SChris Packham 	return 0;
3794341fc3fSAxel Lin }
3804341fc3fSAxel Lin 
adm9240_chip_read(struct device * dev,u32 attr,long * val)381124b7e34SGuenter Roeck static int adm9240_chip_read(struct device *dev, u32 attr, long *val)
382124b7e34SGuenter Roeck {
383124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
384124b7e34SGuenter Roeck 	u8 regs[2];
385124b7e34SGuenter Roeck 	int err;
386124b7e34SGuenter Roeck 
387124b7e34SGuenter Roeck 	switch (attr) {
388124b7e34SGuenter Roeck 	case hwmon_chip_alarms:
389124b7e34SGuenter Roeck 		err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), &regs, 2);
390124b7e34SGuenter Roeck 		if (err < 0)
391124b7e34SGuenter Roeck 			return err;
392124b7e34SGuenter Roeck 		*val = regs[0] | regs[1] << 8;
393124b7e34SGuenter Roeck 		break;
394124b7e34SGuenter Roeck 	default:
395124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
396124b7e34SGuenter Roeck 	}
397124b7e34SGuenter Roeck 	return 0;
398124b7e34SGuenter Roeck }
399124b7e34SGuenter Roeck 
adm9240_intrusion_read(struct device * dev,u32 attr,long * val)400124b7e34SGuenter Roeck static int adm9240_intrusion_read(struct device *dev, u32 attr, long *val)
401124b7e34SGuenter Roeck {
402124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
403124b7e34SGuenter Roeck 	unsigned int regval;
404124b7e34SGuenter Roeck 	int err;
405124b7e34SGuenter Roeck 
406124b7e34SGuenter Roeck 	switch (attr) {
407124b7e34SGuenter Roeck 	case hwmon_intrusion_alarm:
408124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_INT(1), &regval);
409124b7e34SGuenter Roeck 		if (err < 0)
410124b7e34SGuenter Roeck 			return err;
411124b7e34SGuenter Roeck 		*val = !!(regval & BIT(4));
412124b7e34SGuenter Roeck 		break;
413124b7e34SGuenter Roeck 	default:
414124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
415124b7e34SGuenter Roeck 	}
416124b7e34SGuenter Roeck 	return 0;
417124b7e34SGuenter Roeck }
418124b7e34SGuenter Roeck 
adm9240_intrusion_write(struct device * dev,u32 attr,long val)419124b7e34SGuenter Roeck static int adm9240_intrusion_write(struct device *dev, u32 attr, long val)
420124b7e34SGuenter Roeck {
421124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
422124b7e34SGuenter Roeck 	int err;
423124b7e34SGuenter Roeck 
424124b7e34SGuenter Roeck 	switch (attr) {
425124b7e34SGuenter Roeck 	case hwmon_intrusion_alarm:
426124b7e34SGuenter Roeck 		if (val)
427124b7e34SGuenter Roeck 			return -EINVAL;
428124b7e34SGuenter Roeck 		err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80);
429124b7e34SGuenter Roeck 		if (err < 0)
430124b7e34SGuenter Roeck 			return err;
431124b7e34SGuenter Roeck 		dev_dbg(data->dev, "chassis intrusion latch cleared\n");
432124b7e34SGuenter Roeck 		break;
433124b7e34SGuenter Roeck 	default:
434124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
435124b7e34SGuenter Roeck 	}
436124b7e34SGuenter Roeck 	return 0;
437124b7e34SGuenter Roeck }
438124b7e34SGuenter Roeck 
adm9240_in_read(struct device * dev,u32 attr,int channel,long * val)439124b7e34SGuenter Roeck static int adm9240_in_read(struct device *dev, u32 attr, int channel, long *val)
440124b7e34SGuenter Roeck {
441124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
442124b7e34SGuenter Roeck 	unsigned int regval;
443124b7e34SGuenter Roeck 	int reg;
444124b7e34SGuenter Roeck 	int err;
445124b7e34SGuenter Roeck 
446124b7e34SGuenter Roeck 	switch (attr) {
447124b7e34SGuenter Roeck 	case hwmon_in_input:
448124b7e34SGuenter Roeck 		reg = ADM9240_REG_IN(channel);
449124b7e34SGuenter Roeck 		break;
450124b7e34SGuenter Roeck 	case hwmon_in_min:
451124b7e34SGuenter Roeck 		reg = ADM9240_REG_IN_MIN(channel);
452124b7e34SGuenter Roeck 		break;
453124b7e34SGuenter Roeck 	case hwmon_in_max:
454124b7e34SGuenter Roeck 		reg = ADM9240_REG_IN_MAX(channel);
455124b7e34SGuenter Roeck 		break;
456124b7e34SGuenter Roeck 	case hwmon_in_alarm:
457124b7e34SGuenter Roeck 		if (channel < 4) {
458124b7e34SGuenter Roeck 			reg = ADM9240_REG_INT(0);
459124b7e34SGuenter Roeck 		} else {
460124b7e34SGuenter Roeck 			reg = ADM9240_REG_INT(1);
461124b7e34SGuenter Roeck 			channel -= 4;
462124b7e34SGuenter Roeck 		}
463124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, reg, &regval);
464124b7e34SGuenter Roeck 		if (err < 0)
465124b7e34SGuenter Roeck 			return err;
466124b7e34SGuenter Roeck 		*val = !!(regval & BIT(channel));
467124b7e34SGuenter Roeck 		return 0;
468124b7e34SGuenter Roeck 	default:
469124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
470124b7e34SGuenter Roeck 	}
471124b7e34SGuenter Roeck 	err = regmap_read(data->regmap, reg, &regval);
472124b7e34SGuenter Roeck 	if (err < 0)
473124b7e34SGuenter Roeck 		return err;
474124b7e34SGuenter Roeck 	*val = IN_FROM_REG(regval, channel);
475124b7e34SGuenter Roeck 	return 0;
476124b7e34SGuenter Roeck }
477124b7e34SGuenter Roeck 
adm9240_in_write(struct device * dev,u32 attr,int channel,long val)478124b7e34SGuenter Roeck static int adm9240_in_write(struct device *dev, u32 attr, int channel, long val)
479124b7e34SGuenter Roeck {
480124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
481124b7e34SGuenter Roeck 	int reg;
482124b7e34SGuenter Roeck 
483124b7e34SGuenter Roeck 	switch (attr) {
484124b7e34SGuenter Roeck 	case hwmon_in_min:
485124b7e34SGuenter Roeck 		reg = ADM9240_REG_IN_MIN(channel);
486124b7e34SGuenter Roeck 		break;
487124b7e34SGuenter Roeck 	case hwmon_in_max:
4883b5169c2SGuenter Roeck 		reg = ADM9240_REG_IN_MAX(channel);
489124b7e34SGuenter Roeck 		break;
490124b7e34SGuenter Roeck 	default:
491124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
492124b7e34SGuenter Roeck 	}
493124b7e34SGuenter Roeck 	return regmap_write(data->regmap, reg, IN_TO_REG(val, channel));
494124b7e34SGuenter Roeck }
495124b7e34SGuenter Roeck 
adm9240_fan_read(struct device * dev,u32 attr,int channel,long * val)496124b7e34SGuenter Roeck static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val)
497124b7e34SGuenter Roeck {
498124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
499124b7e34SGuenter Roeck 	unsigned int regval;
500124b7e34SGuenter Roeck 	int err;
501124b7e34SGuenter Roeck 
502124b7e34SGuenter Roeck 	switch (attr) {
503124b7e34SGuenter Roeck 	case hwmon_fan_input:
5048887516fSLi Zhong 		mutex_lock(&data->update_lock);
505124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), &regval);
5068887516fSLi Zhong 		if (err < 0) {
5078887516fSLi Zhong 			mutex_unlock(&data->update_lock);
508124b7e34SGuenter Roeck 			return err;
5098887516fSLi Zhong 		}
510124b7e34SGuenter Roeck 		if (regval == 255 && data->fan_div[channel] < 3) {
511124b7e34SGuenter Roeck 			/* adjust fan clock divider on overflow */
512124b7e34SGuenter Roeck 			err = adm9240_write_fan_div(data, channel,
513124b7e34SGuenter Roeck 						    ++data->fan_div[channel]);
5148887516fSLi Zhong 			if (err) {
5158887516fSLi Zhong 				mutex_unlock(&data->update_lock);
516124b7e34SGuenter Roeck 				return err;
517124b7e34SGuenter Roeck 			}
5188887516fSLi Zhong 		}
519124b7e34SGuenter Roeck 		*val = FAN_FROM_REG(regval, BIT(data->fan_div[channel]));
5208887516fSLi Zhong 		mutex_unlock(&data->update_lock);
521124b7e34SGuenter Roeck 		break;
522124b7e34SGuenter Roeck 	case hwmon_fan_div:
523124b7e34SGuenter Roeck 		*val = BIT(data->fan_div[channel]);
524124b7e34SGuenter Roeck 		break;
525124b7e34SGuenter Roeck 	case hwmon_fan_min:
526124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_FAN_MIN(channel), &regval);
527124b7e34SGuenter Roeck 		if (err < 0)
528124b7e34SGuenter Roeck 			return err;
529124b7e34SGuenter Roeck 		*val = FAN_FROM_REG(regval, BIT(data->fan_div[channel]));
530124b7e34SGuenter Roeck 		break;
531124b7e34SGuenter Roeck 	case hwmon_fan_alarm:
532124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_INT(0), &regval);
533124b7e34SGuenter Roeck 		if (err < 0)
534124b7e34SGuenter Roeck 			return err;
535124b7e34SGuenter Roeck 		*val = !!(regval & BIT(channel + 6));
536124b7e34SGuenter Roeck 		break;
537124b7e34SGuenter Roeck 	default:
538124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
539124b7e34SGuenter Roeck 	}
540124b7e34SGuenter Roeck 	return 0;
541124b7e34SGuenter Roeck }
542124b7e34SGuenter Roeck 
adm9240_fan_write(struct device * dev,u32 attr,int channel,long val)543124b7e34SGuenter Roeck static int adm9240_fan_write(struct device *dev, u32 attr, int channel, long val)
544124b7e34SGuenter Roeck {
545124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
546124b7e34SGuenter Roeck 	int err;
547124b7e34SGuenter Roeck 
548124b7e34SGuenter Roeck 	switch (attr) {
549124b7e34SGuenter Roeck 	case hwmon_fan_min:
550124b7e34SGuenter Roeck 		err = adm9240_fan_min_write(data, channel, val);
551124b7e34SGuenter Roeck 		if (err < 0)
552124b7e34SGuenter Roeck 			return err;
553124b7e34SGuenter Roeck 		break;
554124b7e34SGuenter Roeck 	default:
555124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
556124b7e34SGuenter Roeck 	}
557124b7e34SGuenter Roeck 	return 0;
558124b7e34SGuenter Roeck }
559124b7e34SGuenter Roeck 
adm9240_temp_read(struct device * dev,u32 attr,int channel,long * val)560124b7e34SGuenter Roeck static int adm9240_temp_read(struct device *dev, u32 attr, int channel, long *val)
561124b7e34SGuenter Roeck {
562124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
563124b7e34SGuenter Roeck 	unsigned int regval;
564124b7e34SGuenter Roeck 	int err, temp;
565124b7e34SGuenter Roeck 
566124b7e34SGuenter Roeck 	switch (attr) {
567124b7e34SGuenter Roeck 	case hwmon_temp_input:
568124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_TEMP, &regval);
569124b7e34SGuenter Roeck 		if (err < 0)
570124b7e34SGuenter Roeck 			return err;
571124b7e34SGuenter Roeck 		temp = regval << 1;
572124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, &regval);
573124b7e34SGuenter Roeck 		if (err < 0)
574124b7e34SGuenter Roeck 			return err;
575124b7e34SGuenter Roeck 		temp |= regval >> 7;
576124b7e34SGuenter Roeck 		*val = sign_extend32(temp, 8) * 500;
577124b7e34SGuenter Roeck 		break;
578124b7e34SGuenter Roeck 	case hwmon_temp_max:
579124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(0), &regval);
580124b7e34SGuenter Roeck 		if (err < 0)
581124b7e34SGuenter Roeck 			return err;
582124b7e34SGuenter Roeck 		*val = (s8)regval * 1000;
583124b7e34SGuenter Roeck 		break;
584124b7e34SGuenter Roeck 	case hwmon_temp_max_hyst:
585124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(1), &regval);
586124b7e34SGuenter Roeck 		if (err < 0)
587124b7e34SGuenter Roeck 			return err;
588124b7e34SGuenter Roeck 		*val = (s8)regval * 1000;
589124b7e34SGuenter Roeck 		break;
590124b7e34SGuenter Roeck 	case hwmon_temp_alarm:
591124b7e34SGuenter Roeck 		err = regmap_read(data->regmap, ADM9240_REG_INT(0), &regval);
592124b7e34SGuenter Roeck 		if (err < 0)
593124b7e34SGuenter Roeck 			return err;
594124b7e34SGuenter Roeck 		*val = !!(regval & BIT(4));
595124b7e34SGuenter Roeck 		break;
596124b7e34SGuenter Roeck 	default:
597124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
598124b7e34SGuenter Roeck 	}
599124b7e34SGuenter Roeck 	return 0;
600124b7e34SGuenter Roeck }
601124b7e34SGuenter Roeck 
adm9240_temp_write(struct device * dev,u32 attr,int channel,long val)602124b7e34SGuenter Roeck static int adm9240_temp_write(struct device *dev, u32 attr, int channel, long val)
603124b7e34SGuenter Roeck {
604124b7e34SGuenter Roeck 	struct adm9240_data *data = dev_get_drvdata(dev);
605124b7e34SGuenter Roeck 	int reg;
606124b7e34SGuenter Roeck 
607124b7e34SGuenter Roeck 	switch (attr) {
608124b7e34SGuenter Roeck 	case hwmon_temp_max:
609124b7e34SGuenter Roeck 		reg = ADM9240_REG_TEMP_MAX(0);
610124b7e34SGuenter Roeck 		break;
611124b7e34SGuenter Roeck 	case hwmon_temp_max_hyst:
612124b7e34SGuenter Roeck 		reg = ADM9240_REG_TEMP_MAX(1);
613124b7e34SGuenter Roeck 		break;
614124b7e34SGuenter Roeck 	default:
615124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
616124b7e34SGuenter Roeck 	}
617124b7e34SGuenter Roeck 	return regmap_write(data->regmap, reg, TEMP_TO_REG(val));
618124b7e34SGuenter Roeck }
619124b7e34SGuenter Roeck 
adm9240_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)620124b7e34SGuenter Roeck static int adm9240_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
621124b7e34SGuenter Roeck 			int channel, long *val)
622124b7e34SGuenter Roeck {
623124b7e34SGuenter Roeck 	switch (type) {
624124b7e34SGuenter Roeck 	case hwmon_chip:
625124b7e34SGuenter Roeck 		return adm9240_chip_read(dev, attr, val);
626124b7e34SGuenter Roeck 	case hwmon_intrusion:
627124b7e34SGuenter Roeck 		return adm9240_intrusion_read(dev, attr, val);
628124b7e34SGuenter Roeck 	case hwmon_in:
629124b7e34SGuenter Roeck 		return adm9240_in_read(dev, attr, channel, val);
630124b7e34SGuenter Roeck 	case hwmon_fan:
631124b7e34SGuenter Roeck 		return adm9240_fan_read(dev, attr, channel, val);
632124b7e34SGuenter Roeck 	case hwmon_temp:
633124b7e34SGuenter Roeck 		return adm9240_temp_read(dev, attr, channel, val);
634124b7e34SGuenter Roeck 	default:
635124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
636124b7e34SGuenter Roeck 	}
637124b7e34SGuenter Roeck }
638124b7e34SGuenter Roeck 
adm9240_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)639124b7e34SGuenter Roeck static int adm9240_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
640124b7e34SGuenter Roeck 			 int channel, long val)
641124b7e34SGuenter Roeck {
642124b7e34SGuenter Roeck 	switch (type) {
643124b7e34SGuenter Roeck 	case hwmon_intrusion:
644124b7e34SGuenter Roeck 		return adm9240_intrusion_write(dev, attr, val);
645124b7e34SGuenter Roeck 	case hwmon_in:
646124b7e34SGuenter Roeck 		return adm9240_in_write(dev, attr, channel, val);
647124b7e34SGuenter Roeck 	case hwmon_fan:
648124b7e34SGuenter Roeck 		return adm9240_fan_write(dev, attr, channel, val);
649124b7e34SGuenter Roeck 	case hwmon_temp:
650124b7e34SGuenter Roeck 		return adm9240_temp_write(dev, attr, channel, val);
651124b7e34SGuenter Roeck 	default:
652124b7e34SGuenter Roeck 		return -EOPNOTSUPP;
653124b7e34SGuenter Roeck 	}
654124b7e34SGuenter Roeck }
655124b7e34SGuenter Roeck 
adm9240_is_visible(const void * _data,enum hwmon_sensor_types type,u32 attr,int channel)656124b7e34SGuenter Roeck static umode_t adm9240_is_visible(const void *_data, enum hwmon_sensor_types type,
657124b7e34SGuenter Roeck 				  u32 attr, int channel)
658124b7e34SGuenter Roeck {
659124b7e34SGuenter Roeck 	umode_t mode = 0;
660124b7e34SGuenter Roeck 
661124b7e34SGuenter Roeck 	switch (type) {
662124b7e34SGuenter Roeck 	case hwmon_chip:
663124b7e34SGuenter Roeck 		switch (attr) {
664124b7e34SGuenter Roeck 		case hwmon_chip_alarms:
665124b7e34SGuenter Roeck 			mode = 0444;
666124b7e34SGuenter Roeck 			break;
667124b7e34SGuenter Roeck 		default:
668124b7e34SGuenter Roeck 			break;
669124b7e34SGuenter Roeck 		}
670124b7e34SGuenter Roeck 		break;
671124b7e34SGuenter Roeck 	case hwmon_intrusion:
672124b7e34SGuenter Roeck 		switch (attr) {
673124b7e34SGuenter Roeck 		case hwmon_intrusion_alarm:
674124b7e34SGuenter Roeck 			mode = 0644;
675124b7e34SGuenter Roeck 			break;
676124b7e34SGuenter Roeck 		default:
677124b7e34SGuenter Roeck 			break;
678124b7e34SGuenter Roeck 		}
679124b7e34SGuenter Roeck 		break;
680124b7e34SGuenter Roeck 	case hwmon_temp:
681124b7e34SGuenter Roeck 		switch (attr) {
682124b7e34SGuenter Roeck 		case hwmon_temp:
683124b7e34SGuenter Roeck 		case hwmon_temp_alarm:
684124b7e34SGuenter Roeck 			mode = 0444;
685124b7e34SGuenter Roeck 			break;
686124b7e34SGuenter Roeck 		case hwmon_temp_max:
687124b7e34SGuenter Roeck 		case hwmon_temp_max_hyst:
688124b7e34SGuenter Roeck 			mode = 0644;
689124b7e34SGuenter Roeck 			break;
690124b7e34SGuenter Roeck 		default:
691124b7e34SGuenter Roeck 			break;
692124b7e34SGuenter Roeck 		}
693124b7e34SGuenter Roeck 		break;
694124b7e34SGuenter Roeck 	case hwmon_fan:
695124b7e34SGuenter Roeck 		switch (attr) {
696124b7e34SGuenter Roeck 		case hwmon_fan_input:
697124b7e34SGuenter Roeck 		case hwmon_fan_div:
698124b7e34SGuenter Roeck 		case hwmon_fan_alarm:
699124b7e34SGuenter Roeck 			mode = 0444;
700124b7e34SGuenter Roeck 			break;
701124b7e34SGuenter Roeck 		case hwmon_fan_min:
702124b7e34SGuenter Roeck 			mode = 0644;
703124b7e34SGuenter Roeck 			break;
704124b7e34SGuenter Roeck 		default:
705124b7e34SGuenter Roeck 			break;
706124b7e34SGuenter Roeck 		}
707124b7e34SGuenter Roeck 		break;
708124b7e34SGuenter Roeck 	case hwmon_in:
709124b7e34SGuenter Roeck 		switch (attr) {
710124b7e34SGuenter Roeck 		case hwmon_in_input:
711124b7e34SGuenter Roeck 		case hwmon_in_alarm:
712124b7e34SGuenter Roeck 			mode = 0444;
713124b7e34SGuenter Roeck 			break;
714124b7e34SGuenter Roeck 		case hwmon_in_min:
715124b7e34SGuenter Roeck 		case hwmon_in_max:
716124b7e34SGuenter Roeck 			mode = 0644;
717124b7e34SGuenter Roeck 			break;
718124b7e34SGuenter Roeck 		default:
719124b7e34SGuenter Roeck 			break;
720124b7e34SGuenter Roeck 		}
721124b7e34SGuenter Roeck 		break;
722124b7e34SGuenter Roeck 	default:
723124b7e34SGuenter Roeck 		break;
724124b7e34SGuenter Roeck 	}
725124b7e34SGuenter Roeck 	return mode;
726124b7e34SGuenter Roeck }
727124b7e34SGuenter Roeck 
728124b7e34SGuenter Roeck static const struct hwmon_ops adm9240_hwmon_ops = {
729124b7e34SGuenter Roeck 	.is_visible = adm9240_is_visible,
730124b7e34SGuenter Roeck 	.read = adm9240_read,
731124b7e34SGuenter Roeck 	.write = adm9240_write,
732124b7e34SGuenter Roeck };
733124b7e34SGuenter Roeck 
734013adc98SKrzysztof Kozlowski static const struct hwmon_channel_info * const adm9240_info[] = {
735124b7e34SGuenter Roeck 	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS),
736124b7e34SGuenter Roeck 	HWMON_CHANNEL_INFO(intrusion, HWMON_INTRUSION_ALARM),
737124b7e34SGuenter Roeck 	HWMON_CHANNEL_INFO(temp,
738124b7e34SGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_ALARM),
739124b7e34SGuenter Roeck 	HWMON_CHANNEL_INFO(in,
740124b7e34SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
741124b7e34SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
742124b7e34SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
743124b7e34SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
744124b7e34SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
745124b7e34SGuenter Roeck 			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM),
746124b7e34SGuenter Roeck 	HWMON_CHANNEL_INFO(fan,
747124b7e34SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM,
748124b7e34SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM),
749124b7e34SGuenter Roeck 	NULL
750124b7e34SGuenter Roeck };
751124b7e34SGuenter Roeck 
752124b7e34SGuenter Roeck static const struct hwmon_chip_info adm9240_chip_info = {
753124b7e34SGuenter Roeck 	.ops = &adm9240_hwmon_ops,
754124b7e34SGuenter Roeck 	.info = adm9240_info,
755124b7e34SGuenter Roeck };
756124b7e34SGuenter Roeck 
adm9240_volatile_reg(struct device * dev,unsigned int reg)757124b7e34SGuenter Roeck static bool adm9240_volatile_reg(struct device *dev, unsigned int reg)
758124b7e34SGuenter Roeck {
759124b7e34SGuenter Roeck 	switch (reg) {
760124b7e34SGuenter Roeck 	case ADM9240_REG_IN(0) ... ADM9240_REG_IN(5):
761124b7e34SGuenter Roeck 	case ADM9240_REG_FAN(0) ... ADM9240_REG_FAN(1):
762124b7e34SGuenter Roeck 	case ADM9240_REG_INT(0) ... ADM9240_REG_INT(1):
763124b7e34SGuenter Roeck 	case ADM9240_REG_TEMP:
764124b7e34SGuenter Roeck 	case ADM9240_REG_TEMP_CONF:
765124b7e34SGuenter Roeck 	case ADM9240_REG_VID_FAN_DIV:
766124b7e34SGuenter Roeck 	case ADM9240_REG_VID4:
767124b7e34SGuenter Roeck 	case ADM9240_REG_ANALOG_OUT:
768124b7e34SGuenter Roeck 		return true;
769124b7e34SGuenter Roeck 	default:
770124b7e34SGuenter Roeck 		return false;
771124b7e34SGuenter Roeck 	}
772124b7e34SGuenter Roeck }
773124b7e34SGuenter Roeck 
774df885d91SChris Packham static const struct regmap_config adm9240_regmap_config = {
775df885d91SChris Packham 	.reg_bits = 8,
776df885d91SChris Packham 	.val_bits = 8,
777df885d91SChris Packham 	.use_single_read = true,
778df885d91SChris Packham 	.use_single_write = true,
779124b7e34SGuenter Roeck 	.volatile_reg = adm9240_volatile_reg,
780df885d91SChris Packham };
781df885d91SChris Packham 
adm9240_probe(struct i2c_client * client)782124b7e34SGuenter Roeck static int adm9240_probe(struct i2c_client *client)
7837fae8283SJean Delvare {
784124b7e34SGuenter Roeck 	struct device *dev = &client->dev;
785715f69beSAxel Lin 	struct device *hwmon_dev;
7867fae8283SJean Delvare 	struct adm9240_data *data;
787df885d91SChris Packham 	int err;
7887fae8283SJean Delvare 
789715f69beSAxel Lin 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
790e5ebb772SGuenter Roeck 	if (!data)
791e5ebb772SGuenter Roeck 		return -ENOMEM;
7927fae8283SJean Delvare 
7934f427dcbSGuenter Roeck 	data->dev = dev;
7949a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
795124b7e34SGuenter Roeck 	data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config);
796df885d91SChris Packham 	if (IS_ERR(data->regmap))
797df885d91SChris Packham 		return PTR_ERR(data->regmap);
7988d5d45fbSJean Delvare 
7994f427dcbSGuenter Roeck 	err = adm9240_init_client(data);
800df885d91SChris Packham 	if (err < 0)
801df885d91SChris Packham 		return err;
8028d5d45fbSJean Delvare 
803124b7e34SGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
804124b7e34SGuenter Roeck 							 &adm9240_chip_info,
805715f69beSAxel Lin 							 adm9240_groups);
806715f69beSAxel Lin 	return PTR_ERR_OR_ZERO(hwmon_dev);
8078d5d45fbSJean Delvare }
8088d5d45fbSJean Delvare 
8094341fc3fSAxel Lin static const struct i2c_device_id adm9240_id[] = {
8104341fc3fSAxel Lin 	{ "adm9240", adm9240 },
8114341fc3fSAxel Lin 	{ "ds1780", ds1780 },
8124341fc3fSAxel Lin 	{ "lm81", lm81 },
8134341fc3fSAxel Lin 	{ }
8144341fc3fSAxel Lin };
8154341fc3fSAxel Lin MODULE_DEVICE_TABLE(i2c, adm9240_id);
8168d5d45fbSJean Delvare 
8174341fc3fSAxel Lin static struct i2c_driver adm9240_driver = {
8184341fc3fSAxel Lin 	.class		= I2C_CLASS_HWMON,
8194341fc3fSAxel Lin 	.driver = {
8204341fc3fSAxel Lin 		.name	= "adm9240",
8214341fc3fSAxel Lin 	},
822*1975d167SUwe Kleine-König 	.probe		= adm9240_probe,
8234341fc3fSAxel Lin 	.id_table	= adm9240_id,
8244341fc3fSAxel Lin 	.detect		= adm9240_detect,
8254341fc3fSAxel Lin 	.address_list	= normal_i2c,
8264341fc3fSAxel Lin };
8278d5d45fbSJean Delvare 
828f0967eeaSAxel Lin module_i2c_driver(adm9240_driver);
8298d5d45fbSJean Delvare 
8308d5d45fbSJean Delvare MODULE_AUTHOR("Michiel Rook <michiel@grendelproject.nl>, "
8312ca7b961SGrant Coady 		"Grant Coady <gcoady.lk@gmail.com> and others");
8328d5d45fbSJean Delvare MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver");
8338d5d45fbSJean Delvare MODULE_LICENSE("GPL");
834