blob: ec601bbf91b9cbd71cd36eb2aa7e5e5cb67e60f0 [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
Jean Delvare18c73f92008-10-17 17:51:15 +0200123 not like to detect it double. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Jean Delvarec40769f2007-05-08 17:22:00 +0200125/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
126 the driver field to differentiate between I2C and ISA chips. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127struct lm78_data {
128 struct i2c_client client;
Tony Jones1beeffe2007-08-20 13:46:20 -0700129 struct device *hwmon_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100130 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 enum chips type;
132
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100133 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 char valid; /* !=0 if following fields are valid */
135 unsigned long last_updated; /* In jiffies */
136
137 u8 in[7]; /* Register value */
138 u8 in_max[7]; /* Register value */
139 u8 in_min[7]; /* Register value */
140 u8 fan[3]; /* Register value */
141 u8 fan_min[3]; /* Register value */
142 s8 temp; /* Register value */
143 s8 temp_over; /* Register value */
144 s8 temp_hyst; /* Register value */
145 u8 fan_div[3]; /* Register encoding, shifted right */
146 u8 vid; /* Register encoding, combined */
147 u16 alarms; /* Register encoding, combined */
148};
149
150
151static int lm78_attach_adapter(struct i2c_adapter *adapter);
152static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
153static int lm78_detach_client(struct i2c_client *client);
154
Jean Delvarec40769f2007-05-08 17:22:00 +0200155static int __devinit lm78_isa_probe(struct platform_device *pdev);
156static int __devexit lm78_isa_remove(struct platform_device *pdev);
157
Jean Delvarec59cc302007-05-08 17:22:01 +0200158static int lm78_read_value(struct lm78_data *data, u8 reg);
159static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160static struct lm78_data *lm78_update_device(struct device *dev);
Jean Delvarec59cc302007-05-08 17:22:01 +0200161static void lm78_init_device(struct lm78_data *data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
163
164static struct i2c_driver lm78_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100165 .driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100166 .name = "lm78",
167 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 .attach_adapter = lm78_attach_adapter,
169 .detach_client = lm78_detach_client,
170};
171
Jean Delvarec40769f2007-05-08 17:22:00 +0200172static struct platform_driver lm78_isa_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100173 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200174 .owner = THIS_MODULE,
Jean Delvarec40769f2007-05-08 17:22:00 +0200175 .name = "lm78",
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100176 },
Jean Delvarec40769f2007-05-08 17:22:00 +0200177 .probe = lm78_isa_probe,
178 .remove = lm78_isa_remove,
Jean Delvarefde09502005-07-19 23:51:07 +0200179};
180
181
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182/* 7 Voltages */
Jean Delvare247dde42007-05-08 17:22:01 +0200183static ssize_t show_in(struct device *dev, struct device_attribute *da,
184 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
Jean Delvare247dde42007-05-08 17:22:01 +0200186 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200188 return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189}
190
Jean Delvare247dde42007-05-08 17:22:01 +0200191static ssize_t show_in_min(struct device *dev, struct device_attribute *da,
192 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193{
Jean Delvare247dde42007-05-08 17:22:01 +0200194 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200196 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
Jean Delvare247dde42007-05-08 17:22:01 +0200199static ssize_t show_in_max(struct device *dev, struct device_attribute *da,
200 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201{
Jean Delvare247dde42007-05-08 17:22:01 +0200202 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200204 return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205}
206
Jean Delvare247dde42007-05-08 17:22:01 +0200207static ssize_t set_in_min(struct device *dev, struct device_attribute *da,
208 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Jean Delvare247dde42007-05-08 17:22:01 +0200210 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200211 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200213 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100215 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 data->in_min[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200217 lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100218 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 return count;
220}
221
Jean Delvare247dde42007-05-08 17:22:01 +0200222static ssize_t set_in_max(struct device *dev, struct device_attribute *da,
223 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224{
Jean Delvare247dde42007-05-08 17:22:01 +0200225 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200226 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 unsigned long val = simple_strtoul(buf, NULL, 10);
Jean Delvare247dde42007-05-08 17:22:01 +0200228 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100230 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 data->in_max[nr] = IN_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200232 lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100233 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return count;
235}
236
237#define show_in_offset(offset) \
Jean Delvare247dde42007-05-08 17:22:01 +0200238static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
239 show_in, NULL, offset); \
240static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
241 show_in_min, set_in_min, offset); \
242static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
243 show_in_max, set_in_max, offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
245show_in_offset(0);
246show_in_offset(1);
247show_in_offset(2);
248show_in_offset(3);
249show_in_offset(4);
250show_in_offset(5);
251show_in_offset(6);
252
253/* Temperature */
Jean Delvare247dde42007-05-08 17:22:01 +0200254static ssize_t show_temp(struct device *dev, struct device_attribute *da,
255 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 struct lm78_data *data = lm78_update_device(dev);
258 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
259}
260
Jean Delvare247dde42007-05-08 17:22:01 +0200261static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
262 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
264 struct lm78_data *data = lm78_update_device(dev);
265 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
266}
267
Jean Delvare247dde42007-05-08 17:22:01 +0200268static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
269 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270{
Jean Delvarec40769f2007-05-08 17:22:00 +0200271 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 long val = simple_strtol(buf, NULL, 10);
273
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100274 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 data->temp_over = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200276 lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100277 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 return count;
279}
280
Jean Delvare247dde42007-05-08 17:22:01 +0200281static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
282 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 struct lm78_data *data = lm78_update_device(dev);
285 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
286}
287
Jean Delvare247dde42007-05-08 17:22:01 +0200288static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
289 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Jean Delvarec40769f2007-05-08 17:22:00 +0200291 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 long val = simple_strtol(buf, NULL, 10);
293
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100294 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 data->temp_hyst = TEMP_TO_REG(val);
Jean Delvarec59cc302007-05-08 17:22:01 +0200296 lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100297 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 return count;
299}
300
301static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
302static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
303 show_temp_over, set_temp_over);
304static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
305 show_temp_hyst, set_temp_hyst);
306
307/* 3 Fans */
Jean Delvare247dde42007-05-08 17:22:01 +0200308static ssize_t show_fan(struct device *dev, struct device_attribute *da,
309 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310{
Jean Delvare247dde42007-05-08 17:22:01 +0200311 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200313 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
315 DIV_FROM_REG(data->fan_div[nr])) );
316}
317
Jean Delvare247dde42007-05-08 17:22:01 +0200318static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
319 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
Jean Delvare247dde42007-05-08 17:22:01 +0200321 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200323 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
325 DIV_FROM_REG(data->fan_div[nr])) );
326}
327
Jean Delvare247dde42007-05-08 17:22:01 +0200328static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
329 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330{
Jean Delvare247dde42007-05-08 17:22:01 +0200331 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200332 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200333 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 unsigned long val = simple_strtoul(buf, NULL, 10);
335
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100336 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200338 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100339 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 return count;
341}
342
Jean Delvare247dde42007-05-08 17:22:01 +0200343static ssize_t show_fan_div(struct device *dev, struct device_attribute *da,
344 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
Jean Delvare247dde42007-05-08 17:22:01 +0200346 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 struct lm78_data *data = lm78_update_device(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200348 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349}
350
351/* Note: we save and restore the fan minimum here, because its value is
352 determined in part by the fan divisor. This follows the principle of
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200353 least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 because the divisor changed. */
Jean Delvare247dde42007-05-08 17:22:01 +0200355static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
356 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
Jean Delvare247dde42007-05-08 17:22:01 +0200358 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
Jean Delvarec40769f2007-05-08 17:22:00 +0200359 struct lm78_data *data = dev_get_drvdata(dev);
Jean Delvare247dde42007-05-08 17:22:01 +0200360 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 unsigned long val = simple_strtoul(buf, NULL, 10);
362 unsigned long min;
363 u8 reg;
364
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100365 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 min = FAN_FROM_REG(data->fan_min[nr],
367 DIV_FROM_REG(data->fan_div[nr]));
368
369 switch (val) {
370 case 1: data->fan_div[nr] = 0; break;
371 case 2: data->fan_div[nr] = 1; break;
372 case 4: data->fan_div[nr] = 2; break;
373 case 8: data->fan_div[nr] = 3; break;
374 default:
Jean Delvarec40769f2007-05-08 17:22:00 +0200375 dev_err(dev, "fan_div value %ld not "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 "supported. Choose one of 1, 2, 4 or 8!\n", val);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100377 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 return -EINVAL;
379 }
380
Jean Delvarec59cc302007-05-08 17:22:01 +0200381 reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 switch (nr) {
383 case 0:
384 reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
385 break;
386 case 1:
387 reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
388 break;
389 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200390 lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 data->fan_min[nr] =
393 FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
Jean Delvarec59cc302007-05-08 17:22:01 +0200394 lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100395 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
397 return count;
398}
399
Jean Delvare247dde42007-05-08 17:22:01 +0200400#define show_fan_offset(offset) \
401static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
402 show_fan, NULL, offset - 1); \
403static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
404 show_fan_min, set_fan_min, offset - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
406show_fan_offset(1);
407show_fan_offset(2);
408show_fan_offset(3);
409
410/* Fan 3 divisor is locked in H/W */
Jean Delvare247dde42007-05-08 17:22:01 +0200411static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
412 show_fan_div, set_fan_div, 0);
413static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
414 show_fan_div, set_fan_div, 1);
415static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
417/* VID */
Jean Delvare247dde42007-05-08 17:22:01 +0200418static ssize_t show_vid(struct device *dev, struct device_attribute *da,
419 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
421 struct lm78_data *data = lm78_update_device(dev);
Jean Delvared0d3cd62005-11-23 15:44:26 -0800422 return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423}
424static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
425
426/* Alarms */
Jean Delvare247dde42007-05-08 17:22:01 +0200427static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
428 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
430 struct lm78_data *data = lm78_update_device(dev);
431 return sprintf(buf, "%u\n", data->alarms);
432}
433static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
434
Jean Delvare428a7032007-09-04 23:25:33 +0200435static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
436 char *buf)
437{
438 struct lm78_data *data = lm78_update_device(dev);
439 int nr = to_sensor_dev_attr(da)->index;
440 return sprintf(buf, "%u\n", (data->alarms >> nr) & 1);
441}
442static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
443static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
444static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
445static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
446static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
447static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9);
448static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10);
449static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
450static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
451static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
452static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
453
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454/* This function is called when:
455 * lm78_driver is inserted (when this module is loaded), for each
456 available adapter
Jean Delvare18c73f92008-10-17 17:51:15 +0200457 * when a new adapter is inserted (and lm78_driver is still present)
458 We block updates of the ISA device to minimize the risk of concurrent
459 access to the same LM78 chip through different interfaces. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460static int lm78_attach_adapter(struct i2c_adapter *adapter)
461{
Jean Delvare18c73f92008-10-17 17:51:15 +0200462 struct lm78_data *data;
463 int err;
464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 if (!(adapter->class & I2C_CLASS_HWMON))
466 return 0;
Jean Delvare18c73f92008-10-17 17:51:15 +0200467
468 data = pdev ? platform_get_drvdata(pdev) : NULL;
469 if (data)
470 mutex_lock(&data->update_lock);
471 err = i2c_probe(adapter, &addr_data, lm78_detect);
472 if (data)
473 mutex_unlock(&data->update_lock);
474 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475}
476
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200477static struct attribute *lm78_attributes[] = {
Jean Delvare247dde42007-05-08 17:22:01 +0200478 &sensor_dev_attr_in0_input.dev_attr.attr,
479 &sensor_dev_attr_in0_min.dev_attr.attr,
480 &sensor_dev_attr_in0_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200481 &sensor_dev_attr_in0_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200482 &sensor_dev_attr_in1_input.dev_attr.attr,
483 &sensor_dev_attr_in1_min.dev_attr.attr,
484 &sensor_dev_attr_in1_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200485 &sensor_dev_attr_in1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200486 &sensor_dev_attr_in2_input.dev_attr.attr,
487 &sensor_dev_attr_in2_min.dev_attr.attr,
488 &sensor_dev_attr_in2_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200489 &sensor_dev_attr_in2_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200490 &sensor_dev_attr_in3_input.dev_attr.attr,
491 &sensor_dev_attr_in3_min.dev_attr.attr,
492 &sensor_dev_attr_in3_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200493 &sensor_dev_attr_in3_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200494 &sensor_dev_attr_in4_input.dev_attr.attr,
495 &sensor_dev_attr_in4_min.dev_attr.attr,
496 &sensor_dev_attr_in4_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200497 &sensor_dev_attr_in4_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200498 &sensor_dev_attr_in5_input.dev_attr.attr,
499 &sensor_dev_attr_in5_min.dev_attr.attr,
500 &sensor_dev_attr_in5_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200501 &sensor_dev_attr_in5_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200502 &sensor_dev_attr_in6_input.dev_attr.attr,
503 &sensor_dev_attr_in6_min.dev_attr.attr,
504 &sensor_dev_attr_in6_max.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200505 &sensor_dev_attr_in6_alarm.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200506 &dev_attr_temp1_input.attr,
507 &dev_attr_temp1_max.attr,
508 &dev_attr_temp1_max_hyst.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200509 &sensor_dev_attr_temp1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200510 &sensor_dev_attr_fan1_input.dev_attr.attr,
511 &sensor_dev_attr_fan1_min.dev_attr.attr,
512 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200513 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200514 &sensor_dev_attr_fan2_input.dev_attr.attr,
515 &sensor_dev_attr_fan2_min.dev_attr.attr,
516 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200517 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvare247dde42007-05-08 17:22:01 +0200518 &sensor_dev_attr_fan3_input.dev_attr.attr,
519 &sensor_dev_attr_fan3_min.dev_attr.attr,
520 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare428a7032007-09-04 23:25:33 +0200521 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200522 &dev_attr_alarms.attr,
523 &dev_attr_cpu0_vid.attr,
524
525 NULL
526};
527
528static const struct attribute_group lm78_group = {
529 .attrs = lm78_attributes,
530};
531
Jean Delvarec40769f2007-05-08 17:22:00 +0200532/* I2C devices get this name attribute automatically, but for ISA devices
533 we must create it by ourselves. */
534static ssize_t show_name(struct device *dev, struct device_attribute
535 *devattr, char *buf)
536{
537 struct lm78_data *data = dev_get_drvdata(dev);
538
539 return sprintf(buf, "%s\n", data->client.name);
540}
541static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
542
Jean Delvare18c73f92008-10-17 17:51:15 +0200543/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
544static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
545{
546 struct lm78_data *i2c, *isa;
547 int i;
548
549 if (!pdev) /* No ISA chip */
550 return 0;
551
552 i2c = i2c_get_clientdata(client);
553 isa = platform_get_drvdata(pdev);
554
555 if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
556 return 0; /* Address doesn't match */
557 if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
558 return 0; /* Chip type doesn't match */
559
560 /* We compare all the limit registers, the config register and the
561 * interrupt mask registers */
562 for (i = 0x2b; i <= 0x3d; i++) {
563 if (lm78_read_value(isa, i) != lm78_read_value(i2c, i))
564 return 0;
565 }
566 if (lm78_read_value(isa, LM78_REG_CONFIG) !=
567 lm78_read_value(i2c, LM78_REG_CONFIG))
568 return 0;
569 for (i = 0x43; i <= 0x46; i++) {
570 if (lm78_read_value(isa, i) != lm78_read_value(i2c, i))
571 return 0;
572 }
573
574 return 1;
575}
576
Jean Delvare2ed2dc32005-07-31 21:42:02 +0200577/* This function is called by i2c_probe */
Ben Dooksd8d20612005-10-26 21:05:46 +0200578static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
580 int i, err;
581 struct i2c_client *new_client;
582 struct lm78_data *data;
583 const char *client_name = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Jean Delvarec40769f2007-05-08 17:22:00 +0200585 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 err = -ENODEV;
Jean Delvarec40769f2007-05-08 17:22:00 +0200587 goto ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 }
589
590 /* OK. For now, we presume we have a valid client. We now create the
591 client structure, even though we cannot fill it completely yet.
592 But it allows us to access lm78_{read,write}_value. */
593
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200594 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 err = -ENOMEM;
596 goto ERROR1;
597 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
599 new_client = &data->client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 i2c_set_clientdata(new_client, data);
601 new_client->addr = address;
602 new_client->adapter = adapter;
Jean Delvarec40769f2007-05-08 17:22:00 +0200603 new_client->driver = &lm78_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
605 /* Now, we do the remaining detection. */
606 if (kind < 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200607 if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 err = -ENODEV;
609 goto ERROR2;
610 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200611 if (lm78_read_value(data, LM78_REG_I2C_ADDR) !=
Jean Delvarec40769f2007-05-08 17:22:00 +0200612 address) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 err = -ENODEV;
614 goto ERROR2;
615 }
616 }
617
618 /* Determine the chip type. */
619 if (kind <= 0) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200620 i = lm78_read_value(data, LM78_REG_CHIPID);
Jean Delvare27fe0482005-07-27 21:30:16 +0200621 if (i == 0x00 || i == 0x20 /* LM78 */
622 || i == 0x40) /* LM78-J */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 kind = lm78;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 else if ((i & 0xfe) == 0xc0)
625 kind = lm79;
626 else {
627 if (kind == 0)
628 dev_warn(&adapter->dev, "Ignoring 'force' "
629 "parameter for unknown chip at "
630 "adapter %d, address 0x%02x\n",
631 i2c_adapter_id(adapter), address);
632 err = -ENODEV;
633 goto ERROR2;
634 }
Jean Delvare18c73f92008-10-17 17:51:15 +0200635
636 if (lm78_alias_detect(new_client, i)) {
637 dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
638 "be the same as ISA device\n", address);
639 err = -ENODEV;
640 goto ERROR2;
641 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 }
643
644 if (kind == lm78) {
645 client_name = "lm78";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 } else if (kind == lm79) {
647 client_name = "lm79";
648 }
649
650 /* Fill in the remaining client fields and put into the global list */
651 strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
652 data->type = kind;
653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 /* Tell the I2C layer a new client has arrived */
655 if ((err = i2c_attach_client(new_client)))
656 goto ERROR2;
657
658 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200659 lm78_init_device(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 /* Register sysfs hooks */
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200662 if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
663 goto ERROR3;
664
Tony Jones1beeffe2007-08-20 13:46:20 -0700665 data->hwmon_dev = hwmon_device_register(&new_client->dev);
666 if (IS_ERR(data->hwmon_dev)) {
667 err = PTR_ERR(data->hwmon_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200668 goto ERROR4;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400669 }
670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 return 0;
672
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200673ERROR4:
674 sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400675ERROR3:
676 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677ERROR2:
678 kfree(data);
679ERROR1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 return err;
681}
682
683static int lm78_detach_client(struct i2c_client *client)
684{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400685 struct lm78_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 int err;
687
Tony Jones1beeffe2007-08-20 13:46:20 -0700688 hwmon_device_unregister(data->hwmon_dev);
Mark M. Hoffmanc1685f62006-09-24 20:59:49 +0200689 sysfs_remove_group(&client->dev.kobj, &lm78_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400690
Jean Delvare7bef5592005-07-27 22:14:49 +0200691 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Jean Delvarec40769f2007-05-08 17:22:00 +0200694 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Jean Delvarec40769f2007-05-08 17:22:00 +0200696 return 0;
697}
698
699static int __devinit lm78_isa_probe(struct platform_device *pdev)
700{
701 int err;
702 struct lm78_data *data;
703 struct resource *res;
704 const char *name;
705
706 /* Reserve the ISA region */
707 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvare47c15532008-10-17 17:51:15 +0200708 if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) {
Jean Delvarec40769f2007-05-08 17:22:00 +0200709 err = -EBUSY;
710 goto exit;
711 }
712
713 if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
714 err = -ENOMEM;
715 goto exit_release_region;
716 }
717 mutex_init(&data->lock);
718 data->client.addr = res->start;
719 i2c_set_clientdata(&data->client, data);
720 platform_set_drvdata(pdev, data);
721
Jean Delvarec59cc302007-05-08 17:22:01 +0200722 if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
Jean Delvarec40769f2007-05-08 17:22:00 +0200723 data->type = lm79;
724 name = "lm79";
725 } else {
726 data->type = lm78;
727 name = "lm78";
728 }
729 strlcpy(data->client.name, name, I2C_NAME_SIZE);
730
731 /* Initialize the LM78 chip */
Jean Delvarec59cc302007-05-08 17:22:01 +0200732 lm78_init_device(data);
Jean Delvarec40769f2007-05-08 17:22:00 +0200733
734 /* Register sysfs hooks */
735 if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group))
736 || (err = device_create_file(&pdev->dev, &dev_attr_name)))
737 goto exit_remove_files;
738
Tony Jones1beeffe2007-08-20 13:46:20 -0700739 data->hwmon_dev = hwmon_device_register(&pdev->dev);
740 if (IS_ERR(data->hwmon_dev)) {
741 err = PTR_ERR(data->hwmon_dev);
Jean Delvarec40769f2007-05-08 17:22:00 +0200742 goto exit_remove_files;
743 }
744
745 return 0;
746
747 exit_remove_files:
748 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
749 device_remove_file(&pdev->dev, &dev_attr_name);
750 kfree(data);
751 exit_release_region:
Jean Delvare47c15532008-10-17 17:51:15 +0200752 release_region(res->start + LM78_ADDR_REG_OFFSET, 2);
Jean Delvarec40769f2007-05-08 17:22:00 +0200753 exit:
754 return err;
755}
756
757static int __devexit lm78_isa_remove(struct platform_device *pdev)
758{
759 struct lm78_data *data = platform_get_drvdata(pdev);
760
Tony Jones1beeffe2007-08-20 13:46:20 -0700761 hwmon_device_unregister(data->hwmon_dev);
Jean Delvarec40769f2007-05-08 17:22:00 +0200762 sysfs_remove_group(&pdev->dev.kobj, &lm78_group);
763 device_remove_file(&pdev->dev, &dev_attr_name);
Jean Delvare47c15532008-10-17 17:51:15 +0200764 release_region(data->client.addr + LM78_ADDR_REG_OFFSET, 2);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400765 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
767 return 0;
768}
769
Steven Cole44bbe872005-05-03 18:21:25 -0600770/* The SMBus locks itself, but ISA access must be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 We don't want to lock the whole ISA bus, so we lock each client
772 separately.
773 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
774 would slow down the LM78 access and should not be necessary. */
Jean Delvarec59cc302007-05-08 17:22:01 +0200775static int lm78_read_value(struct lm78_data *data, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776{
Jean Delvarec59cc302007-05-08 17:22:01 +0200777 struct i2c_client *client = &data->client;
778
Jean Delvarec40769f2007-05-08 17:22:00 +0200779 if (!client->driver) { /* ISA device */
Jean Delvarec59cc302007-05-08 17:22:01 +0200780 int res;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100781 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
783 res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100784 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 return res;
786 } else
787 return i2c_smbus_read_byte_data(client, reg);
788}
789
Steven Cole44bbe872005-05-03 18:21:25 -0600790/* The SMBus locks itself, but ISA access muse be locked explicitly!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 We don't want to lock the whole ISA bus, so we lock each client
792 separately.
793 We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
794 would slow down the LM78 access and should not be necessary.
795 There are some ugly typecasts here, but the good new is - they should
796 nowhere else be necessary! */
Jean Delvarec59cc302007-05-08 17:22:01 +0200797static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
Jean Delvarec59cc302007-05-08 17:22:01 +0200799 struct i2c_client *client = &data->client;
800
Jean Delvarec40769f2007-05-08 17:22:00 +0200801 if (!client->driver) { /* ISA device */
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100802 mutex_lock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
804 outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100805 mutex_unlock(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 return 0;
807 } else
808 return i2c_smbus_write_byte_data(client, reg, value);
809}
810
Jean Delvarec59cc302007-05-08 17:22:01 +0200811static void lm78_init_device(struct lm78_data *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812{
Jean Delvarec40769f2007-05-08 17:22:00 +0200813 u8 config;
814 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
816 /* Start monitoring */
Jean Delvarec59cc302007-05-08 17:22:01 +0200817 config = lm78_read_value(data, LM78_REG_CONFIG);
Jean Delvarec40769f2007-05-08 17:22:00 +0200818 if ((config & 0x09) != 0x01)
Jean Delvarec59cc302007-05-08 17:22:01 +0200819 lm78_write_value(data, LM78_REG_CONFIG,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 (config & 0xf7) | 0x01);
Jean Delvarec40769f2007-05-08 17:22:00 +0200821
822 /* A few vars need to be filled upon startup */
823 for (i = 0; i < 3; i++) {
Jean Delvarec59cc302007-05-08 17:22:01 +0200824 data->fan_min[i] = lm78_read_value(data,
Jean Delvarec40769f2007-05-08 17:22:00 +0200825 LM78_REG_FAN_MIN(i));
826 }
827
828 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829}
830
831static struct lm78_data *lm78_update_device(struct device *dev)
832{
Jean Delvarec40769f2007-05-08 17:22:00 +0200833 struct lm78_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 int i;
835
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100836 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
839 || !data->valid) {
840
Jean Delvarec40769f2007-05-08 17:22:00 +0200841 dev_dbg(dev, "Starting lm78 update\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
843 for (i = 0; i <= 6; i++) {
844 data->in[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200845 lm78_read_value(data, LM78_REG_IN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 data->in_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200847 lm78_read_value(data, LM78_REG_IN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 data->in_max[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200849 lm78_read_value(data, LM78_REG_IN_MAX(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 }
851 for (i = 0; i < 3; i++) {
852 data->fan[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200853 lm78_read_value(data, LM78_REG_FAN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 data->fan_min[i] =
Jean Delvarec59cc302007-05-08 17:22:01 +0200855 lm78_read_value(data, LM78_REG_FAN_MIN(i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 }
Jean Delvarec59cc302007-05-08 17:22:01 +0200857 data->temp = lm78_read_value(data, LM78_REG_TEMP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 data->temp_over =
Jean Delvarec59cc302007-05-08 17:22:01 +0200859 lm78_read_value(data, LM78_REG_TEMP_OVER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 data->temp_hyst =
Jean Delvarec59cc302007-05-08 17:22:01 +0200861 lm78_read_value(data, LM78_REG_TEMP_HYST);
862 i = lm78_read_value(data, LM78_REG_VID_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 data->vid = i & 0x0f;
864 if (data->type == lm79)
865 data->vid |=
Jean Delvarec59cc302007-05-08 17:22:01 +0200866 (lm78_read_value(data, LM78_REG_CHIPID) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 0x01) << 4;
868 else
869 data->vid |= 0x10;
870 data->fan_div[0] = (i >> 4) & 0x03;
871 data->fan_div[1] = i >> 6;
Jean Delvarec59cc302007-05-08 17:22:01 +0200872 data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
873 (lm78_read_value(data, LM78_REG_ALARM2) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 data->last_updated = jiffies;
875 data->valid = 1;
876
877 data->fan_div[2] = 1;
878 }
879
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100880 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
882 return data;
883}
884
Jean Delvarec40769f2007-05-08 17:22:00 +0200885/* return 1 if a supported chip is found, 0 otherwise */
886static int __init lm78_isa_found(unsigned short address)
887{
888 int val, save, found = 0;
889
Jean Delvare47c15532008-10-17 17:51:15 +0200890 /* We have to request the region in two parts because some
891 boards declare base+4 to base+7 as a PNP device */
892 if (!request_region(address, 4, "lm78")) {
893 pr_debug("lm78: Failed to request low part of region\n");
Jean Delvarec40769f2007-05-08 17:22:00 +0200894 return 0;
Jean Delvare47c15532008-10-17 17:51:15 +0200895 }
896 if (!request_region(address + 4, 4, "lm78")) {
897 pr_debug("lm78: Failed to request high part of region\n");
898 release_region(address, 4);
899 return 0;
900 }
Jean Delvarec40769f2007-05-08 17:22:00 +0200901
902#define REALLY_SLOW_IO
903 /* We need the timeouts for at least some LM78-like
904 chips. But only if we read 'undefined' registers. */
905 val = inb_p(address + 1);
906 if (inb_p(address + 2) != val
907 || inb_p(address + 3) != val
908 || inb_p(address + 7) != val)
909 goto release;
910#undef REALLY_SLOW_IO
911
912 /* We should be able to change the 7 LSB of the address port. The
913 MSB (busy flag) should be clear initially, set after the write. */
914 save = inb_p(address + LM78_ADDR_REG_OFFSET);
915 if (save & 0x80)
916 goto release;
917 val = ~save & 0x7f;
918 outb_p(val, address + LM78_ADDR_REG_OFFSET);
919 if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
920 outb_p(save, address + LM78_ADDR_REG_OFFSET);
921 goto release;
922 }
923
924 /* We found a device, now see if it could be an LM78 */
925 outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
926 val = inb_p(address + LM78_DATA_REG_OFFSET);
927 if (val & 0x80)
928 goto release;
929 outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
930 val = inb_p(address + LM78_DATA_REG_OFFSET);
931 if (val < 0x03 || val > 0x77) /* Not a valid I2C address */
932 goto release;
933
934 /* The busy flag should be clear again */
935 if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
936 goto release;
937
938 /* Explicitly prevent the misdetection of Winbond chips */
939 outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
940 val = inb_p(address + LM78_DATA_REG_OFFSET);
941 if (val == 0xa3 || val == 0x5c)
942 goto release;
943
944 /* Explicitly prevent the misdetection of ITE chips */
945 outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
946 val = inb_p(address + LM78_DATA_REG_OFFSET);
947 if (val == 0x90)
948 goto release;
949
950 /* Determine the chip type */
951 outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
952 val = inb_p(address + LM78_DATA_REG_OFFSET);
Hans de Goedeacf346a2007-07-24 23:36:00 +0200953 if (val == 0x00 || val == 0x20 /* LM78 */
Jean Delvarec40769f2007-05-08 17:22:00 +0200954 || val == 0x40 /* LM78-J */
955 || (val & 0xfe) == 0xc0) /* LM79 */
956 found = 1;
957
958 if (found)
959 pr_info("lm78: Found an %s chip at %#x\n",
960 val & 0x80 ? "LM79" : "LM78", (int)address);
961
962 release:
Jean Delvare47c15532008-10-17 17:51:15 +0200963 release_region(address + 4, 4);
964 release_region(address, 4);
Jean Delvarec40769f2007-05-08 17:22:00 +0200965 return found;
966}
967
968static int __init lm78_isa_device_add(unsigned short address)
969{
970 struct resource res = {
971 .start = address,
Jean Delvare15bde2f2007-08-29 10:39:57 +0200972 .end = address + LM78_EXTENT - 1,
Jean Delvarec40769f2007-05-08 17:22:00 +0200973 .name = "lm78",
974 .flags = IORESOURCE_IO,
975 };
976 int err;
977
978 pdev = platform_device_alloc("lm78", address);
979 if (!pdev) {
980 err = -ENOMEM;
981 printk(KERN_ERR "lm78: Device allocation failed\n");
982 goto exit;
983 }
984
985 err = platform_device_add_resources(pdev, &res, 1);
986 if (err) {
987 printk(KERN_ERR "lm78: Device resource addition failed "
988 "(%d)\n", err);
989 goto exit_device_put;
990 }
991
992 err = platform_device_add(pdev);
993 if (err) {
994 printk(KERN_ERR "lm78: Device addition failed (%d)\n",
995 err);
996 goto exit_device_put;
997 }
998
999 return 0;
1000
1001 exit_device_put:
1002 platform_device_put(pdev);
1003 exit:
1004 pdev = NULL;
1005 return err;
1006}
1007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008static int __init sm_lm78_init(void)
1009{
Jean Delvarefde09502005-07-19 23:51:07 +02001010 int res;
1011
Jean Delvare18c73f92008-10-17 17:51:15 +02001012 /* We register the ISA device first, so that we can skip the
1013 * registration of an I2C interface to the same device. */
Jean Delvarec40769f2007-05-08 17:22:00 +02001014 if (lm78_isa_found(isa_address)) {
1015 res = platform_driver_register(&lm78_isa_driver);
1016 if (res)
Jean Delvare18c73f92008-10-17 17:51:15 +02001017 goto exit;
Jean Delvarec40769f2007-05-08 17:22:00 +02001018
1019 /* Sets global pdev as a side effect */
1020 res = lm78_isa_device_add(isa_address);
1021 if (res)
1022 goto exit_unreg_isa_driver;
1023 }
Jean Delvarefde09502005-07-19 23:51:07 +02001024
Jean Delvare18c73f92008-10-17 17:51:15 +02001025 res = i2c_add_driver(&lm78_driver);
1026 if (res)
1027 goto exit_unreg_isa_device;
1028
Jean Delvarefde09502005-07-19 23:51:07 +02001029 return 0;
Jean Delvarec40769f2007-05-08 17:22:00 +02001030
Jean Delvare18c73f92008-10-17 17:51:15 +02001031 exit_unreg_isa_device:
1032 platform_device_unregister(pdev);
Jean Delvarec40769f2007-05-08 17:22:00 +02001033 exit_unreg_isa_driver:
1034 platform_driver_unregister(&lm78_isa_driver);
Jean Delvarec40769f2007-05-08 17:22:00 +02001035 exit:
1036 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037}
1038
1039static void __exit sm_lm78_exit(void)
1040{
Jean Delvarec40769f2007-05-08 17:22:00 +02001041 if (pdev) {
1042 platform_device_unregister(pdev);
1043 platform_driver_unregister(&lm78_isa_driver);
1044 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 i2c_del_driver(&lm78_driver);
1046}
1047
1048
1049
1050MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
Jean Delvare27fe0482005-07-27 21:30:16 +02001051MODULE_DESCRIPTION("LM78/LM79 driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052MODULE_LICENSE("GPL");
1053
1054module_init(sm_lm78_init);
1055module_exit(sm_lm78_exit);