blob: ed7859f0e16a952c8b63cefc376e0edb92d56934 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 lm78.c - Part of lm_sensors, Linux kernel modules for hardware
3 monitoring
4 Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
Jean Delvarec40769f2007-05-08 17:22:00 +02005 Copyright (c) 2007 Jean Delvare <khali@linux-fr.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/slab.h>
25#include <linux/jiffies.h>
26#include <linux/i2c.h>
Jean Delvarec40769f2007-05-08 17:22:00 +020027#include <linux/platform_device.h>
28#include <linux/ioport.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040029#include <linux/hwmon.h>
Jean Delvare19f673e2005-07-31 22:12:09 +020030#include <linux/hwmon-vid.h>
Jean Delvare247dde42007-05-08 17:22:01 +020031#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040032#include <linux/err.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010033#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <asm/io.h>
35
Jean Delvarec40769f2007-05-08 17:22:00 +020036/* ISA device, if found */
37static struct platform_device *pdev;
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039/* Addresses to scan */
Mark M. Hoffman25e9c862008-02-17 22:28:03 -050040static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
41 0x2e, 0x2f, I2C_CLIENT_END };
Jean Delvare2d8672c2005-07-19 23:56:35 +020042static unsigned short isa_address = 0x290;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44/* Insmod parameters */
Jean Delvaref4b50262005-07-31 21:49:03 +020045I2C_CLIENT_INSMOD_2(lm78, lm79);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47/* Many LM78 constants specified below */
48
49/* Length of ISA address segment */
50#define LM78_EXTENT 8
51
52/* Where are the ISA address/data registers relative to the base address */
53#define LM78_ADDR_REG_OFFSET 5
54#define LM78_DATA_REG_OFFSET 6
55
56/* The LM78 registers */
57#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
58#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
59#define LM78_REG_IN(nr) (0x20 + (nr))
60
61#define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
62#define LM78_REG_FAN(nr) (0x28 + (nr))
63
64#define LM78_REG_TEMP 0x27
65#define LM78_REG_TEMP_OVER 0x39
66#define LM78_REG_TEMP_HYST 0x3a
67
68#define LM78_REG_ALARM1 0x41
69#define LM78_REG_ALARM2 0x42
70
71#define LM78_REG_VID_FANDIV 0x47
72
73#define LM78_REG_CONFIG 0x40
74#define LM78_REG_CHIPID 0x49
75#define LM78_REG_I2C_ADDR 0x48
76
77
78/* Conversions. Rounding and limit checking is only done on the TO_REG
79 variants. */
80
81/* IN: mV, (0V to 4.08V)
82 REG: 16mV/bit */
83static inline u8 IN_TO_REG(unsigned long val)
84{
85 unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
86 return (nval + 8) / 16;
87}
88#define IN_FROM_REG(val) ((val) * 16)
89
90static inline u8 FAN_TO_REG(long rpm, int div)
91{
92 if (rpm <= 0)
93 return 255;
94 return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
95}
96
97static inline int FAN_FROM_REG(u8 val, int div)
98{
99 return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
100}
101
102/* TEMP: mC (-128C to +127C)
103 REG: 1C/bit, two's complement */
104static inline s8 TEMP_TO_REG(int val)
105{
106 int nval = SENSORS_LIMIT(val, -128000, 127000) ;
107 return nval<0 ? (nval-500)/1000 : (nval+500)/1000;
108}
109
110static inline int TEMP_FROM_REG(s8 val)
111{
112 return val * 1000;
113}
114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115#define DIV_FROM_REG(val) (1 << (val))
116
117/* There are some complications in a module like this. First off, LM78 chips
118 may be both present on the SMBus and the ISA bus, and we have to handle
119 those cases separately at some places. Second, there might be several
120 LM78 chips available (well, actually, that is probably never done; but
121 it is a clean illustration of how to handle a case like that). Finally,
122 a specific chip may be attached to *both* ISA and SMBus, and we would
123 not like to detect it double. Fortunately, in the case of the LM78 at
124 least, a register tells us what SMBus address we are on, so that helps
125 a bit - except if there could be more than one SMBus. Groan. No solution
126 for this yet. */
127
Jean Delvarec40769f2007-05-08 17:22:00 +0200128/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
129 the driver field to differentiate between I2C and ISA chips. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130struct lm78_data {
131 struct i2c_client client;
Tony Jones1beeffe2007-08-20 13:46:20 -0700132 struct device *hwmon_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100133 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 enum chips type;
135
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100136 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 char valid; /* !=0 if following fields are valid */
138 unsigned long last_updated; /* In jiffies */
139
140 u8 in[7]; /* Register value */
141 u8 in_max[7]; /* Register value */
142 u8 in_min[7]; /* Register value */
143 u8 fan[3]; /* Register value */
144 u8 fan_min[3]; /* Register value */
145 s8 temp; /* Register value */
146 s8 temp_over; /* Register value */
147 s8 temp_hyst; /* Register value */
148 u8 fan_div[3]; /* Register encoding, shifted right */
149 u8 vid; /* Register encoding, combined */
150 u16 alarms; /* Register encoding, combined */
151};
152
153
154static int lm78_attach_adapter(struct i2c_adapter *adapter);
155static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
156static int lm78_detach_client(struct i2c_client *client);
157
Jean Delvarec40769f2007-05-08 17:22:00 +0200158static int __devinit lm78_isa_probe(struct platform_device *pdev);
159static int __devexit lm78_isa_remove(struct platform_device *pdev);
160
Jean Delvarec59cc302007-05-08 17:22:01 +0200161static int lm78_read_value(struct lm78_data *data, u8 reg);
162static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163static struct lm78_data *lm78_update_device(struct device *dev);
Jean Delvarec59cc302007-05-08 17:22:01 +0200164static void lm78_init_device(struct lm78_data *data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
166
167static struct i2c_driver lm78_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100168 .driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100169 .name = "lm78",
170 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 .attach_adapter = lm78_attach_adapter,
172 .detach_client = lm78_detach_client,
173};
174
Jean Delvarec40769f2007-05-08 17:22:00 +0200175static struct platform_driver lm78_isa_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100176 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200177 .owner = THIS_MODULE,
Jean Delvarec40769f2007-05-08 17:22:00 +0200178 .name = "lm78",
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100179 },
Jean Delvarec40769f2007-05-08 17:22:00 +0200180 .probe = lm78_isa_probe,
181 .remove = lm78_isa_remove,
Jean Delvarefde09502005-07-19 23:51:07 +0200182};
183
184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185/* 7 Voltages */
Jean Delvare247dde42007-05-08 17:22:01 +0200186static ssize_t show_in(struct device *dev, struct device_attribute *da,
187 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
Jean Delvare247dde42007-05-08 17:22:01 +0200189 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200191 return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
193
Jean Delvare247dde42007-05-08 17:22:01 +0200194static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
195 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
Jean Delvare247dde42007-05-08 17:22:01 +0200197 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200199 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
Jean Delvare247dde42007-05-08 17:22:01 +0200202static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
203 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
Jean Delvare247dde42007-05-08 17:22:01 +0200205 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200207 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208}
209
Jean Delvare247dde42007-05-08 17:22:01 +0200210static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
211 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
Jean Delvare247dde42007-05-08 17:22:01 +0200213 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200214 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200216 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100218 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 data->in_min[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200220 lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100221 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 return count;
223}
224
Jean Delvare247dde42007-05-08 17:22:01 +0200225static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
226 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
Jean Delvare247dde42007-05-08 17:22:01 +0200228 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200229 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200231 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100233 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 data->in_max[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200235 lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100236 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return count;
238}
239
240#define show_in_offset(offset) \
Jean Delvare247dde42007-05-08 17:22:01 +0200241static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
242 show_in, NULL, offset); \
243static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
244 show_in_min, set_in_min, offset); \
245static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
246 show_in_max, set_in_max, offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248show_in_offset(0);
249show_in_offset(1);
250show_in_offset(2);
251show_in_offset(3);
252show_in_offset(4);
253show_in_offset(5);
254show_in_offset(6);
255
256/* Temperature */
Jean Delvare247dde42007-05-08 17:22:01 +0200257static ssize_t show_temp(struct device *dev, struct device_attribute *da,
258 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
260 struct lm78_data *data = lm78_update_device(dev);
261 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
262}
263
Jean Delvare247dde42007-05-08 17:22:01 +0200264static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
265 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266{
267 struct lm78_data *data = lm78_update_device(dev);
268 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
269}
270
Jean Delvare247dde42007-05-08 17:22:01 +0200271static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
272 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
Jean Delvarec40769f2007-05-08 17:22:00 +0200274 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 long val = simple_strtol(buf, NULL, 10);
276
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100277 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 data->temp_over = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200279 lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100280 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 return count;
282}
283
Jean Delvare247dde42007-05-08 17:22:01 +0200284static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
285 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
287 struct lm78_data *data = lm78_update_device(dev);
288 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
289}
290
Jean Delvare247dde42007-05-08 17:22:01 +0200291static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
292 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293{
Jean Delvarec40769f2007-05-08 17:22:00 +0200294 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 long val = simple_strtol(buf, NULL, 10);
296
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100297 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 data->temp_hyst = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200299 lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100300 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 return count;
302}
303
304static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
305static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
306 show_temp_over, set_temp_over);
307static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
308 show_temp_hyst, set_temp_hyst);
309
310/* 3 Fans */
Jean Delvare247dde42007-05-08 17:22:01 +0200311static ssize_t show_fan(struct device *dev, struct device_attribute *da,
312 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
Jean Delvare247dde42007-05-08 17:22:01 +0200314 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200316 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
318 DIV_FROM_REG(data->fan_div[nr])) );
319}
320
Jean Delvare247dde42007-05-08 17:22:01 +0200321static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
322 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
Jean Delvare247dde42007-05-08 17:22:01 +0200324 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200326 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
328 DIV_FROM_REG(data->fan_div[nr])) );
329}
330
Jean Delvare247dde42007-05-08 17:22:01 +0200331static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
332 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Jean Delvare247dde42007-05-08 17:22:01 +0200334 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200335 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200336 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 unsigned long val = simple_strtoul(buf, NULL, 10);
338
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100339 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200341 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100342 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 return count;
344}
345
Jean Delvare247dde42007-05-08 17:22:01 +0200346static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
347 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
Jean Delvare247dde42007-05-08 17:22:01 +0200349 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200351 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352}
353
354/* Note: we save and restore the fan minimum here, because its value is
355 determined in part by the fan divisor. This follows the principle of
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200356 least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 because the divisor changed. */
Jean Delvare247dde42007-05-08 17:22:01 +0200358static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
359 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
Jean Delvare247dde42007-05-08 17:22:01 +0200361 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200362 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200363 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 unsigned long val = simple_strtoul(buf, NULL, 10);
365 unsigned long min;
366 u8 reg;
367
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100368 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 min = FAN_FROM_REG(data->fan_min[nr],
370 DIV_FROM_REG(data->fan_div[nr]));
371
372 switch (val) {
373 case 1: data->fan_div[nr] = 0; break;
374 case 2: data->fan_div[nr] = 1; break;
375 case 4: data->fan_div[nr] = 2; break;
376 case 8: data->fan_div[nr] = 3; break;
377 default:
Jean Delvarec40769f2007-05-08 17:22:00 +0200378 dev_err(dev, "fan_div value %ld not "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 "supported. Choose one of 1, 2, 4 or 8!\n", val);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100380 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 return -EINVAL;
382 }
383
Jean Delvarec59cc302007-05-08 17:22:01 +0200384 reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 switch (nr) {
386 case 0:
387 reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
388 break;
389 case 1:
390 reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
391 break;
392 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200393 lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
395 data->fan_min[nr] =
396 FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200397 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100398 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399
400 return count;
401}
402
Jean Delvare247dde42007-05-08 17:22:01 +0200403#define show_fan_offset(offset) \
404static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
405 show_fan, NULL, offset - 1); \
406static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
407 show_fan_min, set_fan_min, offset - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409show_fan_offset(1);
410show_fan_offset(2);
411show_fan_offset(3);
412
413/* Fan 3 divisor is locked in H/W */
Jean Delvare247dde42007-05-08 17:22:01 +0200414static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
415 show_fan_div, set_fan_div, 0);
416static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
417 show_fan_div, set_fan_div, 1);
418static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420/* VID */
Jean Delvare247dde42007-05-08 17:22:01 +0200421static ssize_t show_vid(struct device *dev, struct device_attribute *da,
422 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
424 struct lm78_data *data = lm78_update_device(dev);
Jean Delvared0d3cd62005-11-23 15:44:26 -0800425 return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
428
429/* Alarms */
Jean Delvare247dde42007-05-08 17:22:01 +0200430static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
431 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432{
433 struct lm78_data *data = lm78_update_device(dev);
434 return sprintf(buf, "%u\n", data->alarms);
435}
436static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
437
Jean Delvare428a7032007-09-04 23:25:33 +0200438static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
439 char *buf)
440{
441 struct lm78_data *data = lm78_update_device(dev);
442 int nr = to_sensor_dev_attr(da)->index;
443 return sprintf(buf, "%u\n", (data->alarms >> nr) & 1);
444}
445static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
446static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
447static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
448static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
449static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
450static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9);
451static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10);
452static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
453static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
454static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
455static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
456
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457/* This function is called when:
458 * lm78_driver is inserted (when this module is loaded), for each
459 available adapter
460 * when a new adapter is inserted (and lm78_driver is still present) */
461static int lm78_attach_adapter(struct i2c_adapter *adapter)
462{
463 if (!(adapter->class & I2C_CLASS_HWMON))
464 return 0;
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200465 return i2c_probe(adapter, &addr_data, lm78_detect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466}
467
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200468static struct attribute *lm78_attributes[] = {
Jean Delvare247dde42007-05-08 17:22:01 +0200469 &sensor_dev_attr_in0_input.dev_attr.attr,
470 &sensor_dev_attr_in0_min.dev_attr.attr,
471 &sensor_dev_attr_in0_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200472 &sensor_dev_attr_in0_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200473 &sensor_dev_attr_in1_input.dev_attr.attr,
474 &sensor_dev_attr_in1_min.dev_attr.attr,
475 &sensor_dev_attr_in1_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200476 &sensor_dev_attr_in1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200477 &sensor_dev_attr_in2_input.dev_attr.attr,
478 &sensor_dev_attr_in2_min.dev_attr.attr,
479 &sensor_dev_attr_in2_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200480 &sensor_dev_attr_in2_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200481 &sensor_dev_attr_in3_input.dev_attr.attr,
482 &sensor_dev_attr_in3_min.dev_attr.attr,
483 &sensor_dev_attr_in3_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200484 &sensor_dev_attr_in3_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200485 &sensor_dev_attr_in4_input.dev_attr.attr,
486 &sensor_dev_attr_in4_min.dev_attr.attr,
487 &sensor_dev_attr_in4_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200488 &sensor_dev_attr_in4_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200489 &sensor_dev_attr_in5_input.dev_attr.attr,
490 &sensor_dev_attr_in5_min.dev_attr.attr,
491 &sensor_dev_attr_in5_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200492 &sensor_dev_attr_in5_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200493 &sensor_dev_attr_in6_input.dev_attr.attr,
494 &sensor_dev_attr_in6_min.dev_attr.attr,
495 &sensor_dev_attr_in6_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200496 &sensor_dev_attr_in6_alarm.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200497 &dev_attr_temp1_input.attr,
498 &dev_attr_temp1_max.attr,
499 &dev_attr_temp1_max_hyst.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200500 &sensor_dev_attr_temp1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200501 &sensor_dev_attr_fan1_input.dev_attr.attr,
502 &sensor_dev_attr_fan1_min.dev_attr.attr,
503 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200504 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200505 &sensor_dev_attr_fan2_input.dev_attr.attr,
506 &sensor_dev_attr_fan2_min.dev_attr.attr,
507 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200508 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200509 &sensor_dev_attr_fan3_input.dev_attr.attr,
510 &sensor_dev_attr_fan3_min.dev_attr.attr,
511 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200512 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200513 &dev_attr_alarms.attr,
514 &dev_attr_cpu0_vid.attr,
515
516 NULL
517};
518
519static const struct attribute_group lm78_group = {
520 .attrs = lm78_attributes,
521};
522
Jean Delvarec40769f2007-05-08 17:22:00 +0200523/* I2C devices get this name attribute automatically, but for ISA devices
524 we must create it by ourselves. */
525static ssize_t show_name(struct device *dev, struct device_attribute
526 *devattr, char *buf)
527{
528 struct lm78_data *data = dev_get_drvdata(dev);
529
530 return sprintf(buf, "%s\n", data->client.name);
531}
532static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
533
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200534/* This function is called by i2c_probe */
Ben Dooksd8d20612005-10-26 21:05:46 +0200535static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
537 int i, err;
538 struct i2c_client *new_client;
539 struct lm78_data *data;
540 const char *client_name = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
Jean Delvarec40769f2007-05-08 17:22:00 +0200542 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 err = -ENODEV;
Jean Delvarec40769f2007-05-08 17:22:00 +0200544 goto ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
546
547 /* OK. For now, we presume we have a valid client. We now create the
548 client structure, even though we cannot fill it completely yet.
549 But it allows us to access lm78_{read,write}_value. */
550
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200551 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 err = -ENOMEM;
553 goto ERROR1;
554 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556 new_client = &data->client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 i2c_set_clientdata(new_client, data);
558 new_client->addr = address;
559 new_client->adapter = adapter;
Jean Delvarec40769f2007-05-08 17:22:00 +0200560 new_client->driver = &lm78_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562 /* Now, we do the remaining detection. */
563 if (kind < 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200564 if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 err = -ENODEV;
566 goto ERROR2;
567 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200568 if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
Jean Delvarec40769f2007-05-08 17:22:00 +0200569 address) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 err = -ENODEV;
571 goto ERROR2;
572 }
573 }
574
575 /* Determine the chip type. */
576 if (kind <= 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200577 i = lm78_read_value(data, LM78_REG_CHIPID);
Jean Delvare27fe0482005-07-27 21:30:16 +0200578 if (i == 0x00 || i == 0x20 /* LM78 */
579 || i == 0x40) /* LM78-J */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 kind = lm78;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 else if ((i & 0xfe) == 0xc0)
582 kind = lm79;
583 else {
584 if (kind == 0)
585 dev_warn(&adapter->dev, "Ignoring 'force' "
586 "parameter for unknown chip at "
587 "adapter %d, address 0x%02x\n",
588 i2c_adapter_id(adapter), address);
589 err = -ENODEV;
590 goto ERROR2;
591 }
592 }
593
594 if (kind == lm78) {
595 client_name = "lm78";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 } else if (kind == lm79) {
597 client_name = "lm79";
598 }
599
600 /* Fill in the remaining client fields and put into the global list */
601 strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
602 data->type = kind;
603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 /* Tell the I2C layer a new client has arrived */
605 if ((err = i2c_attach_client(new_client)))
606 goto ERROR2;
607
608 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200609 lm78_init_device(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 /* Register sysfs hooks */
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200612 if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
613 goto ERROR3;
614
Tony Jones1beeffe2007-08-20 13:46:20 -0700615 data->hwmon_dev = hwmon_device_register(&new_client->dev);
616 if (IS_ERR(data->hwmon_dev)) {
617 err = PTR_ERR(data->hwmon_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200618 goto ERROR4;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400619 }
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return 0;
622
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200623ERROR4:
624 sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400625ERROR3:
626 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627ERROR2:
628 kfree(data);
629ERROR1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return err;
631}
632
633static int lm78_detach_client(struct i2c_client *client)
634{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400635 struct lm78_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 int err;
637
Tony Jones1beeffe2007-08-20 13:46:20 -0700638 hwmon_device_unregister(data->hwmon_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200639 sysfs_remove_group(&client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400640
Jean Delvare7bef5592005-07-27 22:14:49 +0200641 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
Jean Delvarec40769f2007-05-08 17:22:00 +0200644 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Jean Delvarec40769f2007-05-08 17:22:00 +0200646 return 0;
647}
648
649static int __devinit lm78_isa_probe(struct platform_device *pdev)
650{
651 int err;
652 struct lm78_data *data;
653 struct resource *res;
654 const char *name;
655
656 /* Reserve the ISA region */
657 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
658 if (!request_region(res->start, LM78_EXTENT, "lm78")) {
659 err = -EBUSY;
660 goto exit;
661 }
662
663 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
664 err = -ENOMEM;
665 goto exit_release_region;
666 }
667 mutex_init(&data->lock);
668 data->client.addr = res->start;
669 i2c_set_clientdata(&data->client, data);
670 platform_set_drvdata(pdev, data);
671
Jean Delvarec59cc302007-05-08 17:22:01 +0200672 if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
Jean Delvarec40769f2007-05-08 17:22:00 +0200673 data->type = lm79;
674 name = "lm79";
675 } else {
676 data->type = lm78;
677 name = "lm78";
678 }
679 strlcpy(data->client.name, name, I2C_NAME_SIZE);
680
681 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200682 lm78_init_device(data);
Jean Delvarec40769f2007-05-08 17:22:00 +0200683
684 /* Register sysfs hooks */
685 if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
686 || (err = device_create_file(&pdev->dev, &dev_attr_name)))
687 goto exit_remove_files;
688
Tony Jones1beeffe2007-08-20 13:46:20 -0700689 data->hwmon_dev = hwmon_device_register(&pdev->dev);
690 if (IS_ERR(data->hwmon_dev)) {
691 err = PTR_ERR(data->hwmon_dev);
Jean Delvarec40769f2007-05-08 17:22:00 +0200692 goto exit_remove_files;
693 }
694
695 return 0;
696
697 exit_remove_files:
698 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
699 device_remove_file(&pdev->dev, &dev_attr_name);
700 kfree(data);
701 exit_release_region:
702 release_region(res->start, LM78_EXTENT);
703 exit:
704 return err;
705}
706
707static int __devexit lm78_isa_remove(struct platform_device *pdev)
708{
709 struct lm78_data *data = platform_get_drvdata(pdev);
710
Tony Jones1beeffe2007-08-20 13:46:20 -0700711 hwmon_device_unregister(data->hwmon_dev);
Jean Delvarec40769f2007-05-08 17:22:00 +0200712 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
713 device_remove_file(&pdev->dev, &dev_attr_name);
714 release_region(data->client.addr, LM78_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400715 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
717 return 0;
718}
719
Steven Cole44bbe872005-05-03 18:21:25 -0600720/* The SMBus locks itself, but ISA access must be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 We don't want to lock the whole ISA bus, so we lock each client
722 separately.
723 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
724 would slow down the LM78 access and should not be necessary. */
Jean Delvarec59cc302007-05-08 17:22:01 +0200725static int lm78_read_value(struct lm78_data *data, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726{
Jean Delvarec59cc302007-05-08 17:22:01 +0200727 struct i2c_client *client = &data->client;
728
Jean Delvarec40769f2007-05-08 17:22:00 +0200729 if (!client->driver) { /* ISA device */
Jean Delvarec59cc302007-05-08 17:22:01 +0200730 int res;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100731 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
733 res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100734 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 return res;
736 } else
737 return i2c_smbus_read_byte_data(client, reg);
738}
739
Steven Cole44bbe872005-05-03 18:21:25 -0600740/* The SMBus locks itself, but ISA access muse be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 We don't want to lock the whole ISA bus, so we lock each client
742 separately.
743 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
744 would slow down the LM78 access and should not be necessary.
745 There are some ugly typecasts here, but the good new is - they should
746 nowhere else be necessary! */
Jean Delvarec59cc302007-05-08 17:22:01 +0200747static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748{
Jean Delvarec59cc302007-05-08 17:22:01 +0200749 struct i2c_client *client = &data->client;
750
Jean Delvarec40769f2007-05-08 17:22:00 +0200751 if (!client->driver) { /* ISA device */
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100752 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
754 outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100755 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 return 0;
757 } else
758 return i2c_smbus_write_byte_data(client, reg, value);
759}
760
Jean Delvarec59cc302007-05-08 17:22:01 +0200761static void lm78_init_device(struct lm78_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
Jean Delvarec40769f2007-05-08 17:22:00 +0200763 u8 config;
764 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 /* Start monitoring */
Jean Delvarec59cc302007-05-08 17:22:01 +0200767 config = lm78_read_value(data, LM78_REG_CONFIG);
Jean Delvarec40769f2007-05-08 17:22:00 +0200768 if ((config & 0x09) != 0x01)
Jean Delvarec59cc302007-05-08 17:22:01 +0200769 lm78_write_value(data, LM78_REG_CONFIG,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 (config & 0xf7) | 0x01);
Jean Delvarec40769f2007-05-08 17:22:00 +0200771
772 /* A few vars need to be filled upon startup */
773 for (i = 0; i < 3; i++) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200774 data->fan_min[i] = lm78_read_value(data,
Jean Delvarec40769f2007-05-08 17:22:00 +0200775 LM78_REG_FAN_MIN(i));
776 }
777
778 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779}
780
781static struct lm78_data *lm78_update_device(struct device *dev)
782{
Jean Delvarec40769f2007-05-08 17:22:00 +0200783 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 int i;
785
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100786 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
789 || !data->valid) {
790
Jean Delvarec40769f2007-05-08 17:22:00 +0200791 dev_dbg(dev, "Starting lm78 update\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793 for (i = 0; i <= 6; i++) {
794 data->in[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200795 lm78_read_value(data, LM78_REG_IN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 data->in_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200797 lm78_read_value(data, LM78_REG_IN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 data->in_max[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200799 lm78_read_value(data, LM78_REG_IN_MAX(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 }
801 for (i = 0; i < 3; i++) {
802 data->fan[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200803 lm78_read_value(data, LM78_REG_FAN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 data->fan_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200805 lm78_read_value(data, LM78_REG_FAN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200807 data->temp = lm78_read_value(data, LM78_REG_TEMP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 data->temp_over =
Jean Delvarec59cc302007-05-08 17:22:01 +0200809 lm78_read_value(data, LM78_REG_TEMP_OVER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 data->temp_hyst =
Jean Delvarec59cc302007-05-08 17:22:01 +0200811 lm78_read_value(data, LM78_REG_TEMP_HYST);
812 i = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 data->vid = i & 0x0f;
814 if (data->type == lm79)
815 data->vid |=
Jean Delvarec59cc302007-05-08 17:22:01 +0200816 (lm78_read_value(data, LM78_REG_CHIPID) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 0x01) << 4;
818 else
819 data->vid |= 0x10;
820 data->fan_div[0] = (i >> 4) & 0x03;
821 data->fan_div[1] = i >> 6;
Jean Delvarec59cc302007-05-08 17:22:01 +0200822 data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
823 (lm78_read_value(data, LM78_REG_ALARM2) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 data->last_updated = jiffies;
825 data->valid = 1;
826
827 data->fan_div[2] = 1;
828 }
829
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100830 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
832 return data;
833}
834
Jean Delvarec40769f2007-05-08 17:22:00 +0200835/* return 1 if a supported chip is found, 0 otherwise */
836static int __init lm78_isa_found(unsigned short address)
837{
838 int val, save, found = 0;
839
840 if (!request_region(address, LM78_EXTENT, "lm78"))
841 return 0;
842
843#define REALLY_SLOW_IO
844 /* We need the timeouts for at least some LM78-like
845 chips. But only if we read 'undefined' registers. */
846 val = inb_p(address + 1);
847 if (inb_p(address + 2) != val
848 || inb_p(address + 3) != val
849 || inb_p(address + 7) != val)
850 goto release;
851#undef REALLY_SLOW_IO
852
853 /* We should be able to change the 7 LSB of the address port. The
854 MSB (busy flag) should be clear initially, set after the write. */
855 save = inb_p(address + LM78_ADDR_REG_OFFSET);
856 if (save & 0x80)
857 goto release;
858 val = ~save & 0x7f;
859 outb_p(val, address + LM78_ADDR_REG_OFFSET);
860 if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
861 outb_p(save, address + LM78_ADDR_REG_OFFSET);
862 goto release;
863 }
864
865 /* We found a device, now see if it could be an LM78 */
866 outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
867 val = inb_p(address + LM78_DATA_REG_OFFSET);
868 if (val & 0x80)
869 goto release;
870 outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
871 val = inb_p(address + LM78_DATA_REG_OFFSET);
872 if (val < 0x03 || val > 0x77) /* Not a valid I2C address */
873 goto release;
874
875 /* The busy flag should be clear again */
876 if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
877 goto release;
878
879 /* Explicitly prevent the misdetection of Winbond chips */
880 outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
881 val = inb_p(address + LM78_DATA_REG_OFFSET);
882 if (val == 0xa3 || val == 0x5c)
883 goto release;
884
885 /* Explicitly prevent the misdetection of ITE chips */
886 outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
887 val = inb_p(address + LM78_DATA_REG_OFFSET);
888 if (val == 0x90)
889 goto release;
890
891 /* Determine the chip type */
892 outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
893 val = inb_p(address + LM78_DATA_REG_OFFSET);
Hans de Goedeacf346a2007-07-24 23:36:00 +0200894 if (val == 0x00 || val == 0x20 /* LM78 */
Jean Delvarec40769f2007-05-08 17:22:00 +0200895 || val == 0x40 /* LM78-J */
896 || (val & 0xfe) == 0xc0) /* LM79 */
897 found = 1;
898
899 if (found)
900 pr_info("lm78: Found an %s chip at %#x\n",
901 val & 0x80 ? "LM79" : "LM78", (int)address);
902
903 release:
904 release_region(address, LM78_EXTENT);
905 return found;
906}
907
908static int __init lm78_isa_device_add(unsigned short address)
909{
910 struct resource res = {
911 .start = address,
Jean Delvare15bde2f2007-08-29 10:39:57 +0200912 .end = address + LM78_EXTENT - 1,
Jean Delvarec40769f2007-05-08 17:22:00 +0200913 .name = "lm78",
914 .flags = IORESOURCE_IO,
915 };
916 int err;
917
918 pdev = platform_device_alloc("lm78", address);
919 if (!pdev) {
920 err = -ENOMEM;
921 printk(KERN_ERR "lm78: Device allocation failed\n");
922 goto exit;
923 }
924
925 err = platform_device_add_resources(pdev, &res, 1);
926 if (err) {
927 printk(KERN_ERR "lm78: Device resource addition failed "
928 "(%d)\n", err);
929 goto exit_device_put;
930 }
931
932 err = platform_device_add(pdev);
933 if (err) {
934 printk(KERN_ERR "lm78: Device addition failed (%d)\n",
935 err);
936 goto exit_device_put;
937 }
938
939 return 0;
940
941 exit_device_put:
942 platform_device_put(pdev);
943 exit:
944 pdev = NULL;
945 return err;
946}
947
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948static int __init sm_lm78_init(void)
949{
Jean Delvarefde09502005-07-19 23:51:07 +0200950 int res;
951
952 res = i2c_add_driver(&lm78_driver);
953 if (res)
Jean Delvarec40769f2007-05-08 17:22:00 +0200954 goto exit;
Jean Delvarefde09502005-07-19 23:51:07 +0200955
Jean Delvarec40769f2007-05-08 17:22:00 +0200956 if (lm78_isa_found(isa_address)) {
957 res = platform_driver_register(&lm78_isa_driver);
958 if (res)
959 goto exit_unreg_i2c_driver;
960
961 /* Sets global pdev as a side effect */
962 res = lm78_isa_device_add(isa_address);
963 if (res)
964 goto exit_unreg_isa_driver;
965 }
Jean Delvarefde09502005-07-19 23:51:07 +0200966
967 return 0;
Jean Delvarec40769f2007-05-08 17:22:00 +0200968
969 exit_unreg_isa_driver:
970 platform_driver_unregister(&lm78_isa_driver);
971 exit_unreg_i2c_driver:
972 i2c_del_driver(&lm78_driver);
973 exit:
974 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
977static void __exit sm_lm78_exit(void)
978{
Jean Delvarec40769f2007-05-08 17:22:00 +0200979 if (pdev) {
980 platform_device_unregister(pdev);
981 platform_driver_unregister(&lm78_isa_driver);
982 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 i2c_del_driver(&lm78_driver);
984}
985
986
987
988MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
Jean Delvare27fe0482005-07-27 21:30:16 +0200989MODULE_DESCRIPTION("LM78/LM79 driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990MODULE_LICENSE("GPL");
991
992module_init(sm_lm78_init);
993module_exit(sm_lm78_exit);