blob: 9fb572f03ba5bf707cd8768a7c1f60928c444821 [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 */
40static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24,
41 0x25, 0x26, 0x27, 0x28, 0x29,
42 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
43 0x2f, I2C_CLIENT_END };
Jean Delvare2d8672c2005-07-19 23:56:35 +020044static unsigned short isa_address = 0x290;
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46/* Insmod parameters */
Jean Delvaref4b50262005-07-31 21:49:03 +020047I2C_CLIENT_INSMOD_2(lm78, lm79);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49/* Many LM78 constants specified below */
50
51/* Length of ISA address segment */
52#define LM78_EXTENT 8
53
54/* Where are the ISA address/data registers relative to the base address */
55#define LM78_ADDR_REG_OFFSET 5
56#define LM78_DATA_REG_OFFSET 6
57
58/* The LM78 registers */
59#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
60#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
61#define LM78_REG_IN(nr) (0x20 + (nr))
62
63#define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
64#define LM78_REG_FAN(nr) (0x28 + (nr))
65
66#define LM78_REG_TEMP 0x27
67#define LM78_REG_TEMP_OVER 0x39
68#define LM78_REG_TEMP_HYST 0x3a
69
70#define LM78_REG_ALARM1 0x41
71#define LM78_REG_ALARM2 0x42
72
73#define LM78_REG_VID_FANDIV 0x47
74
75#define LM78_REG_CONFIG 0x40
76#define LM78_REG_CHIPID 0x49
77#define LM78_REG_I2C_ADDR 0x48
78
79
80/* Conversions. Rounding and limit checking is only done on the TO_REG
81 variants. */
82
83/* IN: mV, (0V to 4.08V)
84 REG: 16mV/bit */
85static inline u8 IN_TO_REG(unsigned long val)
86{
87 unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
88 return (nval + 8) / 16;
89}
90#define IN_FROM_REG(val) ((val) * 16)
91
92static inline u8 FAN_TO_REG(long rpm, int div)
93{
94 if (rpm <= 0)
95 return 255;
96 return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
97}
98
99static inline int FAN_FROM_REG(u8 val, int div)
100{
101 return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
102}
103
104/* TEMP: mC (-128C to +127C)
105 REG: 1C/bit, two's complement */
106static inline s8 TEMP_TO_REG(int val)
107{
108 int nval = SENSORS_LIMIT(val, -128000, 127000) ;
109 return nval<0 ? (nval-500)/1000 : (nval+500)/1000;
110}
111
112static inline int TEMP_FROM_REG(s8 val)
113{
114 return val * 1000;
115}
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117#define DIV_FROM_REG(val) (1 << (val))
118
119/* There are some complications in a module like this. First off, LM78 chips
120 may be both present on the SMBus and the ISA bus, and we have to handle
121 those cases separately at some places. Second, there might be several
122 LM78 chips available (well, actually, that is probably never done; but
123 it is a clean illustration of how to handle a case like that). Finally,
124 a specific chip may be attached to *both* ISA and SMBus, and we would
125 not like to detect it double. Fortunately, in the case of the LM78 at
126 least, a register tells us what SMBus address we are on, so that helps
127 a bit - except if there could be more than one SMBus. Groan. No solution
128 for this yet. */
129
Jean Delvarec40769f2007-05-08 17:22:00 +0200130/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
131 the driver field to differentiate between I2C and ISA chips. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132struct lm78_data {
133 struct i2c_client client;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400134 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100135 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 enum chips type;
137
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100138 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 char valid; /* !=0 if following fields are valid */
140 unsigned long last_updated; /* In jiffies */
141
142 u8 in[7]; /* Register value */
143 u8 in_max[7]; /* Register value */
144 u8 in_min[7]; /* Register value */
145 u8 fan[3]; /* Register value */
146 u8 fan_min[3]; /* Register value */
147 s8 temp; /* Register value */
148 s8 temp_over; /* Register value */
149 s8 temp_hyst; /* Register value */
150 u8 fan_div[3]; /* Register encoding, shifted right */
151 u8 vid; /* Register encoding, combined */
152 u16 alarms; /* Register encoding, combined */
153};
154
155
156static int lm78_attach_adapter(struct i2c_adapter *adapter);
157static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
158static int lm78_detach_client(struct i2c_client *client);
159
Jean Delvarec40769f2007-05-08 17:22:00 +0200160static int __devinit lm78_isa_probe(struct platform_device *pdev);
161static int __devexit lm78_isa_remove(struct platform_device *pdev);
162
Jean Delvarec59cc302007-05-08 17:22:01 +0200163static int lm78_read_value(struct lm78_data *data, u8 reg);
164static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165static struct lm78_data *lm78_update_device(struct device *dev);
Jean Delvarec59cc302007-05-08 17:22:01 +0200166static void lm78_init_device(struct lm78_data *data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
168
169static struct i2c_driver lm78_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100170 .driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100171 .name = "lm78",
172 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .id = I2C_DRIVERID_LM78,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .attach_adapter = lm78_attach_adapter,
175 .detach_client = lm78_detach_client,
176};
177
Jean Delvarec40769f2007-05-08 17:22:00 +0200178static struct platform_driver lm78_isa_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100179 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200180 .owner = THIS_MODULE,
Jean Delvarec40769f2007-05-08 17:22:00 +0200181 .name = "lm78",
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100182 },
Jean Delvarec40769f2007-05-08 17:22:00 +0200183 .probe = lm78_isa_probe,
184 .remove = lm78_isa_remove,
Jean Delvarefde09502005-07-19 23:51:07 +0200185};
186
187
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188/* 7 Voltages */
Jean Delvare247dde42007-05-08 17:22:01 +0200189static ssize_t show_in(struct device *dev, struct device_attribute *da,
190 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191{
Jean Delvare247dde42007-05-08 17:22:01 +0200192 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200194 return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195}
196
Jean Delvare247dde42007-05-08 17:22:01 +0200197static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
198 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Jean Delvare247dde42007-05-08 17:22:01 +0200200 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200202 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
Jean Delvare247dde42007-05-08 17:22:01 +0200205static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
206 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207{
Jean Delvare247dde42007-05-08 17:22:01 +0200208 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200210 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211}
212
Jean Delvare247dde42007-05-08 17:22:01 +0200213static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
214 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
Jean Delvare247dde42007-05-08 17:22:01 +0200216 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200217 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200219 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100221 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 data->in_min[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200223 lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100224 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 return count;
226}
227
Jean Delvare247dde42007-05-08 17:22:01 +0200228static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
229 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
Jean Delvare247dde42007-05-08 17:22:01 +0200231 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200232 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200234 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100236 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 data->in_max[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200238 lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100239 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 return count;
241}
242
243#define show_in_offset(offset) \
Jean Delvare247dde42007-05-08 17:22:01 +0200244static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
245 show_in, NULL, offset); \
246static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
247 show_in_min, set_in_min, offset); \
248static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
249 show_in_max, set_in_max, offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
251show_in_offset(0);
252show_in_offset(1);
253show_in_offset(2);
254show_in_offset(3);
255show_in_offset(4);
256show_in_offset(5);
257show_in_offset(6);
258
259/* Temperature */
Jean Delvare247dde42007-05-08 17:22:01 +0200260static ssize_t show_temp(struct device *dev, struct device_attribute *da,
261 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
263 struct lm78_data *data = lm78_update_device(dev);
264 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
265}
266
Jean Delvare247dde42007-05-08 17:22:01 +0200267static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
268 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
270 struct lm78_data *data = lm78_update_device(dev);
271 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
272}
273
Jean Delvare247dde42007-05-08 17:22:01 +0200274static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
275 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
Jean Delvarec40769f2007-05-08 17:22:00 +0200277 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 long val = simple_strtol(buf, NULL, 10);
279
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100280 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 data->temp_over = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200282 lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100283 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 return count;
285}
286
Jean Delvare247dde42007-05-08 17:22:01 +0200287static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
288 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
290 struct lm78_data *data = lm78_update_device(dev);
291 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
292}
293
Jean Delvare247dde42007-05-08 17:22:01 +0200294static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
295 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
Jean Delvarec40769f2007-05-08 17:22:00 +0200297 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 long val = simple_strtol(buf, NULL, 10);
299
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100300 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 data->temp_hyst = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200302 lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100303 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 return count;
305}
306
307static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
308static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
309 show_temp_over, set_temp_over);
310static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
311 show_temp_hyst, set_temp_hyst);
312
313/* 3 Fans */
Jean Delvare247dde42007-05-08 17:22:01 +0200314static ssize_t show_fan(struct device *dev, struct device_attribute *da,
315 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316{
Jean Delvare247dde42007-05-08 17:22:01 +0200317 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200319 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
321 DIV_FROM_REG(data->fan_div[nr])) );
322}
323
Jean Delvare247dde42007-05-08 17:22:01 +0200324static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
325 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
Jean Delvare247dde42007-05-08 17:22:01 +0200327 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200329 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
331 DIV_FROM_REG(data->fan_div[nr])) );
332}
333
Jean Delvare247dde42007-05-08 17:22:01 +0200334static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
335 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
Jean Delvare247dde42007-05-08 17:22:01 +0200337 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200338 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200339 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 unsigned long val = simple_strtoul(buf, NULL, 10);
341
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100342 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200344 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100345 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 return count;
347}
348
Jean Delvare247dde42007-05-08 17:22:01 +0200349static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
350 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
Jean Delvare247dde42007-05-08 17:22:01 +0200352 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200354 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355}
356
357/* Note: we save and restore the fan minimum here, because its value is
358 determined in part by the fan divisor. This follows the principle of
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200359 least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 because the divisor changed. */
Jean Delvare247dde42007-05-08 17:22:01 +0200361static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
362 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
Jean Delvare247dde42007-05-08 17:22:01 +0200364 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200365 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200366 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 unsigned long val = simple_strtoul(buf, NULL, 10);
368 unsigned long min;
369 u8 reg;
370
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100371 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 min = FAN_FROM_REG(data->fan_min[nr],
373 DIV_FROM_REG(data->fan_div[nr]));
374
375 switch (val) {
376 case 1: data->fan_div[nr] = 0; break;
377 case 2: data->fan_div[nr] = 1; break;
378 case 4: data->fan_div[nr] = 2; break;
379 case 8: data->fan_div[nr] = 3; break;
380 default:
Jean Delvarec40769f2007-05-08 17:22:00 +0200381 dev_err(dev, "fan_div value %ld not "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 "supported. Choose one of 1, 2, 4 or 8!\n", val);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100383 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 return -EINVAL;
385 }
386
Jean Delvarec59cc302007-05-08 17:22:01 +0200387 reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 switch (nr) {
389 case 0:
390 reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
391 break;
392 case 1:
393 reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
394 break;
395 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200396 lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
398 data->fan_min[nr] =
399 FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200400 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100401 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
403 return count;
404}
405
Jean Delvare247dde42007-05-08 17:22:01 +0200406#define show_fan_offset(offset) \
407static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
408 show_fan, NULL, offset - 1); \
409static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
410 show_fan_min, set_fan_min, offset - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
412show_fan_offset(1);
413show_fan_offset(2);
414show_fan_offset(3);
415
416/* Fan 3 divisor is locked in H/W */
Jean Delvare247dde42007-05-08 17:22:01 +0200417static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
418 show_fan_div, set_fan_div, 0);
419static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
420 show_fan_div, set_fan_div, 1);
421static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
423/* VID */
Jean Delvare247dde42007-05-08 17:22:01 +0200424static ssize_t show_vid(struct device *dev, struct device_attribute *da,
425 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426{
427 struct lm78_data *data = lm78_update_device(dev);
Jean Delvared0d3cd62005-11-23 15:44:26 -0800428 return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
431
432/* Alarms */
Jean Delvare247dde42007-05-08 17:22:01 +0200433static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
434 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435{
436 struct lm78_data *data = lm78_update_device(dev);
437 return sprintf(buf, "%u\n", data->alarms);
438}
439static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
440
441/* This function is called when:
442 * lm78_driver is inserted (when this module is loaded), for each
443 available adapter
444 * when a new adapter is inserted (and lm78_driver is still present) */
445static int lm78_attach_adapter(struct i2c_adapter *adapter)
446{
447 if (!(adapter->class & I2C_CLASS_HWMON))
448 return 0;
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200449 return i2c_probe(adapter, &addr_data, lm78_detect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450}
451
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200452static struct attribute *lm78_attributes[] = {
Jean Delvare247dde42007-05-08 17:22:01 +0200453 &sensor_dev_attr_in0_input.dev_attr.attr,
454 &sensor_dev_attr_in0_min.dev_attr.attr,
455 &sensor_dev_attr_in0_max.dev_attr.attr,
456 &sensor_dev_attr_in1_input.dev_attr.attr,
457 &sensor_dev_attr_in1_min.dev_attr.attr,
458 &sensor_dev_attr_in1_max.dev_attr.attr,
459 &sensor_dev_attr_in2_input.dev_attr.attr,
460 &sensor_dev_attr_in2_min.dev_attr.attr,
461 &sensor_dev_attr_in2_max.dev_attr.attr,
462 &sensor_dev_attr_in3_input.dev_attr.attr,
463 &sensor_dev_attr_in3_min.dev_attr.attr,
464 &sensor_dev_attr_in3_max.dev_attr.attr,
465 &sensor_dev_attr_in4_input.dev_attr.attr,
466 &sensor_dev_attr_in4_min.dev_attr.attr,
467 &sensor_dev_attr_in4_max.dev_attr.attr,
468 &sensor_dev_attr_in5_input.dev_attr.attr,
469 &sensor_dev_attr_in5_min.dev_attr.attr,
470 &sensor_dev_attr_in5_max.dev_attr.attr,
471 &sensor_dev_attr_in6_input.dev_attr.attr,
472 &sensor_dev_attr_in6_min.dev_attr.attr,
473 &sensor_dev_attr_in6_max.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200474 &dev_attr_temp1_input.attr,
475 &dev_attr_temp1_max.attr,
476 &dev_attr_temp1_max_hyst.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200477 &sensor_dev_attr_fan1_input.dev_attr.attr,
478 &sensor_dev_attr_fan1_min.dev_attr.attr,
479 &sensor_dev_attr_fan1_div.dev_attr.attr,
480 &sensor_dev_attr_fan2_input.dev_attr.attr,
481 &sensor_dev_attr_fan2_min.dev_attr.attr,
482 &sensor_dev_attr_fan2_div.dev_attr.attr,
483 &sensor_dev_attr_fan3_input.dev_attr.attr,
484 &sensor_dev_attr_fan3_min.dev_attr.attr,
485 &sensor_dev_attr_fan3_div.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200486 &dev_attr_alarms.attr,
487 &dev_attr_cpu0_vid.attr,
488
489 NULL
490};
491
492static const struct attribute_group lm78_group = {
493 .attrs = lm78_attributes,
494};
495
Jean Delvarec40769f2007-05-08 17:22:00 +0200496/* I2C devices get this name attribute automatically, but for ISA devices
497 we must create it by ourselves. */
498static ssize_t show_name(struct device *dev, struct device_attribute
499 *devattr, char *buf)
500{
501 struct lm78_data *data = dev_get_drvdata(dev);
502
503 return sprintf(buf, "%s\n", data->client.name);
504}
505static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
506
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200507/* This function is called by i2c_probe */
Ben Dooksd8d20612005-10-26 21:05:46 +0200508static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 int i, err;
511 struct i2c_client *new_client;
512 struct lm78_data *data;
513 const char *client_name = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Jean Delvarec40769f2007-05-08 17:22:00 +0200515 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 err = -ENODEV;
Jean Delvarec40769f2007-05-08 17:22:00 +0200517 goto ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 }
519
520 /* OK. For now, we presume we have a valid client. We now create the
521 client structure, even though we cannot fill it completely yet.
522 But it allows us to access lm78_{read,write}_value. */
523
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200524 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 err = -ENOMEM;
526 goto ERROR1;
527 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
529 new_client = &data->client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 i2c_set_clientdata(new_client, data);
531 new_client->addr = address;
532 new_client->adapter = adapter;
Jean Delvarec40769f2007-05-08 17:22:00 +0200533 new_client->driver = &lm78_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
535 /* Now, we do the remaining detection. */
536 if (kind < 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200537 if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 err = -ENODEV;
539 goto ERROR2;
540 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200541 if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
Jean Delvarec40769f2007-05-08 17:22:00 +0200542 address) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 err = -ENODEV;
544 goto ERROR2;
545 }
546 }
547
548 /* Determine the chip type. */
549 if (kind <= 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200550 i = lm78_read_value(data, LM78_REG_CHIPID);
Jean Delvare27fe0482005-07-27 21:30:16 +0200551 if (i == 0x00 || i == 0x20 /* LM78 */
552 || i == 0x40) /* LM78-J */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 kind = lm78;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 else if ((i & 0xfe) == 0xc0)
555 kind = lm79;
556 else {
557 if (kind == 0)
558 dev_warn(&adapter->dev, "Ignoring 'force' "
559 "parameter for unknown chip at "
560 "adapter %d, address 0x%02x\n",
561 i2c_adapter_id(adapter), address);
562 err = -ENODEV;
563 goto ERROR2;
564 }
565 }
566
567 if (kind == lm78) {
568 client_name = "lm78";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 } else if (kind == lm79) {
570 client_name = "lm79";
571 }
572
573 /* Fill in the remaining client fields and put into the global list */
574 strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
575 data->type = kind;
576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 /* Tell the I2C layer a new client has arrived */
578 if ((err = i2c_attach_client(new_client)))
579 goto ERROR2;
580
581 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200582 lm78_init_device(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 /* Register sysfs hooks */
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200585 if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
586 goto ERROR3;
587
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400588 data->class_dev = hwmon_device_register(&new_client->dev);
589 if (IS_ERR(data->class_dev)) {
590 err = PTR_ERR(data->class_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200591 goto ERROR4;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400592 }
593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 return 0;
595
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200596ERROR4:
597 sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400598ERROR3:
599 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600ERROR2:
601 kfree(data);
602ERROR1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 return err;
604}
605
606static int lm78_detach_client(struct i2c_client *client)
607{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400608 struct lm78_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 int err;
610
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400611 hwmon_device_unregister(data->class_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200612 sysfs_remove_group(&client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400613
Jean Delvare7bef5592005-07-27 22:14:49 +0200614 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
Jean Delvarec40769f2007-05-08 17:22:00 +0200617 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
Jean Delvarec40769f2007-05-08 17:22:00 +0200619 return 0;
620}
621
622static int __devinit lm78_isa_probe(struct platform_device *pdev)
623{
624 int err;
625 struct lm78_data *data;
626 struct resource *res;
627 const char *name;
628
629 /* Reserve the ISA region */
630 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
631 if (!request_region(res->start, LM78_EXTENT, "lm78")) {
632 err = -EBUSY;
633 goto exit;
634 }
635
636 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
637 err = -ENOMEM;
638 goto exit_release_region;
639 }
640 mutex_init(&data->lock);
641 data->client.addr = res->start;
642 i2c_set_clientdata(&data->client, data);
643 platform_set_drvdata(pdev, data);
644
Jean Delvarec59cc302007-05-08 17:22:01 +0200645 if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
Jean Delvarec40769f2007-05-08 17:22:00 +0200646 data->type = lm79;
647 name = "lm79";
648 } else {
649 data->type = lm78;
650 name = "lm78";
651 }
652 strlcpy(data->client.name, name, I2C_NAME_SIZE);
653
654 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200655 lm78_init_device(data);
Jean Delvarec40769f2007-05-08 17:22:00 +0200656
657 /* Register sysfs hooks */
658 if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
659 || (err = device_create_file(&pdev->dev, &dev_attr_name)))
660 goto exit_remove_files;
661
662 data->class_dev = hwmon_device_register(&pdev->dev);
663 if (IS_ERR(data->class_dev)) {
664 err = PTR_ERR(data->class_dev);
665 goto exit_remove_files;
666 }
667
668 return 0;
669
670 exit_remove_files:
671 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
672 device_remove_file(&pdev->dev, &dev_attr_name);
673 kfree(data);
674 exit_release_region:
675 release_region(res->start, LM78_EXTENT);
676 exit:
677 return err;
678}
679
680static int __devexit lm78_isa_remove(struct platform_device *pdev)
681{
682 struct lm78_data *data = platform_get_drvdata(pdev);
683
684 hwmon_device_unregister(data->class_dev);
685 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
686 device_remove_file(&pdev->dev, &dev_attr_name);
687 release_region(data->client.addr, LM78_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400688 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
690 return 0;
691}
692
Steven Cole44bbe872005-05-03 18:21:25 -0600693/* The SMBus locks itself, but ISA access must be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 We don't want to lock the whole ISA bus, so we lock each client
695 separately.
696 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
697 would slow down the LM78 access and should not be necessary. */
Jean Delvarec59cc302007-05-08 17:22:01 +0200698static int lm78_read_value(struct lm78_data *data, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699{
Jean Delvarec59cc302007-05-08 17:22:01 +0200700 struct i2c_client *client = &data->client;
701
Jean Delvarec40769f2007-05-08 17:22:00 +0200702 if (!client->driver) { /* ISA device */
Jean Delvarec59cc302007-05-08 17:22:01 +0200703 int res;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100704 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
706 res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100707 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 return res;
709 } else
710 return i2c_smbus_read_byte_data(client, reg);
711}
712
Steven Cole44bbe872005-05-03 18:21:25 -0600713/* The SMBus locks itself, but ISA access muse be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 We don't want to lock the whole ISA bus, so we lock each client
715 separately.
716 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
717 would slow down the LM78 access and should not be necessary.
718 There are some ugly typecasts here, but the good new is - they should
719 nowhere else be necessary! */
Jean Delvarec59cc302007-05-08 17:22:01 +0200720static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721{
Jean Delvarec59cc302007-05-08 17:22:01 +0200722 struct i2c_client *client = &data->client;
723
Jean Delvarec40769f2007-05-08 17:22:00 +0200724 if (!client->driver) { /* ISA device */
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100725 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
727 outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100728 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 return 0;
730 } else
731 return i2c_smbus_write_byte_data(client, reg, value);
732}
733
Jean Delvarec59cc302007-05-08 17:22:01 +0200734static void lm78_init_device(struct lm78_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735{
Jean Delvarec40769f2007-05-08 17:22:00 +0200736 u8 config;
737 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
739 /* Start monitoring */
Jean Delvarec59cc302007-05-08 17:22:01 +0200740 config = lm78_read_value(data, LM78_REG_CONFIG);
Jean Delvarec40769f2007-05-08 17:22:00 +0200741 if ((config & 0x09) != 0x01)
Jean Delvarec59cc302007-05-08 17:22:01 +0200742 lm78_write_value(data, LM78_REG_CONFIG,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 (config & 0xf7) | 0x01);
Jean Delvarec40769f2007-05-08 17:22:00 +0200744
745 /* A few vars need to be filled upon startup */
746 for (i = 0; i < 3; i++) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200747 data->fan_min[i] = lm78_read_value(data,
Jean Delvarec40769f2007-05-08 17:22:00 +0200748 LM78_REG_FAN_MIN(i));
749 }
750
751 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
754static struct lm78_data *lm78_update_device(struct device *dev)
755{
Jean Delvarec40769f2007-05-08 17:22:00 +0200756 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 int i;
758
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100759 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
761 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
762 || !data->valid) {
763
Jean Delvarec40769f2007-05-08 17:22:00 +0200764 dev_dbg(dev, "Starting lm78 update\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 for (i = 0; i <= 6; i++) {
767 data->in[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200768 lm78_read_value(data, LM78_REG_IN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 data->in_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200770 lm78_read_value(data, LM78_REG_IN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 data->in_max[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200772 lm78_read_value(data, LM78_REG_IN_MAX(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 }
774 for (i = 0; i < 3; i++) {
775 data->fan[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200776 lm78_read_value(data, LM78_REG_FAN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 data->fan_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200778 lm78_read_value(data, LM78_REG_FAN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200780 data->temp = lm78_read_value(data, LM78_REG_TEMP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 data->temp_over =
Jean Delvarec59cc302007-05-08 17:22:01 +0200782 lm78_read_value(data, LM78_REG_TEMP_OVER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 data->temp_hyst =
Jean Delvarec59cc302007-05-08 17:22:01 +0200784 lm78_read_value(data, LM78_REG_TEMP_HYST);
785 i = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 data->vid = i & 0x0f;
787 if (data->type == lm79)
788 data->vid |=
Jean Delvarec59cc302007-05-08 17:22:01 +0200789 (lm78_read_value(data, LM78_REG_CHIPID) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 0x01) << 4;
791 else
792 data->vid |= 0x10;
793 data->fan_div[0] = (i >> 4) & 0x03;
794 data->fan_div[1] = i >> 6;
Jean Delvarec59cc302007-05-08 17:22:01 +0200795 data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
796 (lm78_read_value(data, LM78_REG_ALARM2) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 data->last_updated = jiffies;
798 data->valid = 1;
799
800 data->fan_div[2] = 1;
801 }
802
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100803 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
805 return data;
806}
807
Jean Delvarec40769f2007-05-08 17:22:00 +0200808/* return 1 if a supported chip is found, 0 otherwise */
809static int __init lm78_isa_found(unsigned short address)
810{
811 int val, save, found = 0;
812
813 if (!request_region(address, LM78_EXTENT, "lm78"))
814 return 0;
815
816#define REALLY_SLOW_IO
817 /* We need the timeouts for at least some LM78-like
818 chips. But only if we read 'undefined' registers. */
819 val = inb_p(address + 1);
820 if (inb_p(address + 2) != val
821 || inb_p(address + 3) != val
822 || inb_p(address + 7) != val)
823 goto release;
824#undef REALLY_SLOW_IO
825
826 /* We should be able to change the 7 LSB of the address port. The
827 MSB (busy flag) should be clear initially, set after the write. */
828 save = inb_p(address + LM78_ADDR_REG_OFFSET);
829 if (save & 0x80)
830 goto release;
831 val = ~save & 0x7f;
832 outb_p(val, address + LM78_ADDR_REG_OFFSET);
833 if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
834 outb_p(save, address + LM78_ADDR_REG_OFFSET);
835 goto release;
836 }
837
838 /* We found a device, now see if it could be an LM78 */
839 outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
840 val = inb_p(address + LM78_DATA_REG_OFFSET);
841 if (val & 0x80)
842 goto release;
843 outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
844 val = inb_p(address + LM78_DATA_REG_OFFSET);
845 if (val < 0x03 || val > 0x77) /* Not a valid I2C address */
846 goto release;
847
848 /* The busy flag should be clear again */
849 if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
850 goto release;
851
852 /* Explicitly prevent the misdetection of Winbond chips */
853 outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
854 val = inb_p(address + LM78_DATA_REG_OFFSET);
855 if (val == 0xa3 || val == 0x5c)
856 goto release;
857
858 /* Explicitly prevent the misdetection of ITE chips */
859 outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
860 val = inb_p(address + LM78_DATA_REG_OFFSET);
861 if (val == 0x90)
862 goto release;
863
864 /* Determine the chip type */
865 outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
866 val = inb_p(address + LM78_DATA_REG_OFFSET);
867 if (val == 0x00 /* LM78 */
868 || val == 0x40 /* LM78-J */
869 || (val & 0xfe) == 0xc0) /* LM79 */
870 found = 1;
871
872 if (found)
873 pr_info("lm78: Found an %s chip at %#x\n",
874 val & 0x80 ? "LM79" : "LM78", (int)address);
875
876 release:
877 release_region(address, LM78_EXTENT);
878 return found;
879}
880
881static int __init lm78_isa_device_add(unsigned short address)
882{
883 struct resource res = {
884 .start = address,
885 .end = address + LM78_EXTENT,
886 .name = "lm78",
887 .flags = IORESOURCE_IO,
888 };
889 int err;
890
891 pdev = platform_device_alloc("lm78", address);
892 if (!pdev) {
893 err = -ENOMEM;
894 printk(KERN_ERR "lm78: Device allocation failed\n");
895 goto exit;
896 }
897
898 err = platform_device_add_resources(pdev, &res, 1);
899 if (err) {
900 printk(KERN_ERR "lm78: Device resource addition failed "
901 "(%d)\n", err);
902 goto exit_device_put;
903 }
904
905 err = platform_device_add(pdev);
906 if (err) {
907 printk(KERN_ERR "lm78: Device addition failed (%d)\n",
908 err);
909 goto exit_device_put;
910 }
911
912 return 0;
913
914 exit_device_put:
915 platform_device_put(pdev);
916 exit:
917 pdev = NULL;
918 return err;
919}
920
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921static int __init sm_lm78_init(void)
922{
Jean Delvarefde09502005-07-19 23:51:07 +0200923 int res;
924
925 res = i2c_add_driver(&lm78_driver);
926 if (res)
Jean Delvarec40769f2007-05-08 17:22:00 +0200927 goto exit;
Jean Delvarefde09502005-07-19 23:51:07 +0200928
Jean Delvarec40769f2007-05-08 17:22:00 +0200929 if (lm78_isa_found(isa_address)) {
930 res = platform_driver_register(&lm78_isa_driver);
931 if (res)
932 goto exit_unreg_i2c_driver;
933
934 /* Sets global pdev as a side effect */
935 res = lm78_isa_device_add(isa_address);
936 if (res)
937 goto exit_unreg_isa_driver;
938 }
Jean Delvarefde09502005-07-19 23:51:07 +0200939
940 return 0;
Jean Delvarec40769f2007-05-08 17:22:00 +0200941
942 exit_unreg_isa_driver:
943 platform_driver_unregister(&lm78_isa_driver);
944 exit_unreg_i2c_driver:
945 i2c_del_driver(&lm78_driver);
946 exit:
947 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948}
949
950static void __exit sm_lm78_exit(void)
951{
Jean Delvarec40769f2007-05-08 17:22:00 +0200952 if (pdev) {
953 platform_device_unregister(pdev);
954 platform_driver_unregister(&lm78_isa_driver);
955 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 i2c_del_driver(&lm78_driver);
957}
958
959
960
961MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
Jean Delvare27fe0482005-07-27 21:30:16 +0200962MODULE_DESCRIPTION("LM78/LM79 driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963MODULE_LICENSE("GPL");
964
965module_init(sm_lm78_init);
966module_exit(sm_lm78_exit);