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