xref: /openbmc/linux/drivers/hwmon/lm78.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28d5d45fbSJean Delvare /*
39b03079fSGuenter Roeck  * lm78.c - Part of lm_sensors, Linux kernel modules for hardware
49b03079fSGuenter Roeck  *	    monitoring
59b03079fSGuenter Roeck  * Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
67c81c60fSJean Delvare  * Copyright (c) 2007, 2011  Jean Delvare <jdelvare@suse.de>
78d5d45fbSJean Delvare  */
88d5d45fbSJean Delvare 
9ce47da74SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10ce47da74SJoe Perches 
118d5d45fbSJean Delvare #include <linux/module.h>
128d5d45fbSJean Delvare #include <linux/init.h>
138d5d45fbSJean Delvare #include <linux/slab.h>
148d5d45fbSJean Delvare #include <linux/jiffies.h>
158d5d45fbSJean Delvare #include <linux/i2c.h>
16943b0830SMark M. Hoffman #include <linux/hwmon.h>
1719f673edSJean Delvare #include <linux/hwmon-vid.h>
18247dde4cSJean Delvare #include <linux/hwmon-sysfs.h>
19943b0830SMark M. Hoffman #include <linux/err.h>
209a61bf63SIngo Molnar #include <linux/mutex.h>
218d5d45fbSJean Delvare 
2290534c5cSJean Delvare #ifdef CONFIG_ISA
2390534c5cSJean Delvare #include <linux/platform_device.h>
2490534c5cSJean Delvare #include <linux/ioport.h>
2590534c5cSJean Delvare #include <linux/io.h>
2690534c5cSJean Delvare #endif
27c40769feSJean Delvare 
288d5d45fbSJean Delvare /* Addresses to scan */
2925e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
306722feadSJean Delvare 						0x2e, 0x2f, I2C_CLIENT_END };
31e5e9f44cSJean Delvare enum chips { lm78, lm79 };
328d5d45fbSJean Delvare 
338d5d45fbSJean Delvare /* Many LM78 constants specified below */
348d5d45fbSJean Delvare 
358d5d45fbSJean Delvare /* Length of ISA address segment */
368d5d45fbSJean Delvare #define LM78_EXTENT 8
378d5d45fbSJean Delvare 
388d5d45fbSJean Delvare /* Where are the ISA address/data registers relative to the base address */
398d5d45fbSJean Delvare #define LM78_ADDR_REG_OFFSET 5
408d5d45fbSJean Delvare #define LM78_DATA_REG_OFFSET 6
418d5d45fbSJean Delvare 
428d5d45fbSJean Delvare /* The LM78 registers */
438d5d45fbSJean Delvare #define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
448d5d45fbSJean Delvare #define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
458d5d45fbSJean Delvare #define LM78_REG_IN(nr) (0x20 + (nr))
468d5d45fbSJean Delvare 
478d5d45fbSJean Delvare #define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
488d5d45fbSJean Delvare #define LM78_REG_FAN(nr) (0x28 + (nr))
498d5d45fbSJean Delvare 
508d5d45fbSJean Delvare #define LM78_REG_TEMP 0x27
518d5d45fbSJean Delvare #define LM78_REG_TEMP_OVER 0x39
528d5d45fbSJean Delvare #define LM78_REG_TEMP_HYST 0x3a
538d5d45fbSJean Delvare 
548d5d45fbSJean Delvare #define LM78_REG_ALARM1 0x41
558d5d45fbSJean Delvare #define LM78_REG_ALARM2 0x42
568d5d45fbSJean Delvare 
578d5d45fbSJean Delvare #define LM78_REG_VID_FANDIV 0x47
588d5d45fbSJean Delvare 
598d5d45fbSJean Delvare #define LM78_REG_CONFIG 0x40
608d5d45fbSJean Delvare #define LM78_REG_CHIPID 0x49
618d5d45fbSJean Delvare #define LM78_REG_I2C_ADDR 0x48
628d5d45fbSJean Delvare 
639b03079fSGuenter Roeck /*
649b03079fSGuenter Roeck  * Conversions. Rounding and limit checking is only done on the TO_REG
659b03079fSGuenter Roeck  * variants.
669b03079fSGuenter Roeck  */
678d5d45fbSJean Delvare 
689b03079fSGuenter Roeck /*
699b03079fSGuenter Roeck  * IN: mV (0V to 4.08V)
709b03079fSGuenter Roeck  * REG: 16mV/bit
719b03079fSGuenter Roeck  */
IN_TO_REG(unsigned long val)728d5d45fbSJean Delvare static inline u8 IN_TO_REG(unsigned long val)
738d5d45fbSJean Delvare {
742a844c14SGuenter Roeck 	unsigned long nval = clamp_val(val, 0, 4080);
758d5d45fbSJean Delvare 	return (nval + 8) / 16;
768d5d45fbSJean Delvare }
778d5d45fbSJean Delvare #define IN_FROM_REG(val) ((val) *  16)
788d5d45fbSJean Delvare 
FAN_TO_REG(long rpm,int div)798d5d45fbSJean Delvare static inline u8 FAN_TO_REG(long rpm, int div)
808d5d45fbSJean Delvare {
818d5d45fbSJean Delvare 	if (rpm <= 0)
828d5d45fbSJean Delvare 		return 255;
833806b45bSDan Carpenter 	if (rpm > 1350000)
843806b45bSDan Carpenter 		return 1;
852a844c14SGuenter Roeck 	return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
868d5d45fbSJean Delvare }
878d5d45fbSJean Delvare 
FAN_FROM_REG(u8 val,int div)888d5d45fbSJean Delvare static inline int FAN_FROM_REG(u8 val, int div)
898d5d45fbSJean Delvare {
908d5d45fbSJean Delvare 	return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div);
918d5d45fbSJean Delvare }
928d5d45fbSJean Delvare 
939b03079fSGuenter Roeck /*
949b03079fSGuenter Roeck  * TEMP: mC (-128C to +127C)
959b03079fSGuenter Roeck  * REG: 1C/bit, two's complement
969b03079fSGuenter Roeck  */
TEMP_TO_REG(long val)971074d683SGuenter Roeck static inline s8 TEMP_TO_REG(long val)
988d5d45fbSJean Delvare {
992a844c14SGuenter Roeck 	int nval = clamp_val(val, -128000, 127000) ;
1008d5d45fbSJean Delvare 	return nval < 0 ? (nval - 500) / 1000 : (nval + 500) / 1000;
1018d5d45fbSJean Delvare }
1028d5d45fbSJean Delvare 
TEMP_FROM_REG(s8 val)1038d5d45fbSJean Delvare static inline int TEMP_FROM_REG(s8 val)
1048d5d45fbSJean Delvare {
1058d5d45fbSJean Delvare 	return val * 1000;
1068d5d45fbSJean Delvare }
1078d5d45fbSJean Delvare 
1088d5d45fbSJean Delvare #define DIV_FROM_REG(val) (1 << (val))
1098d5d45fbSJean Delvare 
1108d5d45fbSJean Delvare struct lm78_data {
1110c6e9731SJean Delvare 	struct i2c_client *client;
1129a61bf63SIngo Molnar 	struct mutex lock;
1138d5d45fbSJean Delvare 	enum chips type;
1148d5d45fbSJean Delvare 
1156e1b5029SJean Delvare 	/* For ISA device only */
1166e1b5029SJean Delvare 	const char *name;
1176e1b5029SJean Delvare 	int isa_addr;
1186e1b5029SJean Delvare 
1199a61bf63SIngo Molnar 	struct mutex update_lock;
120952a11caSPaul Fertser 	bool valid;		/* true if following fields are valid */
1218d5d45fbSJean Delvare 	unsigned long last_updated;	/* In jiffies */
1228d5d45fbSJean Delvare 
1238d5d45fbSJean Delvare 	u8 in[7];		/* Register value */
1248d5d45fbSJean Delvare 	u8 in_max[7];		/* Register value */
1258d5d45fbSJean Delvare 	u8 in_min[7];		/* Register value */
1268d5d45fbSJean Delvare 	u8 fan[3];		/* Register value */
1278d5d45fbSJean Delvare 	u8 fan_min[3];		/* Register value */
1288d5d45fbSJean Delvare 	s8 temp;		/* Register value */
1298d5d45fbSJean Delvare 	s8 temp_over;		/* Register value */
1308d5d45fbSJean Delvare 	s8 temp_hyst;		/* Register value */
1318d5d45fbSJean Delvare 	u8 fan_div[3];		/* Register encoding, shifted right */
1328d5d45fbSJean Delvare 	u8 vid;			/* Register encoding, combined */
1338d5d45fbSJean Delvare 	u16 alarms;		/* Register encoding, combined */
1348d5d45fbSJean Delvare };
1358d5d45fbSJean Delvare 
136c59cc301SJean Delvare static int lm78_read_value(struct lm78_data *data, u8 reg);
137c59cc301SJean Delvare static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
1388d5d45fbSJean Delvare static struct lm78_data *lm78_update_device(struct device *dev);
139c59cc301SJean Delvare static void lm78_init_device(struct lm78_data *data);
1408d5d45fbSJean Delvare 
1418d5d45fbSJean Delvare /* 7 Voltages */
in_show(struct device * dev,struct device_attribute * da,char * buf)142e7655cfdSGuenter Roeck static ssize_t in_show(struct device *dev, struct device_attribute *da,
143247dde4cSJean Delvare 		       char *buf)
1448d5d45fbSJean Delvare {
145247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1468d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
147247dde4cSJean Delvare 	return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index]));
1488d5d45fbSJean Delvare }
1498d5d45fbSJean Delvare 
in_min_show(struct device * dev,struct device_attribute * da,char * buf)150e7655cfdSGuenter Roeck static ssize_t in_min_show(struct device *dev, struct device_attribute *da,
151247dde4cSJean Delvare 			   char *buf)
1528d5d45fbSJean Delvare {
153247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1548d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
155247dde4cSJean Delvare 	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index]));
1568d5d45fbSJean Delvare }
1578d5d45fbSJean Delvare 
in_max_show(struct device * dev,struct device_attribute * da,char * buf)158e7655cfdSGuenter Roeck static ssize_t in_max_show(struct device *dev, struct device_attribute *da,
159247dde4cSJean Delvare 			   char *buf)
1608d5d45fbSJean Delvare {
161247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1628d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
163247dde4cSJean Delvare 	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index]));
1648d5d45fbSJean Delvare }
1658d5d45fbSJean Delvare 
in_min_store(struct device * dev,struct device_attribute * da,const char * buf,size_t count)166e7655cfdSGuenter Roeck static ssize_t in_min_store(struct device *dev, struct device_attribute *da,
167247dde4cSJean Delvare 			    const char *buf, size_t count)
1688d5d45fbSJean Delvare {
169247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
170c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
171247dde4cSJean Delvare 	int nr = attr->index;
1729b03079fSGuenter Roeck 	unsigned long val;
1739b03079fSGuenter Roeck 	int err;
1749b03079fSGuenter Roeck 
1759b03079fSGuenter Roeck 	err = kstrtoul(buf, 10, &val);
1769b03079fSGuenter Roeck 	if (err)
1779b03079fSGuenter Roeck 		return err;
1788d5d45fbSJean Delvare 
1799a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
1808d5d45fbSJean Delvare 	data->in_min[nr] = IN_TO_REG(val);
181c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
1829a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
1838d5d45fbSJean Delvare 	return count;
1848d5d45fbSJean Delvare }
1858d5d45fbSJean Delvare 
in_max_store(struct device * dev,struct device_attribute * da,const char * buf,size_t count)186e7655cfdSGuenter Roeck static ssize_t in_max_store(struct device *dev, struct device_attribute *da,
187247dde4cSJean Delvare 			    const char *buf, size_t count)
1888d5d45fbSJean Delvare {
189247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
190c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
191247dde4cSJean Delvare 	int nr = attr->index;
1929b03079fSGuenter Roeck 	unsigned long val;
1939b03079fSGuenter Roeck 	int err;
1949b03079fSGuenter Roeck 
1959b03079fSGuenter Roeck 	err = kstrtoul(buf, 10, &val);
1969b03079fSGuenter Roeck 	if (err)
1979b03079fSGuenter Roeck 		return err;
1988d5d45fbSJean Delvare 
1999a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
2008d5d45fbSJean Delvare 	data->in_max[nr] = IN_TO_REG(val);
201c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
2029a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
2038d5d45fbSJean Delvare 	return count;
2048d5d45fbSJean Delvare }
2058d5d45fbSJean Delvare 
206e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0);
207e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0);
208e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0);
209e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1);
210e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1);
211e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1);
212e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2);
213e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2);
214e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2);
215e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3);
216e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3);
217e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3);
218e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4);
219e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4);
220e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4);
221e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in5_input, in, 5);
222e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5);
223e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5);
224e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in6_input, in, 6);
225e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6);
226e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6);
2278d5d45fbSJean Delvare 
2288d5d45fbSJean Delvare /* Temperature */
temp1_input_show(struct device * dev,struct device_attribute * da,char * buf)229eabb6f15SJulia Lawall static ssize_t temp1_input_show(struct device *dev,
230eabb6f15SJulia Lawall 				struct device_attribute *da, char *buf)
2318d5d45fbSJean Delvare {
2328d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
2338d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
2348d5d45fbSJean Delvare }
2358d5d45fbSJean Delvare 
temp1_max_show(struct device * dev,struct device_attribute * da,char * buf)236eabb6f15SJulia Lawall static ssize_t temp1_max_show(struct device *dev, struct device_attribute *da,
237247dde4cSJean Delvare 			      char *buf)
2388d5d45fbSJean Delvare {
2398d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
2408d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
2418d5d45fbSJean Delvare }
2428d5d45fbSJean Delvare 
temp1_max_store(struct device * dev,struct device_attribute * da,const char * buf,size_t count)243eabb6f15SJulia Lawall static ssize_t temp1_max_store(struct device *dev,
244eabb6f15SJulia Lawall 			       struct device_attribute *da, const char *buf,
245eabb6f15SJulia Lawall 			       size_t count)
2468d5d45fbSJean Delvare {
247c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
2489b03079fSGuenter Roeck 	long val;
2499b03079fSGuenter Roeck 	int err;
2509b03079fSGuenter Roeck 
2519b03079fSGuenter Roeck 	err = kstrtol(buf, 10, &val);
2529b03079fSGuenter Roeck 	if (err)
2539b03079fSGuenter Roeck 		return err;
2548d5d45fbSJean Delvare 
2559a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
2568d5d45fbSJean Delvare 	data->temp_over = TEMP_TO_REG(val);
257c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
2589a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
2598d5d45fbSJean Delvare 	return count;
2608d5d45fbSJean Delvare }
2618d5d45fbSJean Delvare 
temp1_max_hyst_show(struct device * dev,struct device_attribute * da,char * buf)262eabb6f15SJulia Lawall static ssize_t temp1_max_hyst_show(struct device *dev,
263eabb6f15SJulia Lawall 				   struct device_attribute *da, char *buf)
2648d5d45fbSJean Delvare {
2658d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
2668d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
2678d5d45fbSJean Delvare }
2688d5d45fbSJean Delvare 
temp1_max_hyst_store(struct device * dev,struct device_attribute * da,const char * buf,size_t count)269eabb6f15SJulia Lawall static ssize_t temp1_max_hyst_store(struct device *dev,
270eabb6f15SJulia Lawall 				    struct device_attribute *da,
271247dde4cSJean Delvare 				    const char *buf, size_t count)
2728d5d45fbSJean Delvare {
273c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
2749b03079fSGuenter Roeck 	long val;
2759b03079fSGuenter Roeck 	int err;
2769b03079fSGuenter Roeck 
2779b03079fSGuenter Roeck 	err = kstrtol(buf, 10, &val);
2789b03079fSGuenter Roeck 	if (err)
2799b03079fSGuenter Roeck 		return err;
2808d5d45fbSJean Delvare 
2819a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
2828d5d45fbSJean Delvare 	data->temp_hyst = TEMP_TO_REG(val);
283c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
2849a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
2858d5d45fbSJean Delvare 	return count;
2868d5d45fbSJean Delvare }
2878d5d45fbSJean Delvare 
288eabb6f15SJulia Lawall static DEVICE_ATTR_RO(temp1_input);
289eabb6f15SJulia Lawall static DEVICE_ATTR_RW(temp1_max);
290eabb6f15SJulia Lawall static DEVICE_ATTR_RW(temp1_max_hyst);
2918d5d45fbSJean Delvare 
2928d5d45fbSJean Delvare /* 3 Fans */
fan_show(struct device * dev,struct device_attribute * da,char * buf)293e7655cfdSGuenter Roeck static ssize_t fan_show(struct device *dev, struct device_attribute *da,
294247dde4cSJean Delvare 			char *buf)
2958d5d45fbSJean Delvare {
296247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
2978d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
298247dde4cSJean Delvare 	int nr = attr->index;
2998d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
3008d5d45fbSJean Delvare 		DIV_FROM_REG(data->fan_div[nr])));
3018d5d45fbSJean Delvare }
3028d5d45fbSJean Delvare 
fan_min_show(struct device * dev,struct device_attribute * da,char * buf)303e7655cfdSGuenter Roeck static ssize_t fan_min_show(struct device *dev, struct device_attribute *da,
304247dde4cSJean Delvare 			    char *buf)
3058d5d45fbSJean Delvare {
306247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3078d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
308247dde4cSJean Delvare 	int nr = attr->index;
3098d5d45fbSJean Delvare 	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
3108d5d45fbSJean Delvare 		DIV_FROM_REG(data->fan_div[nr])));
3118d5d45fbSJean Delvare }
3128d5d45fbSJean Delvare 
fan_min_store(struct device * dev,struct device_attribute * da,const char * buf,size_t count)313e7655cfdSGuenter Roeck static ssize_t fan_min_store(struct device *dev, struct device_attribute *da,
314247dde4cSJean Delvare 			     const char *buf, size_t count)
3158d5d45fbSJean Delvare {
316247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
317c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
318247dde4cSJean Delvare 	int nr = attr->index;
3199b03079fSGuenter Roeck 	unsigned long val;
3209b03079fSGuenter Roeck 	int err;
3219b03079fSGuenter Roeck 
3229b03079fSGuenter Roeck 	err = kstrtoul(buf, 10, &val);
3239b03079fSGuenter Roeck 	if (err)
3249b03079fSGuenter Roeck 		return err;
3258d5d45fbSJean Delvare 
3269a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
3278d5d45fbSJean Delvare 	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
328c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
3299a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
3308d5d45fbSJean Delvare 	return count;
3318d5d45fbSJean Delvare }
3328d5d45fbSJean Delvare 
fan_div_show(struct device * dev,struct device_attribute * da,char * buf)333e7655cfdSGuenter Roeck static ssize_t fan_div_show(struct device *dev, struct device_attribute *da,
334247dde4cSJean Delvare 			    char *buf)
3358d5d45fbSJean Delvare {
336247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3378d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
338247dde4cSJean Delvare 	return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
3398d5d45fbSJean Delvare }
3408d5d45fbSJean Delvare 
3419b03079fSGuenter Roeck /*
3429b03079fSGuenter Roeck  * Note: we save and restore the fan minimum here, because its value is
3439b03079fSGuenter Roeck  * determined in part by the fan divisor.  This follows the principle of
3449b03079fSGuenter Roeck  * least surprise; the user doesn't expect the fan minimum to change just
3459b03079fSGuenter Roeck  * because the divisor changed.
3469b03079fSGuenter Roeck  */
fan_div_store(struct device * dev,struct device_attribute * da,const char * buf,size_t count)347e7655cfdSGuenter Roeck static ssize_t fan_div_store(struct device *dev, struct device_attribute *da,
348247dde4cSJean Delvare 			     const char *buf, size_t count)
3498d5d45fbSJean Delvare {
350247dde4cSJean Delvare 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
351c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
352247dde4cSJean Delvare 	int nr = attr->index;
3538d5d45fbSJean Delvare 	unsigned long min;
3548d5d45fbSJean Delvare 	u8 reg;
3559b03079fSGuenter Roeck 	unsigned long val;
3569b03079fSGuenter Roeck 	int err;
3579b03079fSGuenter Roeck 
3589b03079fSGuenter Roeck 	err = kstrtoul(buf, 10, &val);
3599b03079fSGuenter Roeck 	if (err)
3609b03079fSGuenter Roeck 		return err;
3618d5d45fbSJean Delvare 
3629a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
3638d5d45fbSJean Delvare 	min = FAN_FROM_REG(data->fan_min[nr],
3648d5d45fbSJean Delvare 			   DIV_FROM_REG(data->fan_div[nr]));
3658d5d45fbSJean Delvare 
3668d5d45fbSJean Delvare 	switch (val) {
3679b03079fSGuenter Roeck 	case 1:
3689b03079fSGuenter Roeck 		data->fan_div[nr] = 0;
3699b03079fSGuenter Roeck 		break;
3709b03079fSGuenter Roeck 	case 2:
3719b03079fSGuenter Roeck 		data->fan_div[nr] = 1;
3729b03079fSGuenter Roeck 		break;
3739b03079fSGuenter Roeck 	case 4:
3749b03079fSGuenter Roeck 		data->fan_div[nr] = 2;
3759b03079fSGuenter Roeck 		break;
3769b03079fSGuenter Roeck 	case 8:
3779b03079fSGuenter Roeck 		data->fan_div[nr] = 3;
3789b03079fSGuenter Roeck 		break;
3798d5d45fbSJean Delvare 	default:
380b55f3757SGuenter Roeck 		dev_err(dev,
381b55f3757SGuenter Roeck 			"fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
382b55f3757SGuenter Roeck 			val);
3839a61bf63SIngo Molnar 		mutex_unlock(&data->update_lock);
3848d5d45fbSJean Delvare 		return -EINVAL;
3858d5d45fbSJean Delvare 	}
3868d5d45fbSJean Delvare 
387c59cc301SJean Delvare 	reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
3888d5d45fbSJean Delvare 	switch (nr) {
3898d5d45fbSJean Delvare 	case 0:
3908d5d45fbSJean Delvare 		reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
3918d5d45fbSJean Delvare 		break;
3928d5d45fbSJean Delvare 	case 1:
3938d5d45fbSJean Delvare 		reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
3948d5d45fbSJean Delvare 		break;
3958d5d45fbSJean Delvare 	}
396c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
3978d5d45fbSJean Delvare 
3988d5d45fbSJean Delvare 	data->fan_min[nr] =
3998d5d45fbSJean Delvare 		FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
400c59cc301SJean Delvare 	lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
4019a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
4028d5d45fbSJean Delvare 
4038d5d45fbSJean Delvare 	return count;
4048d5d45fbSJean Delvare }
4058d5d45fbSJean Delvare 
406e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
407e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0);
408e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
409e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
410e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
411e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2);
4128d5d45fbSJean Delvare 
4138d5d45fbSJean Delvare /* Fan 3 divisor is locked in H/W */
414e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0);
415e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
416e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan3_div, fan_div, 2);
4178d5d45fbSJean Delvare 
4188d5d45fbSJean Delvare /* VID */
cpu0_vid_show(struct device * dev,struct device_attribute * da,char * buf)419eabb6f15SJulia Lawall static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *da,
420247dde4cSJean Delvare 			     char *buf)
4218d5d45fbSJean Delvare {
4228d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
423d0d3cd69SJean Delvare 	return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
4248d5d45fbSJean Delvare }
425eabb6f15SJulia Lawall static DEVICE_ATTR_RO(cpu0_vid);
4268d5d45fbSJean Delvare 
4278d5d45fbSJean Delvare /* Alarms */
alarms_show(struct device * dev,struct device_attribute * da,char * buf)428eabb6f15SJulia Lawall static ssize_t alarms_show(struct device *dev, struct device_attribute *da,
429247dde4cSJean Delvare 			   char *buf)
4308d5d45fbSJean Delvare {
4318d5d45fbSJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
4328d5d45fbSJean Delvare 	return sprintf(buf, "%u\n", data->alarms);
4338d5d45fbSJean Delvare }
434eabb6f15SJulia Lawall static DEVICE_ATTR_RO(alarms);
4358d5d45fbSJean Delvare 
alarm_show(struct device * dev,struct device_attribute * da,char * buf)436e7655cfdSGuenter Roeck static ssize_t alarm_show(struct device *dev, struct device_attribute *da,
437428a7039SJean Delvare 			  char *buf)
438428a7039SJean Delvare {
439428a7039SJean Delvare 	struct lm78_data *data = lm78_update_device(dev);
440428a7039SJean Delvare 	int nr = to_sensor_dev_attr(da)->index;
441428a7039SJean Delvare 	return sprintf(buf, "%u\n", (data->alarms >> nr) & 1);
442428a7039SJean Delvare }
443e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0);
444e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1);
445e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2);
446e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3);
447e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8);
448e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9);
449e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 10);
450e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
451e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
452e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 11);
453e7655cfdSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
454428a7039SJean Delvare 
4558eb40610SAxel Lin static struct attribute *lm78_attrs[] = {
456247dde4cSJean Delvare 	&sensor_dev_attr_in0_input.dev_attr.attr,
457247dde4cSJean Delvare 	&sensor_dev_attr_in0_min.dev_attr.attr,
458247dde4cSJean Delvare 	&sensor_dev_attr_in0_max.dev_attr.attr,
459428a7039SJean Delvare 	&sensor_dev_attr_in0_alarm.dev_attr.attr,
460247dde4cSJean Delvare 	&sensor_dev_attr_in1_input.dev_attr.attr,
461247dde4cSJean Delvare 	&sensor_dev_attr_in1_min.dev_attr.attr,
462247dde4cSJean Delvare 	&sensor_dev_attr_in1_max.dev_attr.attr,
463428a7039SJean Delvare 	&sensor_dev_attr_in1_alarm.dev_attr.attr,
464247dde4cSJean Delvare 	&sensor_dev_attr_in2_input.dev_attr.attr,
465247dde4cSJean Delvare 	&sensor_dev_attr_in2_min.dev_attr.attr,
466247dde4cSJean Delvare 	&sensor_dev_attr_in2_max.dev_attr.attr,
467428a7039SJean Delvare 	&sensor_dev_attr_in2_alarm.dev_attr.attr,
468247dde4cSJean Delvare 	&sensor_dev_attr_in3_input.dev_attr.attr,
469247dde4cSJean Delvare 	&sensor_dev_attr_in3_min.dev_attr.attr,
470247dde4cSJean Delvare 	&sensor_dev_attr_in3_max.dev_attr.attr,
471428a7039SJean Delvare 	&sensor_dev_attr_in3_alarm.dev_attr.attr,
472247dde4cSJean Delvare 	&sensor_dev_attr_in4_input.dev_attr.attr,
473247dde4cSJean Delvare 	&sensor_dev_attr_in4_min.dev_attr.attr,
474247dde4cSJean Delvare 	&sensor_dev_attr_in4_max.dev_attr.attr,
475428a7039SJean Delvare 	&sensor_dev_attr_in4_alarm.dev_attr.attr,
476247dde4cSJean Delvare 	&sensor_dev_attr_in5_input.dev_attr.attr,
477247dde4cSJean Delvare 	&sensor_dev_attr_in5_min.dev_attr.attr,
478247dde4cSJean Delvare 	&sensor_dev_attr_in5_max.dev_attr.attr,
479428a7039SJean Delvare 	&sensor_dev_attr_in5_alarm.dev_attr.attr,
480247dde4cSJean Delvare 	&sensor_dev_attr_in6_input.dev_attr.attr,
481247dde4cSJean Delvare 	&sensor_dev_attr_in6_min.dev_attr.attr,
482247dde4cSJean Delvare 	&sensor_dev_attr_in6_max.dev_attr.attr,
483428a7039SJean Delvare 	&sensor_dev_attr_in6_alarm.dev_attr.attr,
484c1685f61SMark M. Hoffman 	&dev_attr_temp1_input.attr,
485c1685f61SMark M. Hoffman 	&dev_attr_temp1_max.attr,
486c1685f61SMark M. Hoffman 	&dev_attr_temp1_max_hyst.attr,
487428a7039SJean Delvare 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
488247dde4cSJean Delvare 	&sensor_dev_attr_fan1_input.dev_attr.attr,
489247dde4cSJean Delvare 	&sensor_dev_attr_fan1_min.dev_attr.attr,
490247dde4cSJean Delvare 	&sensor_dev_attr_fan1_div.dev_attr.attr,
491428a7039SJean Delvare 	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
492247dde4cSJean Delvare 	&sensor_dev_attr_fan2_input.dev_attr.attr,
493247dde4cSJean Delvare 	&sensor_dev_attr_fan2_min.dev_attr.attr,
494247dde4cSJean Delvare 	&sensor_dev_attr_fan2_div.dev_attr.attr,
495428a7039SJean Delvare 	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
496247dde4cSJean Delvare 	&sensor_dev_attr_fan3_input.dev_attr.attr,
497247dde4cSJean Delvare 	&sensor_dev_attr_fan3_min.dev_attr.attr,
498247dde4cSJean Delvare 	&sensor_dev_attr_fan3_div.dev_attr.attr,
499428a7039SJean Delvare 	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
500c1685f61SMark M. Hoffman 	&dev_attr_alarms.attr,
501c1685f61SMark M. Hoffman 	&dev_attr_cpu0_vid.attr,
502c1685f61SMark M. Hoffman 
503c1685f61SMark M. Hoffman 	NULL
504c1685f61SMark M. Hoffman };
505c1685f61SMark M. Hoffman 
5068eb40610SAxel Lin ATTRIBUTE_GROUPS(lm78);
507c1685f61SMark M. Hoffman 
50890534c5cSJean Delvare /*
50990534c5cSJean Delvare  * ISA related code
51090534c5cSJean Delvare  */
51190534c5cSJean Delvare #ifdef CONFIG_ISA
51290534c5cSJean Delvare 
51390534c5cSJean Delvare /* ISA device, if found */
51490534c5cSJean Delvare static struct platform_device *pdev;
51590534c5cSJean Delvare 
51690534c5cSJean Delvare static unsigned short isa_address = 0x290;
51790534c5cSJean Delvare 
lm78_data_if_isa(void)51890534c5cSJean Delvare static struct lm78_data *lm78_data_if_isa(void)
51990534c5cSJean Delvare {
52090534c5cSJean Delvare 	return pdev ? platform_get_drvdata(pdev) : NULL;
52190534c5cSJean Delvare }
52290534c5cSJean Delvare 
52318c73f90SJean Delvare /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
lm78_alias_detect(struct i2c_client * client,u8 chipid)52418c73f90SJean Delvare static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
52518c73f90SJean Delvare {
5260c6e9731SJean Delvare 	struct lm78_data *isa;
52718c73f90SJean Delvare 	int i;
52818c73f90SJean Delvare 
52918c73f90SJean Delvare 	if (!pdev)	/* No ISA chip */
53018c73f90SJean Delvare 		return 0;
53118c73f90SJean Delvare 	isa = platform_get_drvdata(pdev);
53218c73f90SJean Delvare 
53318c73f90SJean Delvare 	if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
53418c73f90SJean Delvare 		return 0;	/* Address doesn't match */
53518c73f90SJean Delvare 	if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
53618c73f90SJean Delvare 		return 0;	/* Chip type doesn't match */
53718c73f90SJean Delvare 
5389b03079fSGuenter Roeck 	/*
5399b03079fSGuenter Roeck 	 * We compare all the limit registers, the config register and the
5409b03079fSGuenter Roeck 	 * interrupt mask registers
5419b03079fSGuenter Roeck 	 */
54218c73f90SJean Delvare 	for (i = 0x2b; i <= 0x3d; i++) {
5430c6e9731SJean Delvare 		if (lm78_read_value(isa, i) !=
5440c6e9731SJean Delvare 		    i2c_smbus_read_byte_data(client, i))
54518c73f90SJean Delvare 			return 0;
54618c73f90SJean Delvare 	}
54718c73f90SJean Delvare 	if (lm78_read_value(isa, LM78_REG_CONFIG) !=
5480c6e9731SJean Delvare 	    i2c_smbus_read_byte_data(client, LM78_REG_CONFIG))
54918c73f90SJean Delvare 		return 0;
55018c73f90SJean Delvare 	for (i = 0x43; i <= 0x46; i++) {
5510c6e9731SJean Delvare 		if (lm78_read_value(isa, i) !=
5520c6e9731SJean Delvare 		    i2c_smbus_read_byte_data(client, i))
55318c73f90SJean Delvare 			return 0;
55418c73f90SJean Delvare 	}
55518c73f90SJean Delvare 
55618c73f90SJean Delvare 	return 1;
55718c73f90SJean Delvare }
55890534c5cSJean Delvare #else /* !CONFIG_ISA */
55990534c5cSJean Delvare 
lm78_alias_detect(struct i2c_client * client,u8 chipid)56090534c5cSJean Delvare static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
56190534c5cSJean Delvare {
56290534c5cSJean Delvare 	return 0;
56390534c5cSJean Delvare }
56490534c5cSJean Delvare 
lm78_data_if_isa(void)56590534c5cSJean Delvare static struct lm78_data *lm78_data_if_isa(void)
56690534c5cSJean Delvare {
56790534c5cSJean Delvare 	return NULL;
56890534c5cSJean Delvare }
56990534c5cSJean Delvare #endif /* CONFIG_ISA */
57018c73f90SJean Delvare 
lm78_i2c_detect(struct i2c_client * client,struct i2c_board_info * info)571310ec792SJean Delvare static int lm78_i2c_detect(struct i2c_client *client,
5720c6e9731SJean Delvare 			   struct i2c_board_info *info)
5738d5d45fbSJean Delvare {
5740c6e9731SJean Delvare 	int i;
57590534c5cSJean Delvare 	struct lm78_data *isa = lm78_data_if_isa();
5760c6e9731SJean Delvare 	const char *client_name;
5770c6e9731SJean Delvare 	struct i2c_adapter *adapter = client->adapter;
5780c6e9731SJean Delvare 	int address = client->addr;
5798d5d45fbSJean Delvare 
5800c6e9731SJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
5810c6e9731SJean Delvare 		return -ENODEV;
5828d5d45fbSJean Delvare 
5839b03079fSGuenter Roeck 	/*
5849b03079fSGuenter Roeck 	 * We block updates of the ISA device to minimize the risk of
5859b03079fSGuenter Roeck 	 * concurrent access to the same LM78 chip through different
5869b03079fSGuenter Roeck 	 * interfaces.
5879b03079fSGuenter Roeck 	 */
5880c6e9731SJean Delvare 	if (isa)
5890c6e9731SJean Delvare 		mutex_lock(&isa->update_lock);
5908d5d45fbSJean Delvare 
5910c6e9731SJean Delvare 	if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
59252df6440SJean Delvare 	 || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address)
5930c6e9731SJean Delvare 		goto err_nodev;
5940c6e9731SJean Delvare 
595ad3273beSJean Delvare 	/* Explicitly prevent the misdetection of Winbond chips */
5960c6e9731SJean Delvare 	i = i2c_smbus_read_byte_data(client, 0x4f);
5970c6e9731SJean Delvare 	if (i == 0xa3 || i == 0x5c)
5980c6e9731SJean Delvare 		goto err_nodev;
5998d5d45fbSJean Delvare 
6008d5d45fbSJean Delvare 	/* Determine the chip type. */
6010c6e9731SJean Delvare 	i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
60227fe048eSJean Delvare 	if (i == 0x00 || i == 0x20	/* LM78 */
60327fe048eSJean Delvare 	 || i == 0x40)			/* LM78-J */
60452df6440SJean Delvare 		client_name = "lm78";
6058d5d45fbSJean Delvare 	else if ((i & 0xfe) == 0xc0)
60652df6440SJean Delvare 		client_name = "lm79";
60752df6440SJean Delvare 	else
6080c6e9731SJean Delvare 		goto err_nodev;
60918c73f90SJean Delvare 
6100c6e9731SJean Delvare 	if (lm78_alias_detect(client, i)) {
611b55f3757SGuenter Roeck 		dev_dbg(&adapter->dev,
612b55f3757SGuenter Roeck 			"Device at 0x%02x appears to be the same as ISA device\n",
613b55f3757SGuenter Roeck 			address);
6140c6e9731SJean Delvare 		goto err_nodev;
61518c73f90SJean Delvare 	}
6168d5d45fbSJean Delvare 
6170c6e9731SJean Delvare 	if (isa)
6180c6e9731SJean Delvare 		mutex_unlock(&isa->update_lock);
6190c6e9731SJean Delvare 
620f2f394dbSWolfram Sang 	strscpy(info->type, client_name, I2C_NAME_SIZE);
6210c6e9731SJean Delvare 
6220c6e9731SJean Delvare 	return 0;
6230c6e9731SJean Delvare 
6240c6e9731SJean Delvare  err_nodev:
6250c6e9731SJean Delvare 	if (isa)
6260c6e9731SJean Delvare 		mutex_unlock(&isa->update_lock);
6270c6e9731SJean Delvare 	return -ENODEV;
6288d5d45fbSJean Delvare }
6298d5d45fbSJean Delvare 
63067487038SStephen Kitt static const struct i2c_device_id lm78_i2c_id[];
63167487038SStephen Kitt 
lm78_i2c_probe(struct i2c_client * client)63267487038SStephen Kitt static int lm78_i2c_probe(struct i2c_client *client)
6330c6e9731SJean Delvare {
6348eb40610SAxel Lin 	struct device *dev = &client->dev;
6358eb40610SAxel Lin 	struct device *hwmon_dev;
6360c6e9731SJean Delvare 	struct lm78_data *data;
6378d5d45fbSJean Delvare 
6388eb40610SAxel Lin 	data = devm_kzalloc(dev, sizeof(struct lm78_data), GFP_KERNEL);
6390c6e9731SJean Delvare 	if (!data)
6400c6e9731SJean Delvare 		return -ENOMEM;
6410c6e9731SJean Delvare 
6420c6e9731SJean Delvare 	data->client = client;
64367487038SStephen Kitt 	data->type = i2c_match_id(lm78_i2c_id, client)->driver_data;
6448d5d45fbSJean Delvare 
6458d5d45fbSJean Delvare 	/* Initialize the LM78 chip */
646c59cc301SJean Delvare 	lm78_init_device(data);
6478d5d45fbSJean Delvare 
6488eb40610SAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
6498eb40610SAxel Lin 							   data, lm78_groups);
6508eb40610SAxel Lin 	return PTR_ERR_OR_ZERO(hwmon_dev);
651c40769feSJean Delvare }
652c40769feSJean Delvare 
653ed4cebdfSJean Delvare static const struct i2c_device_id lm78_i2c_id[] = {
654ed4cebdfSJean Delvare 	{ "lm78", lm78 },
655ed4cebdfSJean Delvare 	{ "lm79", lm79 },
656ed4cebdfSJean Delvare 	{ }
657ed4cebdfSJean Delvare };
658ed4cebdfSJean Delvare MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
659c40769feSJean Delvare 
660ed4cebdfSJean Delvare static struct i2c_driver lm78_driver = {
661ed4cebdfSJean Delvare 	.class		= I2C_CLASS_HWMON,
662ed4cebdfSJean Delvare 	.driver = {
663ed4cebdfSJean Delvare 		.name	= "lm78",
664ed4cebdfSJean Delvare 	},
665*1975d167SUwe Kleine-König 	.probe		= lm78_i2c_probe,
666ed4cebdfSJean Delvare 	.id_table	= lm78_i2c_id,
667ed4cebdfSJean Delvare 	.detect		= lm78_i2c_detect,
668ed4cebdfSJean Delvare 	.address_list	= normal_i2c,
669ed4cebdfSJean Delvare };
6708d5d45fbSJean Delvare 
6719b03079fSGuenter Roeck /*
6729b03079fSGuenter Roeck  * The SMBus locks itself, but ISA access must be locked explicitly!
6739b03079fSGuenter Roeck  * We don't want to lock the whole ISA bus, so we lock each client
6749b03079fSGuenter Roeck  * separately.
6759b03079fSGuenter Roeck  * We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
6769b03079fSGuenter Roeck  * would slow down the LM78 access and should not be necessary.
6779b03079fSGuenter Roeck  */
lm78_read_value(struct lm78_data * data,u8 reg)678c59cc301SJean Delvare static int lm78_read_value(struct lm78_data *data, u8 reg)
6798d5d45fbSJean Delvare {
6800c6e9731SJean Delvare 	struct i2c_client *client = data->client;
681c59cc301SJean Delvare 
68290534c5cSJean Delvare #ifdef CONFIG_ISA
6830c6e9731SJean Delvare 	if (!client) { /* ISA device */
684c59cc301SJean Delvare 		int res;
6859a61bf63SIngo Molnar 		mutex_lock(&data->lock);
6866e1b5029SJean Delvare 		outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
6876e1b5029SJean Delvare 		res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET);
6889a61bf63SIngo Molnar 		mutex_unlock(&data->lock);
6898d5d45fbSJean Delvare 		return res;
6908d5d45fbSJean Delvare 	} else
69190534c5cSJean Delvare #endif
6928d5d45fbSJean Delvare 		return i2c_smbus_read_byte_data(client, reg);
6938d5d45fbSJean Delvare }
6948d5d45fbSJean Delvare 
lm78_write_value(struct lm78_data * data,u8 reg,u8 value)695c59cc301SJean Delvare static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
6968d5d45fbSJean Delvare {
6970c6e9731SJean Delvare 	struct i2c_client *client = data->client;
698c59cc301SJean Delvare 
69990534c5cSJean Delvare #ifdef CONFIG_ISA
7000c6e9731SJean Delvare 	if (!client) { /* ISA device */
7019a61bf63SIngo Molnar 		mutex_lock(&data->lock);
7026e1b5029SJean Delvare 		outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
7036e1b5029SJean Delvare 		outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET);
7049a61bf63SIngo Molnar 		mutex_unlock(&data->lock);
7058d5d45fbSJean Delvare 		return 0;
7068d5d45fbSJean Delvare 	} else
70790534c5cSJean Delvare #endif
7088d5d45fbSJean Delvare 		return i2c_smbus_write_byte_data(client, reg, value);
7098d5d45fbSJean Delvare }
7108d5d45fbSJean Delvare 
lm78_init_device(struct lm78_data * data)711c59cc301SJean Delvare static void lm78_init_device(struct lm78_data *data)
7128d5d45fbSJean Delvare {
713c40769feSJean Delvare 	u8 config;
714c40769feSJean Delvare 	int i;
7158d5d45fbSJean Delvare 
7168d5d45fbSJean Delvare 	/* Start monitoring */
717c59cc301SJean Delvare 	config = lm78_read_value(data, LM78_REG_CONFIG);
718c40769feSJean Delvare 	if ((config & 0x09) != 0x01)
719c59cc301SJean Delvare 		lm78_write_value(data, LM78_REG_CONFIG,
7208d5d45fbSJean Delvare 				 (config & 0xf7) | 0x01);
721c40769feSJean Delvare 
722c40769feSJean Delvare 	/* A few vars need to be filled upon startup */
723c40769feSJean Delvare 	for (i = 0; i < 3; i++) {
724c59cc301SJean Delvare 		data->fan_min[i] = lm78_read_value(data,
725c40769feSJean Delvare 					LM78_REG_FAN_MIN(i));
726c40769feSJean Delvare 	}
727c40769feSJean Delvare 
728c40769feSJean Delvare 	mutex_init(&data->update_lock);
7298d5d45fbSJean Delvare }
7308d5d45fbSJean Delvare 
lm78_update_device(struct device * dev)7318d5d45fbSJean Delvare static struct lm78_data *lm78_update_device(struct device *dev)
7328d5d45fbSJean Delvare {
733c40769feSJean Delvare 	struct lm78_data *data = dev_get_drvdata(dev);
7348d5d45fbSJean Delvare 	int i;
7358d5d45fbSJean Delvare 
7369a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
7378d5d45fbSJean Delvare 
7388d5d45fbSJean Delvare 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
7398d5d45fbSJean Delvare 	    || !data->valid) {
7408d5d45fbSJean Delvare 
741c40769feSJean Delvare 		dev_dbg(dev, "Starting lm78 update\n");
7428d5d45fbSJean Delvare 
7438d5d45fbSJean Delvare 		for (i = 0; i <= 6; i++) {
7448d5d45fbSJean Delvare 			data->in[i] =
745c59cc301SJean Delvare 			    lm78_read_value(data, LM78_REG_IN(i));
7468d5d45fbSJean Delvare 			data->in_min[i] =
747c59cc301SJean Delvare 			    lm78_read_value(data, LM78_REG_IN_MIN(i));
7488d5d45fbSJean Delvare 			data->in_max[i] =
749c59cc301SJean Delvare 			    lm78_read_value(data, LM78_REG_IN_MAX(i));
7508d5d45fbSJean Delvare 		}
7518d5d45fbSJean Delvare 		for (i = 0; i < 3; i++) {
7528d5d45fbSJean Delvare 			data->fan[i] =
753c59cc301SJean Delvare 			    lm78_read_value(data, LM78_REG_FAN(i));
7548d5d45fbSJean Delvare 			data->fan_min[i] =
755c59cc301SJean Delvare 			    lm78_read_value(data, LM78_REG_FAN_MIN(i));
7568d5d45fbSJean Delvare 		}
757c59cc301SJean Delvare 		data->temp = lm78_read_value(data, LM78_REG_TEMP);
7588d5d45fbSJean Delvare 		data->temp_over =
759c59cc301SJean Delvare 		    lm78_read_value(data, LM78_REG_TEMP_OVER);
7608d5d45fbSJean Delvare 		data->temp_hyst =
761c59cc301SJean Delvare 		    lm78_read_value(data, LM78_REG_TEMP_HYST);
762c59cc301SJean Delvare 		i = lm78_read_value(data, LM78_REG_VID_FANDIV);
7638d5d45fbSJean Delvare 		data->vid = i & 0x0f;
7648d5d45fbSJean Delvare 		if (data->type == lm79)
7658d5d45fbSJean Delvare 			data->vid |=
766c59cc301SJean Delvare 			    (lm78_read_value(data, LM78_REG_CHIPID) &
7678d5d45fbSJean Delvare 			     0x01) << 4;
7688d5d45fbSJean Delvare 		else
7698d5d45fbSJean Delvare 			data->vid |= 0x10;
7708d5d45fbSJean Delvare 		data->fan_div[0] = (i >> 4) & 0x03;
7718d5d45fbSJean Delvare 		data->fan_div[1] = i >> 6;
772c59cc301SJean Delvare 		data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
773c59cc301SJean Delvare 		    (lm78_read_value(data, LM78_REG_ALARM2) << 8);
7748d5d45fbSJean Delvare 		data->last_updated = jiffies;
775952a11caSPaul Fertser 		data->valid = true;
7768d5d45fbSJean Delvare 
7778d5d45fbSJean Delvare 		data->fan_div[2] = 1;
7788d5d45fbSJean Delvare 	}
7798d5d45fbSJean Delvare 
7809a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
7818d5d45fbSJean Delvare 
7828d5d45fbSJean Delvare 	return data;
7838d5d45fbSJean Delvare }
7848d5d45fbSJean Delvare 
78590534c5cSJean Delvare #ifdef CONFIG_ISA
lm78_isa_probe(struct platform_device * pdev)7866c931ae1SBill Pemberton static int lm78_isa_probe(struct platform_device *pdev)
787ed4cebdfSJean Delvare {
7888eb40610SAxel Lin 	struct device *dev = &pdev->dev;
7898eb40610SAxel Lin 	struct device *hwmon_dev;
790ed4cebdfSJean Delvare 	struct lm78_data *data;
791ed4cebdfSJean Delvare 	struct resource *res;
792ed4cebdfSJean Delvare 
793ed4cebdfSJean Delvare 	/* Reserve the ISA region */
794ed4cebdfSJean Delvare 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
7958eb40610SAxel Lin 	if (!devm_request_region(dev, res->start + LM78_ADDR_REG_OFFSET,
7967b7bb90cSGuenter Roeck 				 2, "lm78"))
7977b7bb90cSGuenter Roeck 		return -EBUSY;
798ed4cebdfSJean Delvare 
7998eb40610SAxel Lin 	data = devm_kzalloc(dev, sizeof(struct lm78_data), GFP_KERNEL);
8007b7bb90cSGuenter Roeck 	if (!data)
8017b7bb90cSGuenter Roeck 		return -ENOMEM;
8027b7bb90cSGuenter Roeck 
803ed4cebdfSJean Delvare 	mutex_init(&data->lock);
804ed4cebdfSJean Delvare 	data->isa_addr = res->start;
805ed4cebdfSJean Delvare 	platform_set_drvdata(pdev, data);
806ed4cebdfSJean Delvare 
807ed4cebdfSJean Delvare 	if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
808ed4cebdfSJean Delvare 		data->type = lm79;
809ed4cebdfSJean Delvare 		data->name = "lm79";
810ed4cebdfSJean Delvare 	} else {
811ed4cebdfSJean Delvare 		data->type = lm78;
812ed4cebdfSJean Delvare 		data->name = "lm78";
813ed4cebdfSJean Delvare 	}
814ed4cebdfSJean Delvare 
815ed4cebdfSJean Delvare 	/* Initialize the LM78 chip */
816ed4cebdfSJean Delvare 	lm78_init_device(data);
817ed4cebdfSJean Delvare 
8188eb40610SAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
8198eb40610SAxel Lin 							   data, lm78_groups);
8208eb40610SAxel Lin 	return PTR_ERR_OR_ZERO(hwmon_dev);
821ed4cebdfSJean Delvare }
822ed4cebdfSJean Delvare 
823ed4cebdfSJean Delvare static struct platform_driver lm78_isa_driver = {
824ed4cebdfSJean Delvare 	.driver = {
825ed4cebdfSJean Delvare 		.name	= "lm78",
826ed4cebdfSJean Delvare 	},
827ed4cebdfSJean Delvare 	.probe		= lm78_isa_probe,
828ed4cebdfSJean Delvare };
829ed4cebdfSJean Delvare 
830c40769feSJean Delvare /* return 1 if a supported chip is found, 0 otherwise */
lm78_isa_found(unsigned short address)831c40769feSJean Delvare static int __init lm78_isa_found(unsigned short address)
832c40769feSJean Delvare {
833c40769feSJean Delvare 	int val, save, found = 0;
834197027e6SJean Delvare 	int port;
835c40769feSJean Delvare 
8369b03079fSGuenter Roeck 	/*
8379b03079fSGuenter Roeck 	 * Some boards declare base+0 to base+7 as a PNP device, some base+4
838197027e6SJean Delvare 	 * to base+7 and some base+5 to base+6. So we better request each port
8399b03079fSGuenter Roeck 	 * individually for the probing phase.
8409b03079fSGuenter Roeck 	 */
841197027e6SJean Delvare 	for (port = address; port < address + LM78_EXTENT; port++) {
842197027e6SJean Delvare 		if (!request_region(port, 1, "lm78")) {
843ce47da74SJoe Perches 			pr_debug("Failed to request port 0x%x\n", port);
844197027e6SJean Delvare 			goto release;
84547c15532SJean Delvare 		}
84647c15532SJean Delvare 	}
847c40769feSJean Delvare 
848c40769feSJean Delvare #define REALLY_SLOW_IO
8499b03079fSGuenter Roeck 	/*
8509b03079fSGuenter Roeck 	 * We need the timeouts for at least some LM78-like
8519b03079fSGuenter Roeck 	 * chips. But only if we read 'undefined' registers.
8529b03079fSGuenter Roeck 	 */
853c40769feSJean Delvare 	val = inb_p(address + 1);
854c40769feSJean Delvare 	if (inb_p(address + 2) != val
855c40769feSJean Delvare 	 || inb_p(address + 3) != val
856c40769feSJean Delvare 	 || inb_p(address + 7) != val)
857c40769feSJean Delvare 		goto release;
858c40769feSJean Delvare #undef REALLY_SLOW_IO
859c40769feSJean Delvare 
8609b03079fSGuenter Roeck 	/*
8619b03079fSGuenter Roeck 	 * We should be able to change the 7 LSB of the address port. The
8629b03079fSGuenter Roeck 	 * MSB (busy flag) should be clear initially, set after the write.
8639b03079fSGuenter Roeck 	 */
864c40769feSJean Delvare 	save = inb_p(address + LM78_ADDR_REG_OFFSET);
865c40769feSJean Delvare 	if (save & 0x80)
866c40769feSJean Delvare 		goto release;
867c40769feSJean Delvare 	val = ~save & 0x7f;
868c40769feSJean Delvare 	outb_p(val, address + LM78_ADDR_REG_OFFSET);
869c40769feSJean Delvare 	if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
870c40769feSJean Delvare 		outb_p(save, address + LM78_ADDR_REG_OFFSET);
871c40769feSJean Delvare 		goto release;
872c40769feSJean Delvare 	}
873c40769feSJean Delvare 
874c40769feSJean Delvare 	/* We found a device, now see if it could be an LM78 */
875c40769feSJean Delvare 	outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
876c40769feSJean Delvare 	val = inb_p(address + LM78_DATA_REG_OFFSET);
877c40769feSJean Delvare 	if (val & 0x80)
878c40769feSJean Delvare 		goto release;
879c40769feSJean Delvare 	outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
880c40769feSJean Delvare 	val = inb_p(address + LM78_DATA_REG_OFFSET);
881c40769feSJean Delvare 	if (val < 0x03 || val > 0x77)	/* Not a valid I2C address */
882c40769feSJean Delvare 		goto release;
883c40769feSJean Delvare 
884c40769feSJean Delvare 	/* The busy flag should be clear again */
885c40769feSJean Delvare 	if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
886c40769feSJean Delvare 		goto release;
887c40769feSJean Delvare 
888c40769feSJean Delvare 	/* Explicitly prevent the misdetection of Winbond chips */
889c40769feSJean Delvare 	outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
890c40769feSJean Delvare 	val = inb_p(address + LM78_DATA_REG_OFFSET);
891c40769feSJean Delvare 	if (val == 0xa3 || val == 0x5c)
892c40769feSJean Delvare 		goto release;
893c40769feSJean Delvare 
894c40769feSJean Delvare 	/* Explicitly prevent the misdetection of ITE chips */
895c40769feSJean Delvare 	outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
896c40769feSJean Delvare 	val = inb_p(address + LM78_DATA_REG_OFFSET);
897c40769feSJean Delvare 	if (val == 0x90)
898c40769feSJean Delvare 		goto release;
899c40769feSJean Delvare 
900c40769feSJean Delvare 	/* Determine the chip type */
901c40769feSJean Delvare 	outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
902c40769feSJean Delvare 	val = inb_p(address + LM78_DATA_REG_OFFSET);
903acf346a3SHans de Goede 	if (val == 0x00 || val == 0x20	/* LM78 */
904c40769feSJean Delvare 	 || val == 0x40			/* LM78-J */
905c40769feSJean Delvare 	 || (val & 0xfe) == 0xc0)	/* LM79 */
906c40769feSJean Delvare 		found = 1;
907c40769feSJean Delvare 
908c40769feSJean Delvare 	if (found)
909ce47da74SJoe Perches 		pr_info("Found an %s chip at %#x\n",
910c40769feSJean Delvare 			val & 0x80 ? "LM79" : "LM78", (int)address);
911c40769feSJean Delvare 
912c40769feSJean Delvare  release:
913197027e6SJean Delvare 	for (port--; port >= address; port--)
914197027e6SJean Delvare 		release_region(port, 1);
915c40769feSJean Delvare 	return found;
916c40769feSJean Delvare }
917c40769feSJean Delvare 
lm78_isa_device_add(unsigned short address)918c40769feSJean Delvare static int __init lm78_isa_device_add(unsigned short address)
919c40769feSJean Delvare {
920c40769feSJean Delvare 	struct resource res = {
921c40769feSJean Delvare 		.start	= address,
92215bde2f1SJean Delvare 		.end	= address + LM78_EXTENT - 1,
923c40769feSJean Delvare 		.name	= "lm78",
924c40769feSJean Delvare 		.flags	= IORESOURCE_IO,
925c40769feSJean Delvare 	};
926c40769feSJean Delvare 	int err;
927c40769feSJean Delvare 
928c40769feSJean Delvare 	pdev = platform_device_alloc("lm78", address);
929c40769feSJean Delvare 	if (!pdev) {
930c40769feSJean Delvare 		err = -ENOMEM;
931ce47da74SJoe Perches 		pr_err("Device allocation failed\n");
932c40769feSJean Delvare 		goto exit;
933c40769feSJean Delvare 	}
934c40769feSJean Delvare 
935c40769feSJean Delvare 	err = platform_device_add_resources(pdev, &res, 1);
936c40769feSJean Delvare 	if (err) {
937ce47da74SJoe Perches 		pr_err("Device resource addition failed (%d)\n", err);
938c40769feSJean Delvare 		goto exit_device_put;
939c40769feSJean Delvare 	}
940c40769feSJean Delvare 
941c40769feSJean Delvare 	err = platform_device_add(pdev);
942c40769feSJean Delvare 	if (err) {
943ce47da74SJoe Perches 		pr_err("Device addition failed (%d)\n", err);
944c40769feSJean Delvare 		goto exit_device_put;
945c40769feSJean Delvare 	}
946c40769feSJean Delvare 
947c40769feSJean Delvare 	return 0;
948c40769feSJean Delvare 
949c40769feSJean Delvare  exit_device_put:
950c40769feSJean Delvare 	platform_device_put(pdev);
951c40769feSJean Delvare  exit:
952c40769feSJean Delvare 	pdev = NULL;
953c40769feSJean Delvare 	return err;
954c40769feSJean Delvare }
955c40769feSJean Delvare 
lm78_isa_register(void)95690534c5cSJean Delvare static int __init lm78_isa_register(void)
9578d5d45fbSJean Delvare {
958fde09509SJean Delvare 	int res;
959fde09509SJean Delvare 
960c40769feSJean Delvare 	if (lm78_isa_found(isa_address)) {
961c40769feSJean Delvare 		res = platform_driver_register(&lm78_isa_driver);
962c40769feSJean Delvare 		if (res)
96318c73f90SJean Delvare 			goto exit;
964c40769feSJean Delvare 
965c40769feSJean Delvare 		/* Sets global pdev as a side effect */
966c40769feSJean Delvare 		res = lm78_isa_device_add(isa_address);
967c40769feSJean Delvare 		if (res)
968c40769feSJean Delvare 			goto exit_unreg_isa_driver;
969c40769feSJean Delvare 	}
970fde09509SJean Delvare 
97190534c5cSJean Delvare 	return 0;
97290534c5cSJean Delvare 
97390534c5cSJean Delvare  exit_unreg_isa_driver:
97490534c5cSJean Delvare 	platform_driver_unregister(&lm78_isa_driver);
97590534c5cSJean Delvare  exit:
97690534c5cSJean Delvare 	return res;
97790534c5cSJean Delvare }
97890534c5cSJean Delvare 
lm78_isa_unregister(void)97990534c5cSJean Delvare static void lm78_isa_unregister(void)
98090534c5cSJean Delvare {
98190534c5cSJean Delvare 	if (pdev) {
98290534c5cSJean Delvare 		platform_device_unregister(pdev);
98390534c5cSJean Delvare 		platform_driver_unregister(&lm78_isa_driver);
98490534c5cSJean Delvare 	}
98590534c5cSJean Delvare }
98690534c5cSJean Delvare #else /* !CONFIG_ISA */
98790534c5cSJean Delvare 
lm78_isa_register(void)98890534c5cSJean Delvare static int __init lm78_isa_register(void)
98990534c5cSJean Delvare {
99090534c5cSJean Delvare 	return 0;
99190534c5cSJean Delvare }
99290534c5cSJean Delvare 
lm78_isa_unregister(void)99390534c5cSJean Delvare static void lm78_isa_unregister(void)
99490534c5cSJean Delvare {
99590534c5cSJean Delvare }
99690534c5cSJean Delvare #endif /* CONFIG_ISA */
99790534c5cSJean Delvare 
sm_lm78_init(void)99890534c5cSJean Delvare static int __init sm_lm78_init(void)
99990534c5cSJean Delvare {
100090534c5cSJean Delvare 	int res;
100190534c5cSJean Delvare 
10029b03079fSGuenter Roeck 	/*
10039b03079fSGuenter Roeck 	 * We register the ISA device first, so that we can skip the
10049b03079fSGuenter Roeck 	 * registration of an I2C interface to the same device.
10059b03079fSGuenter Roeck 	 */
100690534c5cSJean Delvare 	res = lm78_isa_register();
100790534c5cSJean Delvare 	if (res)
100890534c5cSJean Delvare 		goto exit;
100990534c5cSJean Delvare 
101018c73f90SJean Delvare 	res = i2c_add_driver(&lm78_driver);
101118c73f90SJean Delvare 	if (res)
101218c73f90SJean Delvare 		goto exit_unreg_isa_device;
101318c73f90SJean Delvare 
1014fde09509SJean Delvare 	return 0;
1015c40769feSJean Delvare 
101618c73f90SJean Delvare  exit_unreg_isa_device:
101790534c5cSJean Delvare 	lm78_isa_unregister();
1018c40769feSJean Delvare  exit:
1019c40769feSJean Delvare 	return res;
10208d5d45fbSJean Delvare }
10218d5d45fbSJean Delvare 
sm_lm78_exit(void)10228d5d45fbSJean Delvare static void __exit sm_lm78_exit(void)
10238d5d45fbSJean Delvare {
102490534c5cSJean Delvare 	lm78_isa_unregister();
10258d5d45fbSJean Delvare 	i2c_del_driver(&lm78_driver);
10268d5d45fbSJean Delvare }
10278d5d45fbSJean Delvare 
10287c81c60fSJean Delvare MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <jdelvare@suse.de>");
102927fe048eSJean Delvare MODULE_DESCRIPTION("LM78/LM79 driver");
10308d5d45fbSJean Delvare MODULE_LICENSE("GPL");
10318d5d45fbSJean Delvare 
10328d5d45fbSJean Delvare module_init(sm_lm78_init);
10338d5d45fbSJean Delvare module_exit(sm_lm78_exit);
1034