xref: /openbmc/linux/drivers/hwmon/adm9240.c (revision e415e48b)
18d5d45fbSJean Delvare /*
28d5d45fbSJean Delvare  * adm9240.c	Part of lm_sensors, Linux kernel modules for hardware
38d5d45fbSJean Delvare  * 		monitoring
48d5d45fbSJean Delvare  *
58d5d45fbSJean Delvare  * Copyright (C) 1999	Frodo Looijaard <frodol@dds.nl>
68d5d45fbSJean Delvare  *			Philip Edelbrock <phil@netroedge.com>
78d5d45fbSJean Delvare  * Copyright (C) 2003	Michiel Rook <michiel@grendelproject.nl>
88d5d45fbSJean Delvare  * Copyright (C) 2005	Grant Coady <gcoady@gmail.com> with valuable
98d5d45fbSJean Delvare  * 				guidance from Jean Delvare
108d5d45fbSJean Delvare  *
118d5d45fbSJean Delvare  * Driver supports	Analog Devices		ADM9240
128d5d45fbSJean Delvare  *			Dallas Semiconductor	DS1780
138d5d45fbSJean Delvare  *			National Semiconductor	LM81
148d5d45fbSJean Delvare  *
158d5d45fbSJean Delvare  * ADM9240 is the reference, DS1780 and LM81 are register compatibles
168d5d45fbSJean Delvare  *
178d5d45fbSJean Delvare  * Voltage	Six inputs are scaled by chip, VID also reported
188d5d45fbSJean Delvare  * Temperature	Chip temperature to 0.5'C, maximum and max_hysteris
198d5d45fbSJean Delvare  * Fans		2 fans, low speed alarm, automatic fan clock divider
208d5d45fbSJean Delvare  * Alarms	16-bit map of active alarms
218d5d45fbSJean Delvare  * Analog Out	0..1250 mV output
228d5d45fbSJean Delvare  *
238d5d45fbSJean Delvare  * Chassis Intrusion: clear CI latch with 'echo 1 > chassis_clear'
248d5d45fbSJean Delvare  *
258d5d45fbSJean Delvare  * Test hardware: Intel SE440BX-2 desktop motherboard --Grant
268d5d45fbSJean Delvare  *
278d5d45fbSJean Delvare  * LM81 extended temp reading not implemented
288d5d45fbSJean Delvare  *
298d5d45fbSJean Delvare  * This program is free software; you can redistribute it and/or modify
308d5d45fbSJean Delvare  * it under the terms of the GNU General Public License as published by
318d5d45fbSJean Delvare  * the Free Software Foundation; either version 2 of the License, or
328d5d45fbSJean Delvare  * (at your option) any later version.
338d5d45fbSJean Delvare  *
348d5d45fbSJean Delvare  * This program is distributed in the hope that it will be useful,
358d5d45fbSJean Delvare  * but WITHOUT ANY WARRANTY; without even the implied warranty of
368d5d45fbSJean Delvare  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
378d5d45fbSJean Delvare  * GNU General Public License for more details.
388d5d45fbSJean Delvare  *
398d5d45fbSJean Delvare  * You should have received a copy of the GNU General Public License
408d5d45fbSJean Delvare  * along with this program; if not, write to the Free Software
418d5d45fbSJean Delvare  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
428d5d45fbSJean Delvare  */
438d5d45fbSJean Delvare 
448d5d45fbSJean Delvare #include <linux/init.h>
458d5d45fbSJean Delvare #include <linux/module.h>
468d5d45fbSJean Delvare #include <linux/slab.h>
478d5d45fbSJean Delvare #include <linux/i2c.h>
48c7461a66SGrant Coady #include <linux/hwmon-sysfs.h>
49943b0830SMark M. Hoffman #include <linux/hwmon.h>
50303760b4SJean Delvare #include <linux/hwmon-vid.h>
51943b0830SMark M. Hoffman #include <linux/err.h>
528d5d45fbSJean Delvare 
538d5d45fbSJean Delvare /* Addresses to scan */
548d5d45fbSJean Delvare static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
558d5d45fbSJean Delvare 					I2C_CLIENT_END };
568d5d45fbSJean Delvare 
578d5d45fbSJean Delvare /* Insmod parameters */
58f4b50261SJean Delvare I2C_CLIENT_INSMOD_3(adm9240, ds1780, lm81);
598d5d45fbSJean Delvare 
608d5d45fbSJean Delvare /* ADM9240 registers */
618d5d45fbSJean Delvare #define ADM9240_REG_MAN_ID		0x3e
628d5d45fbSJean Delvare #define ADM9240_REG_DIE_REV		0x3f
638d5d45fbSJean Delvare #define ADM9240_REG_CONFIG		0x40
648d5d45fbSJean Delvare 
658d5d45fbSJean Delvare #define ADM9240_REG_IN(nr)		(0x20 + (nr))   /* 0..5 */
668d5d45fbSJean Delvare #define ADM9240_REG_IN_MAX(nr)		(0x2b + (nr) * 2)
678d5d45fbSJean Delvare #define ADM9240_REG_IN_MIN(nr)		(0x2c + (nr) * 2)
688d5d45fbSJean Delvare #define ADM9240_REG_FAN(nr)		(0x28 + (nr))   /* 0..1 */
698d5d45fbSJean Delvare #define ADM9240_REG_FAN_MIN(nr)		(0x3b + (nr))
708d5d45fbSJean Delvare #define ADM9240_REG_INT(nr)		(0x41 + (nr))
718d5d45fbSJean Delvare #define ADM9240_REG_INT_MASK(nr)	(0x43 + (nr))
728d5d45fbSJean Delvare #define ADM9240_REG_TEMP		0x27
73c7461a66SGrant Coady #define ADM9240_REG_TEMP_MAX(nr)	(0x39 + (nr)) /* 0, 1 = high, hyst */
748d5d45fbSJean Delvare #define ADM9240_REG_ANALOG_OUT		0x19
758d5d45fbSJean Delvare #define ADM9240_REG_CHASSIS_CLEAR	0x46
768d5d45fbSJean Delvare #define ADM9240_REG_VID_FAN_DIV		0x47
778d5d45fbSJean Delvare #define ADM9240_REG_I2C_ADDR		0x48
788d5d45fbSJean Delvare #define ADM9240_REG_VID4		0x49
798d5d45fbSJean Delvare #define ADM9240_REG_TEMP_CONF		0x4b
808d5d45fbSJean Delvare 
818d5d45fbSJean Delvare /* generalised scaling with integer rounding */
828d5d45fbSJean Delvare static inline int SCALE(long val, int mul, int div)
838d5d45fbSJean Delvare {
848d5d45fbSJean Delvare 	if (val < 0)
858d5d45fbSJean Delvare 		return (val * mul - div / 2) / div;
868d5d45fbSJean Delvare 	else
878d5d45fbSJean Delvare 		return (val * mul + div / 2) / div;
888d5d45fbSJean Delvare }
898d5d45fbSJean Delvare 
908d5d45fbSJean Delvare /* adm9240 internally scales voltage measurements */
918d5d45fbSJean Delvare static const u16 nom_mv[] = { 2500, 2700, 3300, 5000, 12000, 2700 };
928d5d45fbSJean Delvare 
938d5d45fbSJean Delvare static inline unsigned int IN_FROM_REG(u8 reg, int n)
948d5d45fbSJean Delvare {
958d5d45fbSJean Delvare 	return SCALE(reg, nom_mv[n], 192);
968d5d45fbSJean Delvare }
978d5d45fbSJean Delvare 
988d5d45fbSJean Delvare static inline u8 IN_TO_REG(unsigned long val, int n)
998d5d45fbSJean Delvare {
1008d5d45fbSJean Delvare 	return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255);
1018d5d45fbSJean Delvare }
1028d5d45fbSJean Delvare 
1038d5d45fbSJean Delvare /* temperature range: -40..125, 127 disables temperature alarm */
1048d5d45fbSJean Delvare static inline s8 TEMP_TO_REG(long val)
1058d5d45fbSJean Delvare {
1068d5d45fbSJean Delvare 	return SENSORS_LIMIT(SCALE(val, 1, 1000), -40, 127);
1078d5d45fbSJean Delvare }
1088d5d45fbSJean Delvare 
1098d5d45fbSJean Delvare /* two fans, each with low fan speed limit */
1108d5d45fbSJean Delvare static inline unsigned int FAN_FROM_REG(u8 reg, u8 div)
1118d5d45fbSJean Delvare {
1128d5d45fbSJean Delvare 	if (!reg) /* error */
1138d5d45fbSJean Delvare 		return -1;
1148d5d45fbSJean Delvare 
1158d5d45fbSJean Delvare 	if (reg == 255)
1168d5d45fbSJean Delvare 		return 0;
1178d5d45fbSJean Delvare 
1188d5d45fbSJean Delvare 	return SCALE(1350000, 1, reg * div);
1198d5d45fbSJean Delvare }
1208d5d45fbSJean Delvare 
1218d5d45fbSJean Delvare /* analog out 0..1250mV */
1228d5d45fbSJean Delvare static inline u8 AOUT_TO_REG(unsigned long val)
1238d5d45fbSJean Delvare {
1248d5d45fbSJean Delvare 	return SENSORS_LIMIT(SCALE(val, 255, 1250), 0, 255);
1258d5d45fbSJean Delvare }
1268d5d45fbSJean Delvare 
1278d5d45fbSJean Delvare static inline unsigned int AOUT_FROM_REG(u8 reg)
1288d5d45fbSJean Delvare {
1298d5d45fbSJean Delvare 	return SCALE(reg, 1250, 255);
1308d5d45fbSJean Delvare }
1318d5d45fbSJean Delvare 
1328d5d45fbSJean Delvare static int adm9240_attach_adapter(struct i2c_adapter *adapter);
1338d5d45fbSJean Delvare static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind);
1348d5d45fbSJean Delvare static void adm9240_init_client(struct i2c_client *client);
1358d5d45fbSJean Delvare static int adm9240_detach_client(struct i2c_client *client);
1368d5d45fbSJean Delvare static struct adm9240_data *adm9240_update_device(struct device *dev);
1378d5d45fbSJean Delvare 
1388d5d45fbSJean Delvare /* driver data */
1398d5d45fbSJean Delvare static struct i2c_driver adm9240_driver = {
1408d5d45fbSJean Delvare 	.owner		= THIS_MODULE,
1418d5d45fbSJean Delvare 	.name		= "adm9240",
1428d5d45fbSJean Delvare 	.id		= I2C_DRIVERID_ADM9240,
1438d5d45fbSJean Delvare 	.flags		= I2C_DF_NOTIFY,
1448d5d45fbSJean Delvare 	.attach_adapter	= adm9240_attach_adapter,
1458d5d45fbSJean Delvare 	.detach_client	= adm9240_detach_client,
1468d5d45fbSJean Delvare };
1478d5d45fbSJean Delvare 
1488d5d45fbSJean Delvare /* per client data */
1498d5d45fbSJean Delvare struct adm9240_data {
1508d5d45fbSJean Delvare 	enum chips type;
1518d5d45fbSJean Delvare 	struct i2c_client client;
152943b0830SMark M. Hoffman 	struct class_device *class_dev;
1538d5d45fbSJean Delvare 	struct semaphore update_lock;
1548d5d45fbSJean Delvare 	char valid;
1558d5d45fbSJean Delvare 	unsigned long last_updated_measure;
1568d5d45fbSJean Delvare 	unsigned long last_updated_config;
1578d5d45fbSJean Delvare 
1588d5d45fbSJean Delvare 	u8 in[6];		/* ro	in0_input */
1598d5d45fbSJean Delvare 	u8 in_max[6];		/* rw	in0_max */
1608d5d45fbSJean Delvare 	u8 in_min[6];		/* rw	in0_min */
1618d5d45fbSJean Delvare 	u8 fan[2];		/* ro	fan1_input */
1628d5d45fbSJean Delvare 	u8 fan_min[2];		/* rw	fan1_min */
1638d5d45fbSJean Delvare 	u8 fan_div[2];		/* rw	fan1_div, read-only accessor */
1648d5d45fbSJean Delvare 	s16 temp;		/* ro	temp1_input, 9-bit sign-extended */
165c7461a66SGrant Coady 	s8 temp_max[2];		/* rw	0 -> temp_max, 1 -> temp_max_hyst */
1668d5d45fbSJean Delvare 	u16 alarms;		/* ro	alarms */
1678d5d45fbSJean Delvare 	u8 aout;		/* rw	aout_output */
1688d5d45fbSJean Delvare 	u8 vid;			/* ro	vid */
1698d5d45fbSJean Delvare 	u8 vrm;			/* --	vrm set on startup, no accessor */
1708d5d45fbSJean Delvare };
1718d5d45fbSJean Delvare 
1728d5d45fbSJean Delvare /*** sysfs accessors ***/
1738d5d45fbSJean Delvare 
1748d5d45fbSJean Delvare /* temperature */
175c7461a66SGrant Coady static ssize_t show_temp(struct device *dev, struct device_attribute *dummy,
176c7461a66SGrant Coady 		char *buf)
177c7461a66SGrant Coady {
178c7461a66SGrant Coady 	struct adm9240_data *data = adm9240_update_device(dev);
179c7461a66SGrant Coady 	return sprintf(buf, "%d\n", data->temp * 500); /* 9-bit value */
1808d5d45fbSJean Delvare }
1818d5d45fbSJean Delvare 
182c7461a66SGrant Coady static ssize_t show_max(struct device *dev, struct device_attribute *devattr,
183c7461a66SGrant Coady 		char *buf)
184c7461a66SGrant Coady {
185c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
186c7461a66SGrant Coady 	struct adm9240_data *data = adm9240_update_device(dev);
187c7461a66SGrant Coady 	return sprintf(buf, "%d\n", data->temp_max[attr->index] * 1000);
188c7461a66SGrant Coady }
1898d5d45fbSJean Delvare 
190c7461a66SGrant Coady static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
191c7461a66SGrant Coady 		const char *buf, size_t count)
192c7461a66SGrant Coady {
193c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
194c7461a66SGrant Coady 	struct i2c_client *client = to_i2c_client(dev);
195c7461a66SGrant Coady 	struct adm9240_data *data = i2c_get_clientdata(client);
196c7461a66SGrant Coady 	long val = simple_strtol(buf, NULL, 10);
197c7461a66SGrant Coady 
198c7461a66SGrant Coady 	down(&data->update_lock);
199c7461a66SGrant Coady 	data->temp_max[attr->index] = TEMP_TO_REG(val);
200c7461a66SGrant Coady 	i2c_smbus_write_byte_data(client, ADM9240_REG_TEMP_MAX(attr->index),
201c7461a66SGrant Coady 			data->temp_max[attr->index]);
202c7461a66SGrant Coady 	up(&data->update_lock);
203c7461a66SGrant Coady 	return count;
204c7461a66SGrant Coady }
205c7461a66SGrant Coady 
2068d5d45fbSJean Delvare static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
207c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
208c7461a66SGrant Coady 		show_max, set_max, 0);
209c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
210c7461a66SGrant Coady 		show_max, set_max, 1);
2118d5d45fbSJean Delvare 
2128d5d45fbSJean Delvare /* voltage */
213c7461a66SGrant Coady static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
214c7461a66SGrant Coady 		char *buf)
2158d5d45fbSJean Delvare {
216c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2178d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
218c7461a66SGrant Coady 	return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index],
219c7461a66SGrant Coady 				attr->index));
2208d5d45fbSJean Delvare }
2218d5d45fbSJean Delvare 
222e415e48bSJean Delvare static ssize_t show_in_min(struct device *dev,
223e415e48bSJean Delvare 		struct device_attribute *devattr, char *buf)
2248d5d45fbSJean Delvare {
225c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2268d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
227c7461a66SGrant Coady 	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index],
228c7461a66SGrant Coady 				attr->index));
2298d5d45fbSJean Delvare }
2308d5d45fbSJean Delvare 
231e415e48bSJean Delvare static ssize_t show_in_max(struct device *dev,
232e415e48bSJean Delvare 		struct device_attribute *devattr, char *buf)
2338d5d45fbSJean Delvare {
234c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2358d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
236c7461a66SGrant Coady 	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index],
237c7461a66SGrant Coady 				attr->index));
2388d5d45fbSJean Delvare }
2398d5d45fbSJean Delvare 
240e415e48bSJean Delvare static ssize_t set_in_min(struct device *dev,
241e415e48bSJean Delvare 		struct device_attribute *devattr,
242c7461a66SGrant Coady 		const char *buf, size_t count)
2438d5d45fbSJean Delvare {
244c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2458d5d45fbSJean Delvare 	struct i2c_client *client = to_i2c_client(dev);
2468d5d45fbSJean Delvare 	struct adm9240_data *data = i2c_get_clientdata(client);
2478d5d45fbSJean Delvare 	unsigned long val = simple_strtoul(buf, NULL, 10);
2488d5d45fbSJean Delvare 
2498d5d45fbSJean Delvare 	down(&data->update_lock);
250c7461a66SGrant Coady 	data->in_min[attr->index] = IN_TO_REG(val, attr->index);
251c7461a66SGrant Coady 	i2c_smbus_write_byte_data(client, ADM9240_REG_IN_MIN(attr->index),
252c7461a66SGrant Coady 			data->in_min[attr->index]);
2538d5d45fbSJean Delvare 	up(&data->update_lock);
2548d5d45fbSJean Delvare 	return count;
2558d5d45fbSJean Delvare }
2568d5d45fbSJean Delvare 
257e415e48bSJean Delvare static ssize_t set_in_max(struct device *dev,
258e415e48bSJean Delvare 		struct device_attribute *devattr,
259c7461a66SGrant Coady 		const char *buf, size_t count)
2608d5d45fbSJean Delvare {
261c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2628d5d45fbSJean Delvare 	struct i2c_client *client = to_i2c_client(dev);
2638d5d45fbSJean Delvare 	struct adm9240_data *data = i2c_get_clientdata(client);
2648d5d45fbSJean Delvare 	unsigned long val = simple_strtoul(buf, NULL, 10);
2658d5d45fbSJean Delvare 
2668d5d45fbSJean Delvare 	down(&data->update_lock);
267c7461a66SGrant Coady 	data->in_max[attr->index] = IN_TO_REG(val, attr->index);
268c7461a66SGrant Coady 	i2c_smbus_write_byte_data(client, ADM9240_REG_IN_MAX(attr->index),
269c7461a66SGrant Coady 			data->in_max[attr->index]);
2708d5d45fbSJean Delvare 	up(&data->update_lock);
2718d5d45fbSJean Delvare 	return count;
2728d5d45fbSJean Delvare }
2738d5d45fbSJean Delvare 
274c7461a66SGrant Coady #define vin(nr)							\
275c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(in##nr##_input, S_IRUGO, 		\
276c7461a66SGrant Coady 		show_in, NULL, nr);				\
277c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(in##nr##_min, S_IRUGO | S_IWUSR,	\
278c7461a66SGrant Coady 		show_in_min, set_in_min, nr);			\
279c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(in##nr##_max, S_IRUGO | S_IWUSR,	\
280c7461a66SGrant Coady 		show_in_max, set_in_max, nr);
2818d5d45fbSJean Delvare 
282c7461a66SGrant Coady vin(0);
283c7461a66SGrant Coady vin(1);
284c7461a66SGrant Coady vin(2);
285c7461a66SGrant Coady vin(3);
286c7461a66SGrant Coady vin(4);
287c7461a66SGrant Coady vin(5);
2888d5d45fbSJean Delvare 
2898d5d45fbSJean Delvare /* fans */
290c7461a66SGrant Coady static ssize_t show_fan(struct device *dev,
291c7461a66SGrant Coady 		struct device_attribute *devattr, char *buf)
2928d5d45fbSJean Delvare {
293c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2948d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
295c7461a66SGrant Coady 	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index],
296c7461a66SGrant Coady 				1 << data->fan_div[attr->index]));
2978d5d45fbSJean Delvare }
2988d5d45fbSJean Delvare 
299c7461a66SGrant Coady static ssize_t show_fan_min(struct device *dev,
300c7461a66SGrant Coady 		struct device_attribute *devattr, char *buf)
3018d5d45fbSJean Delvare {
302c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
3038d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
304c7461a66SGrant Coady 	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[attr->index],
305c7461a66SGrant Coady 				1 << data->fan_div[attr->index]));
3068d5d45fbSJean Delvare }
3078d5d45fbSJean Delvare 
308c7461a66SGrant Coady static ssize_t show_fan_div(struct device *dev,
309c7461a66SGrant Coady 		struct device_attribute *devattr, char *buf)
3108d5d45fbSJean Delvare {
311c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
3128d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
313c7461a66SGrant Coady 	return sprintf(buf, "%d\n", 1 << data->fan_div[attr->index]);
3148d5d45fbSJean Delvare }
3158d5d45fbSJean Delvare 
3168d5d45fbSJean Delvare /* write new fan div, callers must hold data->update_lock */
3178d5d45fbSJean Delvare static void adm9240_write_fan_div(struct i2c_client *client, int nr,
3188d5d45fbSJean Delvare 		u8 fan_div)
3198d5d45fbSJean Delvare {
3208d5d45fbSJean Delvare 	u8 reg, old, shift = (nr + 2) * 2;
3218d5d45fbSJean Delvare 
322205cf13eSGrant Coady 	reg = i2c_smbus_read_byte_data(client, ADM9240_REG_VID_FAN_DIV);
3238d5d45fbSJean Delvare 	old = (reg >> shift) & 3;
3248d5d45fbSJean Delvare 	reg &= ~(3 << shift);
3258d5d45fbSJean Delvare 	reg |= (fan_div << shift);
326205cf13eSGrant Coady 	i2c_smbus_write_byte_data(client, ADM9240_REG_VID_FAN_DIV, reg);
3278d5d45fbSJean Delvare 	dev_dbg(&client->dev, "fan%d clock divider changed from %u "
3288d5d45fbSJean Delvare 			"to %u\n", nr + 1, 1 << old, 1 << fan_div);
3298d5d45fbSJean Delvare }
3308d5d45fbSJean Delvare 
3318d5d45fbSJean Delvare /*
3328d5d45fbSJean Delvare  * set fan speed low limit:
3338d5d45fbSJean Delvare  *
3348d5d45fbSJean Delvare  * - value is zero: disable fan speed low limit alarm
3358d5d45fbSJean Delvare  *
3368d5d45fbSJean Delvare  * - value is below fan speed measurement range: enable fan speed low
3378d5d45fbSJean Delvare  *   limit alarm to be asserted while fan speed too slow to measure
3388d5d45fbSJean Delvare  *
3398d5d45fbSJean Delvare  * - otherwise: select fan clock divider to suit fan speed low limit,
3408d5d45fbSJean Delvare  *   measurement code may adjust registers to ensure fan speed reading
3418d5d45fbSJean Delvare  */
342c7461a66SGrant Coady static ssize_t set_fan_min(struct device *dev,
343c7461a66SGrant Coady 		struct device_attribute *devattr,
344c7461a66SGrant Coady 		const char *buf, size_t count)
3458d5d45fbSJean Delvare {
346c7461a66SGrant Coady 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
3478d5d45fbSJean Delvare 	struct i2c_client *client = to_i2c_client(dev);
3488d5d45fbSJean Delvare 	struct adm9240_data *data = i2c_get_clientdata(client);
3498d5d45fbSJean Delvare 	unsigned long val = simple_strtoul(buf, NULL, 10);
350c7461a66SGrant Coady 	int nr = attr->index;
3518d5d45fbSJean Delvare 	u8 new_div;
3528d5d45fbSJean Delvare 
3538d5d45fbSJean Delvare 	down(&data->update_lock);
3548d5d45fbSJean Delvare 
3558d5d45fbSJean Delvare 	if (!val) {
3568d5d45fbSJean Delvare 		data->fan_min[nr] = 255;
3578d5d45fbSJean Delvare 		new_div = data->fan_div[nr];
3588d5d45fbSJean Delvare 
3598d5d45fbSJean Delvare 		dev_dbg(&client->dev, "fan%u low limit set disabled\n",
3608d5d45fbSJean Delvare 				nr + 1);
3618d5d45fbSJean Delvare 
3628d5d45fbSJean Delvare 	} else if (val < 1350000 / (8 * 254)) {
3638d5d45fbSJean Delvare 		new_div = 3;
3648d5d45fbSJean Delvare 		data->fan_min[nr] = 254;
3658d5d45fbSJean Delvare 
3668d5d45fbSJean Delvare 		dev_dbg(&client->dev, "fan%u low limit set minimum %u\n",
3678d5d45fbSJean Delvare 				nr + 1, FAN_FROM_REG(254, 1 << new_div));
3688d5d45fbSJean Delvare 
3698d5d45fbSJean Delvare 	} else {
3708d5d45fbSJean Delvare 		unsigned int new_min = 1350000 / val;
3718d5d45fbSJean Delvare 
3728d5d45fbSJean Delvare 		new_div = 0;
3738d5d45fbSJean Delvare 		while (new_min > 192 && new_div < 3) {
3748d5d45fbSJean Delvare 			new_div++;
3758d5d45fbSJean Delvare 			new_min /= 2;
3768d5d45fbSJean Delvare 		}
3778d5d45fbSJean Delvare 		if (!new_min) /* keep > 0 */
3788d5d45fbSJean Delvare 			new_min++;
3798d5d45fbSJean Delvare 
3808d5d45fbSJean Delvare 		data->fan_min[nr] = new_min;
3818d5d45fbSJean Delvare 
3828d5d45fbSJean Delvare 		dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n",
3838d5d45fbSJean Delvare 				nr + 1, FAN_FROM_REG(new_min, 1 << new_div));
3848d5d45fbSJean Delvare 	}
3858d5d45fbSJean Delvare 
3868d5d45fbSJean Delvare 	if (new_div != data->fan_div[nr]) {
3878d5d45fbSJean Delvare 		data->fan_div[nr] = new_div;
3888d5d45fbSJean Delvare 		adm9240_write_fan_div(client, nr, new_div);
3898d5d45fbSJean Delvare 	}
390205cf13eSGrant Coady 	i2c_smbus_write_byte_data(client, ADM9240_REG_FAN_MIN(nr),
3918d5d45fbSJean Delvare 			data->fan_min[nr]);
3928d5d45fbSJean Delvare 
3938d5d45fbSJean Delvare 	up(&data->update_lock);
3948d5d45fbSJean Delvare 	return count;
3958d5d45fbSJean Delvare }
3968d5d45fbSJean Delvare 
397c7461a66SGrant Coady #define fan(nr)							\
398c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(fan##nr##_input, S_IRUGO,		\
399c7461a66SGrant Coady 		show_fan, NULL, nr - 1);			\
400c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(fan##nr##_div, S_IRUGO,		\
401c7461a66SGrant Coady 		show_fan_div, NULL, nr - 1);			\
402c7461a66SGrant Coady static SENSOR_DEVICE_ATTR(fan##nr##_min, S_IRUGO | S_IWUSR,	\
403c7461a66SGrant Coady 		show_fan_min, set_fan_min, nr - 1);
4048d5d45fbSJean Delvare 
405c7461a66SGrant Coady fan(1);
406c7461a66SGrant Coady fan(2);
4078d5d45fbSJean Delvare 
4088d5d45fbSJean Delvare /* alarms */
409e415e48bSJean Delvare static ssize_t show_alarms(struct device *dev,
410e415e48bSJean Delvare 		struct device_attribute *attr, char *buf)
4118d5d45fbSJean Delvare {
4128d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
4138d5d45fbSJean Delvare 	return sprintf(buf, "%u\n", data->alarms);
4148d5d45fbSJean Delvare }
4158d5d45fbSJean Delvare static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
4168d5d45fbSJean Delvare 
4178d5d45fbSJean Delvare /* vid */
418e415e48bSJean Delvare static ssize_t show_vid(struct device *dev,
419e415e48bSJean Delvare 		struct device_attribute *attr, char *buf)
4208d5d45fbSJean Delvare {
4218d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
4228d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
4238d5d45fbSJean Delvare }
4248d5d45fbSJean Delvare static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
4258d5d45fbSJean Delvare 
4268d5d45fbSJean Delvare /* analog output */
427e415e48bSJean Delvare static ssize_t show_aout(struct device *dev,
428e415e48bSJean Delvare 		struct device_attribute *attr, char *buf)
4298d5d45fbSJean Delvare {
4308d5d45fbSJean Delvare 	struct adm9240_data *data = adm9240_update_device(dev);
4318d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
4328d5d45fbSJean Delvare }
4338d5d45fbSJean Delvare 
434e415e48bSJean Delvare static ssize_t set_aout(struct device *dev,
435e415e48bSJean Delvare 		struct device_attribute *attr,
436e415e48bSJean Delvare 		const char *buf, size_t count)
4378d5d45fbSJean Delvare {
4388d5d45fbSJean Delvare 	struct i2c_client *client = to_i2c_client(dev);
4398d5d45fbSJean Delvare 	struct adm9240_data *data = i2c_get_clientdata(client);
4408d5d45fbSJean Delvare 	unsigned long val = simple_strtol(buf, NULL, 10);
4418d5d45fbSJean Delvare 
4428d5d45fbSJean Delvare 	down(&data->update_lock);
4438d5d45fbSJean Delvare 	data->aout = AOUT_TO_REG(val);
444205cf13eSGrant Coady 	i2c_smbus_write_byte_data(client, ADM9240_REG_ANALOG_OUT, data->aout);
4458d5d45fbSJean Delvare 	up(&data->update_lock);
4468d5d45fbSJean Delvare 	return count;
4478d5d45fbSJean Delvare }
4488d5d45fbSJean Delvare static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
4498d5d45fbSJean Delvare 
4508d5d45fbSJean Delvare /* chassis_clear */
451e415e48bSJean Delvare static ssize_t chassis_clear(struct device *dev,
452e415e48bSJean Delvare 		struct device_attribute *attr,
453e415e48bSJean Delvare 		const char *buf, size_t count)
4548d5d45fbSJean Delvare {
4558d5d45fbSJean Delvare 	struct i2c_client *client = to_i2c_client(dev);
4568d5d45fbSJean Delvare 	unsigned long val = simple_strtol(buf, NULL, 10);
4578d5d45fbSJean Delvare 
4588d5d45fbSJean Delvare 	if (val == 1) {
459205cf13eSGrant Coady 		i2c_smbus_write_byte_data(client,
460205cf13eSGrant Coady 				ADM9240_REG_CHASSIS_CLEAR, 0x80);
4618d5d45fbSJean Delvare 		dev_dbg(&client->dev, "chassis intrusion latch cleared\n");
4628d5d45fbSJean Delvare 	}
4638d5d45fbSJean Delvare 	return count;
4648d5d45fbSJean Delvare }
4658d5d45fbSJean Delvare static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear);
4668d5d45fbSJean Delvare 
4678d5d45fbSJean Delvare 
4688d5d45fbSJean Delvare /*** sensor chip detect and driver install ***/
4698d5d45fbSJean Delvare 
4708d5d45fbSJean Delvare static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind)
4718d5d45fbSJean Delvare {
4728d5d45fbSJean Delvare 	struct i2c_client *new_client;
4738d5d45fbSJean Delvare 	struct adm9240_data *data;
4748d5d45fbSJean Delvare 	int err = 0;
4758d5d45fbSJean Delvare 	const char *name = "";
4768d5d45fbSJean Delvare 	u8 man_id, die_rev;
4778d5d45fbSJean Delvare 
4788d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
4798d5d45fbSJean Delvare 		goto exit;
4808d5d45fbSJean Delvare 
481205cf13eSGrant Coady 	if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) {
4828d5d45fbSJean Delvare 		err = -ENOMEM;
4838d5d45fbSJean Delvare 		goto exit;
4848d5d45fbSJean Delvare 	}
4858d5d45fbSJean Delvare 
4868d5d45fbSJean Delvare 	new_client = &data->client;
4878d5d45fbSJean Delvare 	i2c_set_clientdata(new_client, data);
4888d5d45fbSJean Delvare 	new_client->addr = address;
4898d5d45fbSJean Delvare 	new_client->adapter = adapter;
4908d5d45fbSJean Delvare 	new_client->driver = &adm9240_driver;
4918d5d45fbSJean Delvare 	new_client->flags = 0;
4928d5d45fbSJean Delvare 
4938d5d45fbSJean Delvare 	if (kind == 0) {
4948d5d45fbSJean Delvare 		kind = adm9240;
4958d5d45fbSJean Delvare 	}
4968d5d45fbSJean Delvare 
4978d5d45fbSJean Delvare 	if (kind < 0) {
4988d5d45fbSJean Delvare 
4998d5d45fbSJean Delvare 		/* verify chip: reg address should match i2c address */
500205cf13eSGrant Coady 		if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR)
5018d5d45fbSJean Delvare 				!= address) {
5028d5d45fbSJean Delvare 			dev_err(&adapter->dev, "detect fail: address match, "
5038d5d45fbSJean Delvare 					"0x%02x\n", address);
5048d5d45fbSJean Delvare 			goto exit_free;
5058d5d45fbSJean Delvare 		}
5068d5d45fbSJean Delvare 
5078d5d45fbSJean Delvare 		/* check known chip manufacturer */
508205cf13eSGrant Coady 		man_id = i2c_smbus_read_byte_data(new_client,
509205cf13eSGrant Coady 				ADM9240_REG_MAN_ID);
5108d5d45fbSJean Delvare 		if (man_id == 0x23) {
5118d5d45fbSJean Delvare 			kind = adm9240;
5128d5d45fbSJean Delvare 		} else if (man_id == 0xda) {
5138d5d45fbSJean Delvare 			kind = ds1780;
5148d5d45fbSJean Delvare 		} else if (man_id == 0x01) {
5158d5d45fbSJean Delvare 			kind = lm81;
5168d5d45fbSJean Delvare 		} else {
5178d5d45fbSJean Delvare 			dev_err(&adapter->dev, "detect fail: unknown manuf, "
5188d5d45fbSJean Delvare 					"0x%02x\n", man_id);
5198d5d45fbSJean Delvare 			goto exit_free;
5208d5d45fbSJean Delvare 		}
5218d5d45fbSJean Delvare 
5228d5d45fbSJean Delvare 		/* successful detect, print chip info */
523205cf13eSGrant Coady 		die_rev = i2c_smbus_read_byte_data(new_client,
524205cf13eSGrant Coady 				ADM9240_REG_DIE_REV);
5258d5d45fbSJean Delvare 		dev_info(&adapter->dev, "found %s revision %u\n",
5268d5d45fbSJean Delvare 				man_id == 0x23 ? "ADM9240" :
5278d5d45fbSJean Delvare 				man_id == 0xda ? "DS1780" : "LM81", die_rev);
5288d5d45fbSJean Delvare 	}
5298d5d45fbSJean Delvare 
5308d5d45fbSJean Delvare 	/* either forced or detected chip kind */
5318d5d45fbSJean Delvare 	if (kind == adm9240) {
5328d5d45fbSJean Delvare 		name = "adm9240";
5338d5d45fbSJean Delvare 	} else if (kind == ds1780) {
5348d5d45fbSJean Delvare 		name = "ds1780";
5358d5d45fbSJean Delvare 	} else if (kind == lm81) {
5368d5d45fbSJean Delvare 		name = "lm81";
5378d5d45fbSJean Delvare 	}
5388d5d45fbSJean Delvare 
5398d5d45fbSJean Delvare 	/* fill in the remaining client fields and attach */
5408d5d45fbSJean Delvare 	strlcpy(new_client->name, name, I2C_NAME_SIZE);
5418d5d45fbSJean Delvare 	data->type = kind;
5428d5d45fbSJean Delvare 	init_MUTEX(&data->update_lock);
5438d5d45fbSJean Delvare 
5448d5d45fbSJean Delvare 	if ((err = i2c_attach_client(new_client)))
5458d5d45fbSJean Delvare 		goto exit_free;
5468d5d45fbSJean Delvare 
5478d5d45fbSJean Delvare 	adm9240_init_client(new_client);
5488d5d45fbSJean Delvare 
5498d5d45fbSJean Delvare 	/* populate sysfs filesystem */
550943b0830SMark M. Hoffman 	data->class_dev = hwmon_device_register(&new_client->dev);
551943b0830SMark M. Hoffman 	if (IS_ERR(data->class_dev)) {
552943b0830SMark M. Hoffman 		err = PTR_ERR(data->class_dev);
553943b0830SMark M. Hoffman 		goto exit_detach;
554943b0830SMark M. Hoffman 	}
555943b0830SMark M. Hoffman 
556c7461a66SGrant Coady 	device_create_file(&new_client->dev,
557c7461a66SGrant Coady 			&sensor_dev_attr_in0_input.dev_attr);
558c7461a66SGrant Coady 	device_create_file(&new_client->dev,
559c7461a66SGrant Coady 			&sensor_dev_attr_in0_min.dev_attr);
560c7461a66SGrant Coady 	device_create_file(&new_client->dev,
561c7461a66SGrant Coady 			&sensor_dev_attr_in0_max.dev_attr);
562c7461a66SGrant Coady 	device_create_file(&new_client->dev,
563c7461a66SGrant Coady 			&sensor_dev_attr_in1_input.dev_attr);
564c7461a66SGrant Coady 	device_create_file(&new_client->dev,
565c7461a66SGrant Coady 			&sensor_dev_attr_in1_min.dev_attr);
566c7461a66SGrant Coady 	device_create_file(&new_client->dev,
567c7461a66SGrant Coady 			&sensor_dev_attr_in1_max.dev_attr);
568c7461a66SGrant Coady 	device_create_file(&new_client->dev,
569c7461a66SGrant Coady 			&sensor_dev_attr_in2_input.dev_attr);
570c7461a66SGrant Coady 	device_create_file(&new_client->dev,
571c7461a66SGrant Coady 			&sensor_dev_attr_in2_min.dev_attr);
572c7461a66SGrant Coady 	device_create_file(&new_client->dev,
573c7461a66SGrant Coady 			&sensor_dev_attr_in2_max.dev_attr);
574c7461a66SGrant Coady 	device_create_file(&new_client->dev,
575c7461a66SGrant Coady 			&sensor_dev_attr_in3_input.dev_attr);
576c7461a66SGrant Coady 	device_create_file(&new_client->dev,
577c7461a66SGrant Coady 			&sensor_dev_attr_in3_min.dev_attr);
578c7461a66SGrant Coady 	device_create_file(&new_client->dev,
579c7461a66SGrant Coady 			&sensor_dev_attr_in3_max.dev_attr);
580c7461a66SGrant Coady 	device_create_file(&new_client->dev,
581c7461a66SGrant Coady 			&sensor_dev_attr_in4_input.dev_attr);
582c7461a66SGrant Coady 	device_create_file(&new_client->dev,
583c7461a66SGrant Coady 			&sensor_dev_attr_in4_min.dev_attr);
584c7461a66SGrant Coady 	device_create_file(&new_client->dev,
585c7461a66SGrant Coady 			&sensor_dev_attr_in4_max.dev_attr);
586c7461a66SGrant Coady 	device_create_file(&new_client->dev,
587c7461a66SGrant Coady 			&sensor_dev_attr_in5_input.dev_attr);
588c7461a66SGrant Coady 	device_create_file(&new_client->dev,
589c7461a66SGrant Coady 			&sensor_dev_attr_in5_min.dev_attr);
590c7461a66SGrant Coady 	device_create_file(&new_client->dev,
591c7461a66SGrant Coady 			&sensor_dev_attr_in5_max.dev_attr);
5928d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_temp1_input);
593c7461a66SGrant Coady 	device_create_file(&new_client->dev,
594c7461a66SGrant Coady 			&sensor_dev_attr_temp1_max.dev_attr);
595c7461a66SGrant Coady 	device_create_file(&new_client->dev,
596c7461a66SGrant Coady 			&sensor_dev_attr_temp1_max_hyst.dev_attr);
597c7461a66SGrant Coady 	device_create_file(&new_client->dev,
598c7461a66SGrant Coady 			&sensor_dev_attr_fan1_input.dev_attr);
599c7461a66SGrant Coady 	device_create_file(&new_client->dev,
600c7461a66SGrant Coady 			&sensor_dev_attr_fan1_div.dev_attr);
601c7461a66SGrant Coady 	device_create_file(&new_client->dev,
602c7461a66SGrant Coady 			&sensor_dev_attr_fan1_min.dev_attr);
603c7461a66SGrant Coady 	device_create_file(&new_client->dev,
604c7461a66SGrant Coady 			&sensor_dev_attr_fan2_input.dev_attr);
605c7461a66SGrant Coady 	device_create_file(&new_client->dev,
606c7461a66SGrant Coady 			&sensor_dev_attr_fan2_div.dev_attr);
607c7461a66SGrant Coady 	device_create_file(&new_client->dev,
608c7461a66SGrant Coady 			&sensor_dev_attr_fan2_min.dev_attr);
6098d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_alarms);
6108d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_aout_output);
6118d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_chassis_clear);
6128d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
6138d5d45fbSJean Delvare 
6148d5d45fbSJean Delvare 	return 0;
615943b0830SMark M. Hoffman 
616943b0830SMark M. Hoffman exit_detach:
617943b0830SMark M. Hoffman 	i2c_detach_client(new_client);
6188d5d45fbSJean Delvare exit_free:
6191f57ff89SAlexey Dobriyan 	kfree(data);
6208d5d45fbSJean Delvare exit:
6218d5d45fbSJean Delvare 	return err;
6228d5d45fbSJean Delvare }
6238d5d45fbSJean Delvare 
6248d5d45fbSJean Delvare static int adm9240_attach_adapter(struct i2c_adapter *adapter)
6258d5d45fbSJean Delvare {
6268d5d45fbSJean Delvare 	if (!(adapter->class & I2C_CLASS_HWMON))
6278d5d45fbSJean Delvare 		return 0;
6282ed2dc3cSJean Delvare 	return i2c_probe(adapter, &addr_data, adm9240_detect);
6298d5d45fbSJean Delvare }
6308d5d45fbSJean Delvare 
6318d5d45fbSJean Delvare static int adm9240_detach_client(struct i2c_client *client)
6328d5d45fbSJean Delvare {
633943b0830SMark M. Hoffman 	struct adm9240_data *data = i2c_get_clientdata(client);
6348d5d45fbSJean Delvare 	int err;
6358d5d45fbSJean Delvare 
636943b0830SMark M. Hoffman 	hwmon_device_unregister(data->class_dev);
637943b0830SMark M. Hoffman 
6387bef5594SJean Delvare 	if ((err = i2c_detach_client(client)))
6398d5d45fbSJean Delvare 		return err;
6408d5d45fbSJean Delvare 
641943b0830SMark M. Hoffman 	kfree(data);
6428d5d45fbSJean Delvare 	return 0;
6438d5d45fbSJean Delvare }
6448d5d45fbSJean Delvare 
6458d5d45fbSJean Delvare static void adm9240_init_client(struct i2c_client *client)
6468d5d45fbSJean Delvare {
6478d5d45fbSJean Delvare 	struct adm9240_data *data = i2c_get_clientdata(client);
648205cf13eSGrant Coady 	u8 conf = i2c_smbus_read_byte_data(client, ADM9240_REG_CONFIG);
649205cf13eSGrant Coady 	u8 mode = i2c_smbus_read_byte_data(client, ADM9240_REG_TEMP_CONF) & 3;
6508d5d45fbSJean Delvare 
651303760b4SJean Delvare 	data->vrm = vid_which_vrm(); /* need this to report vid as mV */
6528d5d45fbSJean Delvare 
6538d5d45fbSJean Delvare 	dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10,
6548d5d45fbSJean Delvare 			data->vrm % 10);
6558d5d45fbSJean Delvare 
6568d5d45fbSJean Delvare 	if (conf & 1) { /* measurement cycle running: report state */
6578d5d45fbSJean Delvare 
6588d5d45fbSJean Delvare 		dev_info(&client->dev, "status: config 0x%02x mode %u\n",
6598d5d45fbSJean Delvare 				conf, mode);
6608d5d45fbSJean Delvare 
6618d5d45fbSJean Delvare 	} else { /* cold start: open limits before starting chip */
6628d5d45fbSJean Delvare 		int i;
6638d5d45fbSJean Delvare 
6648d5d45fbSJean Delvare 		for (i = 0; i < 6; i++)
6658d5d45fbSJean Delvare 		{
666205cf13eSGrant Coady 			i2c_smbus_write_byte_data(client,
6678d5d45fbSJean Delvare 					ADM9240_REG_IN_MIN(i), 0);
668205cf13eSGrant Coady 			i2c_smbus_write_byte_data(client,
6698d5d45fbSJean Delvare 					ADM9240_REG_IN_MAX(i), 255);
6708d5d45fbSJean Delvare 		}
671205cf13eSGrant Coady 		i2c_smbus_write_byte_data(client,
672205cf13eSGrant Coady 				ADM9240_REG_FAN_MIN(0), 255);
673205cf13eSGrant Coady 		i2c_smbus_write_byte_data(client,
674205cf13eSGrant Coady 				ADM9240_REG_FAN_MIN(1), 255);
675205cf13eSGrant Coady 		i2c_smbus_write_byte_data(client,
676c7461a66SGrant Coady 				ADM9240_REG_TEMP_MAX(0), 127);
677205cf13eSGrant Coady 		i2c_smbus_write_byte_data(client,
678c7461a66SGrant Coady 				ADM9240_REG_TEMP_MAX(1), 127);
6798d5d45fbSJean Delvare 
6808d5d45fbSJean Delvare 		/* start measurement cycle */
681205cf13eSGrant Coady 		i2c_smbus_write_byte_data(client, ADM9240_REG_CONFIG, 1);
6828d5d45fbSJean Delvare 
6838d5d45fbSJean Delvare 		dev_info(&client->dev, "cold start: config was 0x%02x "
6848d5d45fbSJean Delvare 				"mode %u\n", conf, mode);
6858d5d45fbSJean Delvare 	}
6868d5d45fbSJean Delvare }
6878d5d45fbSJean Delvare 
6888d5d45fbSJean Delvare static struct adm9240_data *adm9240_update_device(struct device *dev)
6898d5d45fbSJean Delvare {
6908d5d45fbSJean Delvare 	struct i2c_client *client = to_i2c_client(dev);
6918d5d45fbSJean Delvare 	struct adm9240_data *data = i2c_get_clientdata(client);
6928d5d45fbSJean Delvare 	int i;
6938d5d45fbSJean Delvare 
6948d5d45fbSJean Delvare 	down(&data->update_lock);
6958d5d45fbSJean Delvare 
6968d5d45fbSJean Delvare 	/* minimum measurement cycle: 1.75 seconds */
6978d5d45fbSJean Delvare 	if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4))
6988d5d45fbSJean Delvare 			|| !data->valid) {
6998d5d45fbSJean Delvare 
7008d5d45fbSJean Delvare 		for (i = 0; i < 6; i++) /* read voltages */
7018d5d45fbSJean Delvare 		{
702205cf13eSGrant Coady 			data->in[i] = i2c_smbus_read_byte_data(client,
7038d5d45fbSJean Delvare 					ADM9240_REG_IN(i));
7048d5d45fbSJean Delvare 		}
705205cf13eSGrant Coady 		data->alarms = i2c_smbus_read_byte_data(client,
7068d5d45fbSJean Delvare 					ADM9240_REG_INT(0)) |
707205cf13eSGrant Coady 					i2c_smbus_read_byte_data(client,
7088d5d45fbSJean Delvare 					ADM9240_REG_INT(1)) << 8;
7098d5d45fbSJean Delvare 
7108d5d45fbSJean Delvare 		/* read temperature: assume temperature changes less than
7118d5d45fbSJean Delvare 		 * 0.5'C per two measurement cycles thus ignore possible
7128d5d45fbSJean Delvare 		 * but unlikely aliasing error on lsb reading. --Grant */
713205cf13eSGrant Coady 		data->temp = ((i2c_smbus_read_byte_data(client,
7148d5d45fbSJean Delvare 					ADM9240_REG_TEMP) << 8) |
715205cf13eSGrant Coady 					i2c_smbus_read_byte_data(client,
7168d5d45fbSJean Delvare 					ADM9240_REG_TEMP_CONF)) / 128;
7178d5d45fbSJean Delvare 
7188d5d45fbSJean Delvare 		for (i = 0; i < 2; i++) /* read fans */
7198d5d45fbSJean Delvare 		{
720205cf13eSGrant Coady 			data->fan[i] = i2c_smbus_read_byte_data(client,
7218d5d45fbSJean Delvare 					ADM9240_REG_FAN(i));
7228d5d45fbSJean Delvare 
7238d5d45fbSJean Delvare 			/* adjust fan clock divider on overflow */
7248d5d45fbSJean Delvare 			if (data->valid && data->fan[i] == 255 &&
7258d5d45fbSJean Delvare 					data->fan_div[i] < 3) {
7268d5d45fbSJean Delvare 
7278d5d45fbSJean Delvare 				adm9240_write_fan_div(client, i,
7288d5d45fbSJean Delvare 						++data->fan_div[i]);
7298d5d45fbSJean Delvare 
7308d5d45fbSJean Delvare 				/* adjust fan_min if active, but not to 0 */
7318d5d45fbSJean Delvare 				if (data->fan_min[i] < 255 &&
7328d5d45fbSJean Delvare 						data->fan_min[i] >= 2)
7338d5d45fbSJean Delvare 					data->fan_min[i] /= 2;
7348d5d45fbSJean Delvare 			}
7358d5d45fbSJean Delvare 		}
7368d5d45fbSJean Delvare 		data->last_updated_measure = jiffies;
7378d5d45fbSJean Delvare 	}
7388d5d45fbSJean Delvare 
7398d5d45fbSJean Delvare 	/* minimum config reading cycle: 300 seconds */
7408d5d45fbSJean Delvare 	if (time_after(jiffies, data->last_updated_config + (HZ * 300))
7418d5d45fbSJean Delvare 			|| !data->valid) {
7428d5d45fbSJean Delvare 
7438d5d45fbSJean Delvare 		for (i = 0; i < 6; i++)
7448d5d45fbSJean Delvare 		{
745205cf13eSGrant Coady 			data->in_min[i] = i2c_smbus_read_byte_data(client,
7468d5d45fbSJean Delvare 					ADM9240_REG_IN_MIN(i));
747205cf13eSGrant Coady 			data->in_max[i] = i2c_smbus_read_byte_data(client,
7488d5d45fbSJean Delvare 					ADM9240_REG_IN_MAX(i));
7498d5d45fbSJean Delvare 		}
7508d5d45fbSJean Delvare 		for (i = 0; i < 2; i++)
7518d5d45fbSJean Delvare 		{
752205cf13eSGrant Coady 			data->fan_min[i] = i2c_smbus_read_byte_data(client,
7538d5d45fbSJean Delvare 					ADM9240_REG_FAN_MIN(i));
7548d5d45fbSJean Delvare 		}
755c7461a66SGrant Coady 		data->temp_max[0] = i2c_smbus_read_byte_data(client,
756c7461a66SGrant Coady 				ADM9240_REG_TEMP_MAX(0));
757c7461a66SGrant Coady 		data->temp_max[1] = i2c_smbus_read_byte_data(client,
758c7461a66SGrant Coady 				ADM9240_REG_TEMP_MAX(1));
7598d5d45fbSJean Delvare 
7608d5d45fbSJean Delvare 		/* read fan divs and 5-bit VID */
761205cf13eSGrant Coady 		i = i2c_smbus_read_byte_data(client, ADM9240_REG_VID_FAN_DIV);
7628d5d45fbSJean Delvare 		data->fan_div[0] = (i >> 4) & 3;
7638d5d45fbSJean Delvare 		data->fan_div[1] = (i >> 6) & 3;
7648d5d45fbSJean Delvare 		data->vid = i & 0x0f;
765205cf13eSGrant Coady 		data->vid |= (i2c_smbus_read_byte_data(client,
7668d5d45fbSJean Delvare 					ADM9240_REG_VID4) & 1) << 4;
7678d5d45fbSJean Delvare 		/* read analog out */
768205cf13eSGrant Coady 		data->aout = i2c_smbus_read_byte_data(client,
7698d5d45fbSJean Delvare 				ADM9240_REG_ANALOG_OUT);
7708d5d45fbSJean Delvare 
7718d5d45fbSJean Delvare 		data->last_updated_config = jiffies;
7728d5d45fbSJean Delvare 		data->valid = 1;
7738d5d45fbSJean Delvare 	}
7748d5d45fbSJean Delvare 	up(&data->update_lock);
7758d5d45fbSJean Delvare 	return data;
7768d5d45fbSJean Delvare }
7778d5d45fbSJean Delvare 
7788d5d45fbSJean Delvare static int __init sensors_adm9240_init(void)
7798d5d45fbSJean Delvare {
7808d5d45fbSJean Delvare 	return i2c_add_driver(&adm9240_driver);
7818d5d45fbSJean Delvare }
7828d5d45fbSJean Delvare 
7838d5d45fbSJean Delvare static void __exit sensors_adm9240_exit(void)
7848d5d45fbSJean Delvare {
7858d5d45fbSJean Delvare 	i2c_del_driver(&adm9240_driver);
7868d5d45fbSJean Delvare }
7878d5d45fbSJean Delvare 
7888d5d45fbSJean Delvare MODULE_AUTHOR("Michiel Rook <michiel@grendelproject.nl>, "
7898d5d45fbSJean Delvare 		"Grant Coady <gcoady@gmail.com> and others");
7908d5d45fbSJean Delvare MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver");
7918d5d45fbSJean Delvare MODULE_LICENSE("GPL");
7928d5d45fbSJean Delvare 
7938d5d45fbSJean Delvare module_init(sensors_adm9240_init);
7948d5d45fbSJean Delvare module_exit(sensors_adm9240_exit);
7958d5d45fbSJean Delvare 
796