blob: 934378eb4f8f197ad23de3ca6a363971cffba21a [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 */
Jean Delvare6722fea2007-10-07 12:25:46 +020040static 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 .id = I2C_DRIVERID_LM78,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .attach_adapter = lm78_attach_adapter,
173 .detach_client = lm78_detach_client,
174};
175
Jean Delvarec40769f2007-05-08 17:22:00 +0200176static struct platform_driver lm78_isa_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100177 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200178 .owner = THIS_MODULE,
Jean Delvarec40769f2007-05-08 17:22:00 +0200179 .name = "lm78",
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100180 },
Jean Delvarec40769f2007-05-08 17:22:00 +0200181 .probe = lm78_isa_probe,
182 .remove = lm78_isa_remove,
Jean Delvarefde09502005-07-19 23:51:07 +0200183};
184
185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186/* 7 Voltages */
Jean Delvare247dde42007-05-08 17:22:01 +0200187static ssize_t show_in(struct device *dev, struct device_attribute *da,
188 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
Jean Delvare247dde42007-05-08 17:22:01 +0200190 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200192 return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}
194
Jean Delvare247dde42007-05-08 17:22:01 +0200195static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
196 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Jean Delvare247dde42007-05-08 17:22:01 +0200198 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200200 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
Jean Delvare247dde42007-05-08 17:22:01 +0200203static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
204 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
Jean Delvare247dde42007-05-08 17:22:01 +0200206 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200208 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209}
210
Jean Delvare247dde42007-05-08 17:22:01 +0200211static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
212 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
Jean Delvare247dde42007-05-08 17:22:01 +0200214 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200215 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200217 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100219 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 data->in_min[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200221 lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100222 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 return count;
224}
225
Jean Delvare247dde42007-05-08 17:22:01 +0200226static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
227 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
Jean Delvare247dde42007-05-08 17:22:01 +0200229 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200230 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200232 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100234 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 data->in_max[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200236 lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100237 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 return count;
239}
240
241#define show_in_offset(offset) \
Jean Delvare247dde42007-05-08 17:22:01 +0200242static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
243 show_in, NULL, offset); \
244static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
245 show_in_min, set_in_min, offset); \
246static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
247 show_in_max, set_in_max, offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249show_in_offset(0);
250show_in_offset(1);
251show_in_offset(2);
252show_in_offset(3);
253show_in_offset(4);
254show_in_offset(5);
255show_in_offset(6);
256
257/* Temperature */
Jean Delvare247dde42007-05-08 17:22:01 +0200258static ssize_t show_temp(struct device *dev, struct device_attribute *da,
259 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
261 struct lm78_data *data = lm78_update_device(dev);
262 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
263}
264
Jean Delvare247dde42007-05-08 17:22:01 +0200265static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
266 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
268 struct lm78_data *data = lm78_update_device(dev);
269 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
270}
271
Jean Delvare247dde42007-05-08 17:22:01 +0200272static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
273 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Jean Delvarec40769f2007-05-08 17:22:00 +0200275 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 long val = simple_strtol(buf, NULL, 10);
277
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100278 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 data->temp_over = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200280 lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100281 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 return count;
283}
284
Jean Delvare247dde42007-05-08 17:22:01 +0200285static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
286 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287{
288 struct lm78_data *data = lm78_update_device(dev);
289 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
290}
291
Jean Delvare247dde42007-05-08 17:22:01 +0200292static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
293 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294{
Jean Delvarec40769f2007-05-08 17:22:00 +0200295 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 long val = simple_strtol(buf, NULL, 10);
297
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100298 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 data->temp_hyst = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200300 lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100301 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 return count;
303}
304
305static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
306static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
307 show_temp_over, set_temp_over);
308static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
309 show_temp_hyst, set_temp_hyst);
310
311/* 3 Fans */
Jean Delvare247dde42007-05-08 17:22:01 +0200312static ssize_t show_fan(struct device *dev, struct device_attribute *da,
313 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
Jean Delvare247dde42007-05-08 17:22:01 +0200315 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200317 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
319 DIV_FROM_REG(data->fan_div[nr])) );
320}
321
Jean Delvare247dde42007-05-08 17:22:01 +0200322static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
323 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324{
Jean Delvare247dde42007-05-08 17:22:01 +0200325 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200327 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
329 DIV_FROM_REG(data->fan_div[nr])) );
330}
331
Jean Delvare247dde42007-05-08 17:22:01 +0200332static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
333 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Jean Delvare247dde42007-05-08 17:22:01 +0200335 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200336 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200337 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 unsigned long val = simple_strtoul(buf, NULL, 10);
339
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100340 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200342 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100343 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 return count;
345}
346
Jean Delvare247dde42007-05-08 17:22:01 +0200347static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
348 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
Jean Delvare247dde42007-05-08 17:22:01 +0200350 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200352 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353}
354
355/* Note: we save and restore the fan minimum here, because its value is
356 determined in part by the fan divisor. This follows the principle of
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200357 least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 because the divisor changed. */
Jean Delvare247dde42007-05-08 17:22:01 +0200359static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
360 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Jean Delvare247dde42007-05-08 17:22:01 +0200362 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200363 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200364 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 unsigned long val = simple_strtoul(buf, NULL, 10);
366 unsigned long min;
367 u8 reg;
368
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100369 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 min = FAN_FROM_REG(data->fan_min[nr],
371 DIV_FROM_REG(data->fan_div[nr]));
372
373 switch (val) {
374 case 1: data->fan_div[nr] = 0; break;
375 case 2: data->fan_div[nr] = 1; break;
376 case 4: data->fan_div[nr] = 2; break;
377 case 8: data->fan_div[nr] = 3; break;
378 default:
Jean Delvarec40769f2007-05-08 17:22:00 +0200379 dev_err(dev, "fan_div value %ld not "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 "supported. Choose one of 1, 2, 4 or 8!\n", val);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100381 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 return -EINVAL;
383 }
384
Jean Delvarec59cc302007-05-08 17:22:01 +0200385 reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 switch (nr) {
387 case 0:
388 reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
389 break;
390 case 1:
391 reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
392 break;
393 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200394 lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
396 data->fan_min[nr] =
397 FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200398 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100399 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
401 return count;
402}
403
Jean Delvare247dde42007-05-08 17:22:01 +0200404#define show_fan_offset(offset) \
405static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
406 show_fan, NULL, offset - 1); \
407static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
408 show_fan_min, set_fan_min, offset - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
410show_fan_offset(1);
411show_fan_offset(2);
412show_fan_offset(3);
413
414/* Fan 3 divisor is locked in H/W */
Jean Delvare247dde42007-05-08 17:22:01 +0200415static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
416 show_fan_div, set_fan_div, 0);
417static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
418 show_fan_div, set_fan_div, 1);
419static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
421/* VID */
Jean Delvare247dde42007-05-08 17:22:01 +0200422static ssize_t show_vid(struct device *dev, struct device_attribute *da,
423 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
425 struct lm78_data *data = lm78_update_device(dev);
Jean Delvared0d3cd62005-11-23 15:44:26 -0800426 return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427}
428static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
429
430/* Alarms */
Jean Delvare247dde42007-05-08 17:22:01 +0200431static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
432 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433{
434 struct lm78_data *data = lm78_update_device(dev);
435 return sprintf(buf, "%u\n", data->alarms);
436}
437static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
438
Jean Delvare428a7032007-09-04 23:25:33 +0200439static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
440 char *buf)
441{
442 struct lm78_data *data = lm78_update_device(dev);
443 int nr = to_sensor_dev_attr(da)->index;
444 return sprintf(buf, "%u\n", (data->alarms >> nr) & 1);
445}
446static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
447static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
448static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
449static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
450static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
451static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9);
452static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10);
453static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
454static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
455static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
456static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458/* This function is called when:
459 * lm78_driver is inserted (when this module is loaded), for each
460 available adapter
461 * when a new adapter is inserted (and lm78_driver is still present) */
462static int lm78_attach_adapter(struct i2c_adapter *adapter)
463{
464 if (!(adapter->class & I2C_CLASS_HWMON))
465 return 0;
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200466 return i2c_probe(adapter, &addr_data, lm78_detect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467}
468
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200469static struct attribute *lm78_attributes[] = {
Jean Delvare247dde42007-05-08 17:22:01 +0200470 &sensor_dev_attr_in0_input.dev_attr.attr,
471 &sensor_dev_attr_in0_min.dev_attr.attr,
472 &sensor_dev_attr_in0_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200473 &sensor_dev_attr_in0_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200474 &sensor_dev_attr_in1_input.dev_attr.attr,
475 &sensor_dev_attr_in1_min.dev_attr.attr,
476 &sensor_dev_attr_in1_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200477 &sensor_dev_attr_in1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200478 &sensor_dev_attr_in2_input.dev_attr.attr,
479 &sensor_dev_attr_in2_min.dev_attr.attr,
480 &sensor_dev_attr_in2_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200481 &sensor_dev_attr_in2_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200482 &sensor_dev_attr_in3_input.dev_attr.attr,
483 &sensor_dev_attr_in3_min.dev_attr.attr,
484 &sensor_dev_attr_in3_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200485 &sensor_dev_attr_in3_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200486 &sensor_dev_attr_in4_input.dev_attr.attr,
487 &sensor_dev_attr_in4_min.dev_attr.attr,
488 &sensor_dev_attr_in4_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200489 &sensor_dev_attr_in4_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200490 &sensor_dev_attr_in5_input.dev_attr.attr,
491 &sensor_dev_attr_in5_min.dev_attr.attr,
492 &sensor_dev_attr_in5_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200493 &sensor_dev_attr_in5_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200494 &sensor_dev_attr_in6_input.dev_attr.attr,
495 &sensor_dev_attr_in6_min.dev_attr.attr,
496 &sensor_dev_attr_in6_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200497 &sensor_dev_attr_in6_alarm.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200498 &dev_attr_temp1_input.attr,
499 &dev_attr_temp1_max.attr,
500 &dev_attr_temp1_max_hyst.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200501 &sensor_dev_attr_temp1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200502 &sensor_dev_attr_fan1_input.dev_attr.attr,
503 &sensor_dev_attr_fan1_min.dev_attr.attr,
504 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200505 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200506 &sensor_dev_attr_fan2_input.dev_attr.attr,
507 &sensor_dev_attr_fan2_min.dev_attr.attr,
508 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200509 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200510 &sensor_dev_attr_fan3_input.dev_attr.attr,
511 &sensor_dev_attr_fan3_min.dev_attr.attr,
512 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200513 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200514 &dev_attr_alarms.attr,
515 &dev_attr_cpu0_vid.attr,
516
517 NULL
518};
519
520static const struct attribute_group lm78_group = {
521 .attrs = lm78_attributes,
522};
523
Jean Delvarec40769f2007-05-08 17:22:00 +0200524/* I2C devices get this name attribute automatically, but for ISA devices
525 we must create it by ourselves. */
526static ssize_t show_name(struct device *dev, struct device_attribute
527 *devattr, char *buf)
528{
529 struct lm78_data *data = dev_get_drvdata(dev);
530
531 return sprintf(buf, "%s\n", data->client.name);
532}
533static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
534
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200535/* This function is called by i2c_probe */
Ben Dooksd8d20612005-10-26 21:05:46 +0200536static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537{
538 int i, err;
539 struct i2c_client *new_client;
540 struct lm78_data *data;
541 const char *client_name = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Jean Delvarec40769f2007-05-08 17:22:00 +0200543 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 err = -ENODEV;
Jean Delvarec40769f2007-05-08 17:22:00 +0200545 goto ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 }
547
548 /* OK. For now, we presume we have a valid client. We now create the
549 client structure, even though we cannot fill it completely yet.
550 But it allows us to access lm78_{read,write}_value. */
551
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200552 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 err = -ENOMEM;
554 goto ERROR1;
555 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
557 new_client = &data->client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 i2c_set_clientdata(new_client, data);
559 new_client->addr = address;
560 new_client->adapter = adapter;
Jean Delvarec40769f2007-05-08 17:22:00 +0200561 new_client->driver = &lm78_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
563 /* Now, we do the remaining detection. */
564 if (kind < 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200565 if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 err = -ENODEV;
567 goto ERROR2;
568 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200569 if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
Jean Delvarec40769f2007-05-08 17:22:00 +0200570 address) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 err = -ENODEV;
572 goto ERROR2;
573 }
574 }
575
576 /* Determine the chip type. */
577 if (kind <= 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200578 i = lm78_read_value(data, LM78_REG_CHIPID);
Jean Delvare27fe0482005-07-27 21:30:16 +0200579 if (i == 0x00 || i == 0x20 /* LM78 */
580 || i == 0x40) /* LM78-J */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 kind = lm78;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 else if ((i & 0xfe) == 0xc0)
583 kind = lm79;
584 else {
585 if (kind == 0)
586 dev_warn(&adapter->dev, "Ignoring 'force' "
587 "parameter for unknown chip at "
588 "adapter %d, address 0x%02x\n",
589 i2c_adapter_id(adapter), address);
590 err = -ENODEV;
591 goto ERROR2;
592 }
593 }
594
595 if (kind == lm78) {
596 client_name = "lm78";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 } else if (kind == lm79) {
598 client_name = "lm79";
599 }
600
601 /* Fill in the remaining client fields and put into the global list */
602 strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
603 data->type = kind;
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 /* Tell the I2C layer a new client has arrived */
606 if ((err = i2c_attach_client(new_client)))
607 goto ERROR2;
608
609 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200610 lm78_init_device(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 /* Register sysfs hooks */
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200613 if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
614 goto ERROR3;
615
Tony Jones1beeffe2007-08-20 13:46:20 -0700616 data->hwmon_dev = hwmon_device_register(&new_client->dev);
617 if (IS_ERR(data->hwmon_dev)) {
618 err = PTR_ERR(data->hwmon_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200619 goto ERROR4;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400620 }
621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 return 0;
623
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200624ERROR4:
625 sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400626ERROR3:
627 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628ERROR2:
629 kfree(data);
630ERROR1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return err;
632}
633
634static int lm78_detach_client(struct i2c_client *client)
635{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400636 struct lm78_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 int err;
638
Tony Jones1beeffe2007-08-20 13:46:20 -0700639 hwmon_device_unregister(data->hwmon_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200640 sysfs_remove_group(&client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400641
Jean Delvare7bef5592005-07-27 22:14:49 +0200642 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Jean Delvarec40769f2007-05-08 17:22:00 +0200645 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
Jean Delvarec40769f2007-05-08 17:22:00 +0200647 return 0;
648}
649
650static int __devinit lm78_isa_probe(struct platform_device *pdev)
651{
652 int err;
653 struct lm78_data *data;
654 struct resource *res;
655 const char *name;
656
657 /* Reserve the ISA region */
658 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
659 if (!request_region(res->start, LM78_EXTENT, "lm78")) {
660 err = -EBUSY;
661 goto exit;
662 }
663
664 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
665 err = -ENOMEM;
666 goto exit_release_region;
667 }
668 mutex_init(&data->lock);
669 data->client.addr = res->start;
670 i2c_set_clientdata(&data->client, data);
671 platform_set_drvdata(pdev, data);
672
Jean Delvarec59cc302007-05-08 17:22:01 +0200673 if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
Jean Delvarec40769f2007-05-08 17:22:00 +0200674 data->type = lm79;
675 name = "lm79";
676 } else {
677 data->type = lm78;
678 name = "lm78";
679 }
680 strlcpy(data->client.name, name, I2C_NAME_SIZE);
681
682 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200683 lm78_init_device(data);
Jean Delvarec40769f2007-05-08 17:22:00 +0200684
685 /* Register sysfs hooks */
686 if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
687 || (err = device_create_file(&pdev->dev, &dev_attr_name)))
688 goto exit_remove_files;
689
Tony Jones1beeffe2007-08-20 13:46:20 -0700690 data->hwmon_dev = hwmon_device_register(&pdev->dev);
691 if (IS_ERR(data->hwmon_dev)) {
692 err = PTR_ERR(data->hwmon_dev);
Jean Delvarec40769f2007-05-08 17:22:00 +0200693 goto exit_remove_files;
694 }
695
696 return 0;
697
698 exit_remove_files:
699 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
700 device_remove_file(&pdev->dev, &dev_attr_name);
701 kfree(data);
702 exit_release_region:
703 release_region(res->start, LM78_EXTENT);
704 exit:
705 return err;
706}
707
708static int __devexit lm78_isa_remove(struct platform_device *pdev)
709{
710 struct lm78_data *data = platform_get_drvdata(pdev);
711
Tony Jones1beeffe2007-08-20 13:46:20 -0700712 hwmon_device_unregister(data->hwmon_dev);
Jean Delvarec40769f2007-05-08 17:22:00 +0200713 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
714 device_remove_file(&pdev->dev, &dev_attr_name);
715 release_region(data->client.addr, LM78_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400716 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
718 return 0;
719}
720
Steven Cole44bbe872005-05-03 18:21:25 -0600721/* The SMBus locks itself, but ISA access must be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 We don't want to lock the whole ISA bus, so we lock each client
723 separately.
724 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
725 would slow down the LM78 access and should not be necessary. */
Jean Delvarec59cc302007-05-08 17:22:01 +0200726static int lm78_read_value(struct lm78_data *data, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727{
Jean Delvarec59cc302007-05-08 17:22:01 +0200728 struct i2c_client *client = &data->client;
729
Jean Delvarec40769f2007-05-08 17:22:00 +0200730 if (!client->driver) { /* ISA device */
Jean Delvarec59cc302007-05-08 17:22:01 +0200731 int res;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100732 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
734 res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100735 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 return res;
737 } else
738 return i2c_smbus_read_byte_data(client, reg);
739}
740
Steven Cole44bbe872005-05-03 18:21:25 -0600741/* The SMBus locks itself, but ISA access muse be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 We don't want to lock the whole ISA bus, so we lock each client
743 separately.
744 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
745 would slow down the LM78 access and should not be necessary.
746 There are some ugly typecasts here, but the good new is - they should
747 nowhere else be necessary! */
Jean Delvarec59cc302007-05-08 17:22:01 +0200748static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749{
Jean Delvarec59cc302007-05-08 17:22:01 +0200750 struct i2c_client *client = &data->client;
751
Jean Delvarec40769f2007-05-08 17:22:00 +0200752 if (!client->driver) { /* ISA device */
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100753 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
755 outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100756 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 return 0;
758 } else
759 return i2c_smbus_write_byte_data(client, reg, value);
760}
761
Jean Delvarec59cc302007-05-08 17:22:01 +0200762static void lm78_init_device(struct lm78_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763{
Jean Delvarec40769f2007-05-08 17:22:00 +0200764 u8 config;
765 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
767 /* Start monitoring */
Jean Delvarec59cc302007-05-08 17:22:01 +0200768 config = lm78_read_value(data, LM78_REG_CONFIG);
Jean Delvarec40769f2007-05-08 17:22:00 +0200769 if ((config & 0x09) != 0x01)
Jean Delvarec59cc302007-05-08 17:22:01 +0200770 lm78_write_value(data, LM78_REG_CONFIG,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 (config & 0xf7) | 0x01);
Jean Delvarec40769f2007-05-08 17:22:00 +0200772
773 /* A few vars need to be filled upon startup */
774 for (i = 0; i < 3; i++) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200775 data->fan_min[i] = lm78_read_value(data,
Jean Delvarec40769f2007-05-08 17:22:00 +0200776 LM78_REG_FAN_MIN(i));
777 }
778
779 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780}
781
782static struct lm78_data *lm78_update_device(struct device *dev)
783{
Jean Delvarec40769f2007-05-08 17:22:00 +0200784 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 int i;
786
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100787 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
789 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
790 || !data->valid) {
791
Jean Delvarec40769f2007-05-08 17:22:00 +0200792 dev_dbg(dev, "Starting lm78 update\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794 for (i = 0; i <= 6; i++) {
795 data->in[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200796 lm78_read_value(data, LM78_REG_IN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 data->in_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200798 lm78_read_value(data, LM78_REG_IN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 data->in_max[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200800 lm78_read_value(data, LM78_REG_IN_MAX(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 }
802 for (i = 0; i < 3; i++) {
803 data->fan[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200804 lm78_read_value(data, LM78_REG_FAN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 data->fan_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200806 lm78_read_value(data, LM78_REG_FAN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200808 data->temp = lm78_read_value(data, LM78_REG_TEMP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 data->temp_over =
Jean Delvarec59cc302007-05-08 17:22:01 +0200810 lm78_read_value(data, LM78_REG_TEMP_OVER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 data->temp_hyst =
Jean Delvarec59cc302007-05-08 17:22:01 +0200812 lm78_read_value(data, LM78_REG_TEMP_HYST);
813 i = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 data->vid = i & 0x0f;
815 if (data->type == lm79)
816 data->vid |=
Jean Delvarec59cc302007-05-08 17:22:01 +0200817 (lm78_read_value(data, LM78_REG_CHIPID) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 0x01) << 4;
819 else
820 data->vid |= 0x10;
821 data->fan_div[0] = (i >> 4) & 0x03;
822 data->fan_div[1] = i >> 6;
Jean Delvarec59cc302007-05-08 17:22:01 +0200823 data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
824 (lm78_read_value(data, LM78_REG_ALARM2) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 data->last_updated = jiffies;
826 data->valid = 1;
827
828 data->fan_div[2] = 1;
829 }
830
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100831 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
833 return data;
834}
835
Jean Delvarec40769f2007-05-08 17:22:00 +0200836/* return 1 if a supported chip is found, 0 otherwise */
837static int __init lm78_isa_found(unsigned short address)
838{
839 int val, save, found = 0;
840
841 if (!request_region(address, LM78_EXTENT, "lm78"))
842 return 0;
843
844#define REALLY_SLOW_IO
845 /* We need the timeouts for at least some LM78-like
846 chips. But only if we read 'undefined' registers. */
847 val = inb_p(address + 1);
848 if (inb_p(address + 2) != val
849 || inb_p(address + 3) != val
850 || inb_p(address + 7) != val)
851 goto release;
852#undef REALLY_SLOW_IO
853
854 /* We should be able to change the 7 LSB of the address port. The
855 MSB (busy flag) should be clear initially, set after the write. */
856 save = inb_p(address + LM78_ADDR_REG_OFFSET);
857 if (save & 0x80)
858 goto release;
859 val = ~save & 0x7f;
860 outb_p(val, address + LM78_ADDR_REG_OFFSET);
861 if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
862 outb_p(save, address + LM78_ADDR_REG_OFFSET);
863 goto release;
864 }
865
866 /* We found a device, now see if it could be an LM78 */
867 outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
868 val = inb_p(address + LM78_DATA_REG_OFFSET);
869 if (val & 0x80)
870 goto release;
871 outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
872 val = inb_p(address + LM78_DATA_REG_OFFSET);
873 if (val < 0x03 || val > 0x77) /* Not a valid I2C address */
874 goto release;
875
876 /* The busy flag should be clear again */
877 if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
878 goto release;
879
880 /* Explicitly prevent the misdetection of Winbond chips */
881 outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
882 val = inb_p(address + LM78_DATA_REG_OFFSET);
883 if (val == 0xa3 || val == 0x5c)
884 goto release;
885
886 /* Explicitly prevent the misdetection of ITE chips */
887 outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
888 val = inb_p(address + LM78_DATA_REG_OFFSET);
889 if (val == 0x90)
890 goto release;
891
892 /* Determine the chip type */
893 outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
894 val = inb_p(address + LM78_DATA_REG_OFFSET);
Hans de Goedeacf346a2007-07-24 23:36:00 +0200895 if (val == 0x00 || val == 0x20 /* LM78 */
Jean Delvarec40769f2007-05-08 17:22:00 +0200896 || val == 0x40 /* LM78-J */
897 || (val & 0xfe) == 0xc0) /* LM79 */
898 found = 1;
899
900 if (found)
901 pr_info("lm78: Found an %s chip at %#x\n",
902 val & 0x80 ? "LM79" : "LM78", (int)address);
903
904 release:
905 release_region(address, LM78_EXTENT);
906 return found;
907}
908
909static int __init lm78_isa_device_add(unsigned short address)
910{
911 struct resource res = {
912 .start = address,
Jean Delvare15bde2f2007-08-29 10:39:57 +0200913 .end = address + LM78_EXTENT - 1,
Jean Delvarec40769f2007-05-08 17:22:00 +0200914 .name = "lm78",
915 .flags = IORESOURCE_IO,
916 };
917 int err;
918
919 pdev = platform_device_alloc("lm78", address);
920 if (!pdev) {
921 err = -ENOMEM;
922 printk(KERN_ERR "lm78: Device allocation failed\n");
923 goto exit;
924 }
925
926 err = platform_device_add_resources(pdev, &res, 1);
927 if (err) {
928 printk(KERN_ERR "lm78: Device resource addition failed "
929 "(%d)\n", err);
930 goto exit_device_put;
931 }
932
933 err = platform_device_add(pdev);
934 if (err) {
935 printk(KERN_ERR "lm78: Device addition failed (%d)\n",
936 err);
937 goto exit_device_put;
938 }
939
940 return 0;
941
942 exit_device_put:
943 platform_device_put(pdev);
944 exit:
945 pdev = NULL;
946 return err;
947}
948
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949static int __init sm_lm78_init(void)
950{
Jean Delvarefde09502005-07-19 23:51:07 +0200951 int res;
952
953 res = i2c_add_driver(&lm78_driver);
954 if (res)
Jean Delvarec40769f2007-05-08 17:22:00 +0200955 goto exit;
Jean Delvarefde09502005-07-19 23:51:07 +0200956
Jean Delvarec40769f2007-05-08 17:22:00 +0200957 if (lm78_isa_found(isa_address)) {
958 res = platform_driver_register(&lm78_isa_driver);
959 if (res)
960 goto exit_unreg_i2c_driver;
961
962 /* Sets global pdev as a side effect */
963 res = lm78_isa_device_add(isa_address);
964 if (res)
965 goto exit_unreg_isa_driver;
966 }
Jean Delvarefde09502005-07-19 23:51:07 +0200967
968 return 0;
Jean Delvarec40769f2007-05-08 17:22:00 +0200969
970 exit_unreg_isa_driver:
971 platform_driver_unregister(&lm78_isa_driver);
972 exit_unreg_i2c_driver:
973 i2c_del_driver(&lm78_driver);
974 exit:
975 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976}
977
978static void __exit sm_lm78_exit(void)
979{
Jean Delvarec40769f2007-05-08 17:22:00 +0200980 if (pdev) {
981 platform_device_unregister(pdev);
982 platform_driver_unregister(&lm78_isa_driver);
983 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 i2c_del_driver(&lm78_driver);
985}
986
987
988
989MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
Jean Delvare27fe0482005-07-27 21:30:16 +0200990MODULE_DESCRIPTION("LM78/LM79 driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991MODULE_LICENSE("GPL");
992
993module_init(sm_lm78_init);
994module_exit(sm_lm78_exit);