blob: 40301bc6ce186e5a1c92c7a7d9964e945807dac7 [file] [log] [blame]
Jean Delvare08e7e272005-04-25 22:43:25 +02001/*
2 w83627ehf - Driver for the hardware monitoring functionality of
3 the Winbond W83627EHF Super-I/O chip
4 Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
5
6 Shamelessly ripped from the w83627hf driver
7 Copyright (C) 2003 Mark Studebaker
8
9 Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
10 in testing and debugging this driver.
11
Jean Delvare8dd2d2c2005-07-27 21:33:15 +020012 This driver also supports the W83627EHG, which is the lead-free
13 version of the W83627EHF.
14
Jean Delvare08e7e272005-04-25 22:43:25 +020015 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28
29
30 Supports the following chips:
31
32 Chip #vin #fan #pwm #temp chip_id man_id
Rudolf Marekcf0676f2006-03-23 16:25:22 +010033 w83627ehf 10 5 - 3 0x88 0x5ca3
Jean Delvare08e7e272005-04-25 22:43:25 +020034*/
35
36#include <linux/module.h>
37#include <linux/init.h>
38#include <linux/slab.h>
39#include <linux/i2c.h>
Jean Delvarefde09502005-07-19 23:51:07 +020040#include <linux/i2c-isa.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040041#include <linux/hwmon.h>
Yuan Mu412fec82006-02-05 23:24:16 +010042#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040043#include <linux/err.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010044#include <linux/mutex.h>
Jean Delvare08e7e272005-04-25 22:43:25 +020045#include <asm/io.h>
46#include "lm75.h"
47
Jean Delvare2d8672c2005-07-19 23:56:35 +020048/* The actual ISA address is read from Super-I/O configuration space */
49static unsigned short address;
Jean Delvare08e7e272005-04-25 22:43:25 +020050
51/*
52 * Super-I/O constants and functions
53 */
54
55static int REG; /* The register to read/write */
56static int VAL; /* The value to read/write */
57
58#define W83627EHF_LD_HWM 0x0b
59
60#define SIO_REG_LDSEL 0x07 /* Logical device select */
61#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
62#define SIO_REG_ENABLE 0x30 /* Logical device enable */
63#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
64
65#define SIO_W83627EHF_ID 0x8840
66#define SIO_ID_MASK 0xFFC0
67
68static inline void
69superio_outb(int reg, int val)
70{
71 outb(reg, REG);
72 outb(val, VAL);
73}
74
75static inline int
76superio_inb(int reg)
77{
78 outb(reg, REG);
79 return inb(VAL);
80}
81
82static inline void
83superio_select(int ld)
84{
85 outb(SIO_REG_LDSEL, REG);
86 outb(ld, VAL);
87}
88
89static inline void
90superio_enter(void)
91{
92 outb(0x87, REG);
93 outb(0x87, REG);
94}
95
96static inline void
97superio_exit(void)
98{
99 outb(0x02, REG);
100 outb(0x02, VAL);
101}
102
103/*
104 * ISA constants
105 */
106
Petr Vandrovecada0c2f2005-10-07 23:11:03 +0200107#define REGION_ALIGNMENT ~7
108#define REGION_OFFSET 5
109#define REGION_LENGTH 2
Jean Delvare08e7e272005-04-25 22:43:25 +0200110#define ADDR_REG_OFFSET 5
111#define DATA_REG_OFFSET 6
112
113#define W83627EHF_REG_BANK 0x4E
114#define W83627EHF_REG_CONFIG 0x40
115#define W83627EHF_REG_CHIP_ID 0x49
116#define W83627EHF_REG_MAN_ID 0x4F
117
118static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
119static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
120
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100121/* The W83627EHF registers for nr=7,8,9 are in bank 5 */
122#define W83627EHF_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
123 (0x554 + (((nr) - 7) * 2)))
124#define W83627EHF_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
125 (0x555 + (((nr) - 7) * 2)))
126#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
127 (0x550 + (nr) - 7))
128
Jean Delvare08e7e272005-04-25 22:43:25 +0200129#define W83627EHF_REG_TEMP1 0x27
130#define W83627EHF_REG_TEMP1_HYST 0x3a
131#define W83627EHF_REG_TEMP1_OVER 0x39
132static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };
133static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };
134static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };
135static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
136
137/* Fan clock dividers are spread over the following five registers */
138#define W83627EHF_REG_FANDIV1 0x47
139#define W83627EHF_REG_FANDIV2 0x4B
140#define W83627EHF_REG_VBAT 0x5D
141#define W83627EHF_REG_DIODE 0x59
142#define W83627EHF_REG_SMI_OVT 0x4C
143
Jean Delvarea4589db2006-03-23 16:30:29 +0100144#define W83627EHF_REG_ALARM1 0x459
145#define W83627EHF_REG_ALARM2 0x45A
146#define W83627EHF_REG_ALARM3 0x45B
147
Jean Delvare08e7e272005-04-25 22:43:25 +0200148/*
149 * Conversions
150 */
151
152static inline unsigned int
153fan_from_reg(u8 reg, unsigned int div)
154{
155 if (reg == 0 || reg == 255)
156 return 0;
157 return 1350000U / (reg * div);
158}
159
160static inline unsigned int
161div_from_reg(u8 reg)
162{
163 return 1 << reg;
164}
165
166static inline int
167temp1_from_reg(s8 reg)
168{
169 return reg * 1000;
170}
171
172static inline s8
173temp1_to_reg(int temp)
174{
175 if (temp <= -128000)
176 return -128;
177 if (temp >= 127000)
178 return 127;
179 if (temp < 0)
180 return (temp - 500) / 1000;
181 return (temp + 500) / 1000;
182}
183
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100184/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */
185
186static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };
187
188static inline long in_from_reg(u8 reg, u8 nr)
189{
190 return reg * scale_in[nr];
191}
192
193static inline u8 in_to_reg(u32 val, u8 nr)
194{
195 return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255);
196}
197
Jean Delvare08e7e272005-04-25 22:43:25 +0200198/*
199 * Data structures and manipulation thereof
200 */
201
202struct w83627ehf_data {
203 struct i2c_client client;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400204 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100205 struct mutex lock;
Jean Delvare08e7e272005-04-25 22:43:25 +0200206
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100207 struct mutex update_lock;
Jean Delvare08e7e272005-04-25 22:43:25 +0200208 char valid; /* !=0 if following fields are valid */
209 unsigned long last_updated; /* In jiffies */
210
211 /* Register values */
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100212 u8 in[10]; /* Register value */
213 u8 in_max[10]; /* Register value */
214 u8 in_min[10]; /* Register value */
Jean Delvare08e7e272005-04-25 22:43:25 +0200215 u8 fan[5];
216 u8 fan_min[5];
217 u8 fan_div[5];
218 u8 has_fan; /* some fan inputs can be disabled */
219 s8 temp1;
220 s8 temp1_max;
221 s8 temp1_max_hyst;
222 s16 temp[2];
223 s16 temp_max[2];
224 s16 temp_max_hyst[2];
Jean Delvarea4589db2006-03-23 16:30:29 +0100225 u32 alarms;
Jean Delvare08e7e272005-04-25 22:43:25 +0200226};
227
228static inline int is_word_sized(u16 reg)
229{
230 return (((reg & 0xff00) == 0x100
231 || (reg & 0xff00) == 0x200)
232 && ((reg & 0x00ff) == 0x50
233 || (reg & 0x00ff) == 0x53
234 || (reg & 0x00ff) == 0x55));
235}
236
237/* We assume that the default bank is 0, thus the following two functions do
238 nothing for registers which live in bank 0. For others, they respectively
239 set the bank register to the correct value (before the register is
240 accessed), and back to 0 (afterwards). */
241static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg)
242{
243 if (reg & 0xff00) {
244 outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
245 outb_p(reg >> 8, client->addr + DATA_REG_OFFSET);
246 }
247}
248
249static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg)
250{
251 if (reg & 0xff00) {
252 outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
253 outb_p(0, client->addr + DATA_REG_OFFSET);
254 }
255}
256
257static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg)
258{
259 struct w83627ehf_data *data = i2c_get_clientdata(client);
260 int res, word_sized = is_word_sized(reg);
261
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100262 mutex_lock(&data->lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200263
264 w83627ehf_set_bank(client, reg);
265 outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
266 res = inb_p(client->addr + DATA_REG_OFFSET);
267 if (word_sized) {
268 outb_p((reg & 0xff) + 1,
269 client->addr + ADDR_REG_OFFSET);
270 res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET);
271 }
272 w83627ehf_reset_bank(client, reg);
273
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100274 mutex_unlock(&data->lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200275
276 return res;
277}
278
279static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value)
280{
281 struct w83627ehf_data *data = i2c_get_clientdata(client);
282 int word_sized = is_word_sized(reg);
283
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100284 mutex_lock(&data->lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200285
286 w83627ehf_set_bank(client, reg);
287 outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
288 if (word_sized) {
289 outb_p(value >> 8, client->addr + DATA_REG_OFFSET);
290 outb_p((reg & 0xff) + 1,
291 client->addr + ADDR_REG_OFFSET);
292 }
293 outb_p(value & 0xff, client->addr + DATA_REG_OFFSET);
294 w83627ehf_reset_bank(client, reg);
295
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100296 mutex_unlock(&data->lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200297 return 0;
298}
299
300/* This function assumes that the caller holds data->update_lock */
301static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
302{
303 struct w83627ehf_data *data = i2c_get_clientdata(client);
304 u8 reg;
305
306 switch (nr) {
307 case 0:
308 reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf)
309 | ((data->fan_div[0] & 0x03) << 4);
310 w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
311 reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf)
312 | ((data->fan_div[0] & 0x04) << 3);
313 w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
314 break;
315 case 1:
316 reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f)
317 | ((data->fan_div[1] & 0x03) << 6);
318 w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
319 reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf)
320 | ((data->fan_div[1] & 0x04) << 4);
321 w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
322 break;
323 case 2:
324 reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f)
325 | ((data->fan_div[2] & 0x03) << 6);
326 w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg);
327 reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f)
328 | ((data->fan_div[2] & 0x04) << 5);
329 w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
330 break;
331 case 3:
332 reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc)
333 | (data->fan_div[3] & 0x03);
334 w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
335 reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f)
336 | ((data->fan_div[3] & 0x04) << 5);
337 w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg);
338 break;
339 case 4:
340 reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
341 | ((data->fan_div[4] & 0x03) << 3)
342 | ((data->fan_div[4] & 0x04) << 5);
343 w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
344 break;
345 }
346}
347
348static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
349{
350 struct i2c_client *client = to_i2c_client(dev);
351 struct w83627ehf_data *data = i2c_get_clientdata(client);
352 int i;
353
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100354 mutex_lock(&data->update_lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200355
356 if (time_after(jiffies, data->last_updated + HZ)
357 || !data->valid) {
358 /* Fan clock dividers */
359 i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
360 data->fan_div[0] = (i >> 4) & 0x03;
361 data->fan_div[1] = (i >> 6) & 0x03;
362 i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2);
363 data->fan_div[2] = (i >> 6) & 0x03;
364 i = w83627ehf_read_value(client, W83627EHF_REG_VBAT);
365 data->fan_div[0] |= (i >> 3) & 0x04;
366 data->fan_div[1] |= (i >> 4) & 0x04;
367 data->fan_div[2] |= (i >> 5) & 0x04;
368 if (data->has_fan & ((1 << 3) | (1 << 4))) {
369 i = w83627ehf_read_value(client, W83627EHF_REG_DIODE);
370 data->fan_div[3] = i & 0x03;
371 data->fan_div[4] = ((i >> 2) & 0x03)
372 | ((i >> 5) & 0x04);
373 }
374 if (data->has_fan & (1 << 3)) {
375 i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT);
376 data->fan_div[3] |= (i >> 5) & 0x04;
377 }
378
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100379 /* Measured voltages and limits */
380 for (i = 0; i < 10; i++) {
381 data->in[i] = w83627ehf_read_value(client,
382 W83627EHF_REG_IN(i));
383 data->in_min[i] = w83627ehf_read_value(client,
384 W83627EHF_REG_IN_MIN(i));
385 data->in_max[i] = w83627ehf_read_value(client,
386 W83627EHF_REG_IN_MAX(i));
387 }
388
Jean Delvare08e7e272005-04-25 22:43:25 +0200389 /* Measured fan speeds and limits */
390 for (i = 0; i < 5; i++) {
391 if (!(data->has_fan & (1 << i)))
392 continue;
393
394 data->fan[i] = w83627ehf_read_value(client,
395 W83627EHF_REG_FAN[i]);
396 data->fan_min[i] = w83627ehf_read_value(client,
397 W83627EHF_REG_FAN_MIN[i]);
398
399 /* If we failed to measure the fan speed and clock
400 divider can be increased, let's try that for next
401 time */
402 if (data->fan[i] == 0xff
403 && data->fan_div[i] < 0x07) {
404 dev_dbg(&client->dev, "Increasing fan %d "
405 "clock divider from %u to %u\n",
406 i, div_from_reg(data->fan_div[i]),
407 div_from_reg(data->fan_div[i] + 1));
408 data->fan_div[i]++;
409 w83627ehf_write_fan_div(client, i);
410 /* Preserve min limit if possible */
411 if (data->fan_min[i] >= 2
412 && data->fan_min[i] != 255)
413 w83627ehf_write_value(client,
414 W83627EHF_REG_FAN_MIN[i],
415 (data->fan_min[i] /= 2));
416 }
417 }
418
419 /* Measured temperatures and limits */
420 data->temp1 = w83627ehf_read_value(client,
421 W83627EHF_REG_TEMP1);
422 data->temp1_max = w83627ehf_read_value(client,
423 W83627EHF_REG_TEMP1_OVER);
424 data->temp1_max_hyst = w83627ehf_read_value(client,
425 W83627EHF_REG_TEMP1_HYST);
426 for (i = 0; i < 2; i++) {
427 data->temp[i] = w83627ehf_read_value(client,
428 W83627EHF_REG_TEMP[i]);
429 data->temp_max[i] = w83627ehf_read_value(client,
430 W83627EHF_REG_TEMP_OVER[i]);
431 data->temp_max_hyst[i] = w83627ehf_read_value(client,
432 W83627EHF_REG_TEMP_HYST[i]);
433 }
434
Jean Delvarea4589db2006-03-23 16:30:29 +0100435 data->alarms = w83627ehf_read_value(client,
436 W83627EHF_REG_ALARM1) |
437 (w83627ehf_read_value(client,
438 W83627EHF_REG_ALARM2) << 8) |
439 (w83627ehf_read_value(client,
440 W83627EHF_REG_ALARM3) << 16);
441
Jean Delvare08e7e272005-04-25 22:43:25 +0200442 data->last_updated = jiffies;
443 data->valid = 1;
444 }
445
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100446 mutex_unlock(&data->update_lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200447 return data;
448}
449
450/*
451 * Sysfs callback functions
452 */
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100453#define show_in_reg(reg) \
454static ssize_t \
455show_##reg(struct device *dev, struct device_attribute *attr, \
456 char *buf) \
457{ \
458 struct w83627ehf_data *data = w83627ehf_update_device(dev); \
459 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
460 int nr = sensor_attr->index; \
461 return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \
462}
463show_in_reg(in)
464show_in_reg(in_min)
465show_in_reg(in_max)
466
467#define store_in_reg(REG, reg) \
468static ssize_t \
469store_in_##reg (struct device *dev, struct device_attribute *attr, \
470 const char *buf, size_t count) \
471{ \
472 struct i2c_client *client = to_i2c_client(dev); \
473 struct w83627ehf_data *data = i2c_get_clientdata(client); \
474 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
475 int nr = sensor_attr->index; \
476 u32 val = simple_strtoul(buf, NULL, 10); \
477 \
478 mutex_lock(&data->update_lock); \
479 data->in_##reg[nr] = in_to_reg(val, nr); \
480 w83627ehf_write_value(client, W83627EHF_REG_IN_##REG(nr), \
481 data->in_##reg[nr]); \
482 mutex_unlock(&data->update_lock); \
483 return count; \
484}
485
486store_in_reg(MIN, min)
487store_in_reg(MAX, max)
488
Jean Delvarea4589db2006-03-23 16:30:29 +0100489static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
490{
491 struct w83627ehf_data *data = w83627ehf_update_device(dev);
492 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
493 int nr = sensor_attr->index;
494 return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01);
495}
496
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100497static struct sensor_device_attribute sda_in_input[] = {
498 SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
499 SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
500 SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
501 SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
502 SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
503 SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
504 SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
505 SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
506 SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
507 SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9),
508};
509
Jean Delvarea4589db2006-03-23 16:30:29 +0100510static struct sensor_device_attribute sda_in_alarm[] = {
511 SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0),
512 SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1),
513 SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2),
514 SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3),
515 SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8),
516 SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21),
517 SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20),
518 SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16),
519 SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17),
520 SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19),
521};
522
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100523static struct sensor_device_attribute sda_in_min[] = {
524 SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
525 SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
526 SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
527 SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
528 SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
529 SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
530 SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
531 SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
532 SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
533 SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9),
534};
535
536static struct sensor_device_attribute sda_in_max[] = {
537 SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
538 SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
539 SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
540 SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
541 SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
542 SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
543 SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
544 SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
545 SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
546 SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
547};
548
549static void device_create_file_in(struct device *dev, int i)
550{
551 device_create_file(dev, &sda_in_input[i].dev_attr);
Jean Delvarea4589db2006-03-23 16:30:29 +0100552 device_create_file(dev, &sda_in_alarm[i].dev_attr);
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100553 device_create_file(dev, &sda_in_min[i].dev_attr);
554 device_create_file(dev, &sda_in_max[i].dev_attr);
555}
Jean Delvare08e7e272005-04-25 22:43:25 +0200556
557#define show_fan_reg(reg) \
558static ssize_t \
Yuan Mu412fec82006-02-05 23:24:16 +0100559show_##reg(struct device *dev, struct device_attribute *attr, \
560 char *buf) \
Jean Delvare08e7e272005-04-25 22:43:25 +0200561{ \
562 struct w83627ehf_data *data = w83627ehf_update_device(dev); \
Yuan Mu412fec82006-02-05 23:24:16 +0100563 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
564 int nr = sensor_attr->index; \
Jean Delvare08e7e272005-04-25 22:43:25 +0200565 return sprintf(buf, "%d\n", \
566 fan_from_reg(data->reg[nr], \
567 div_from_reg(data->fan_div[nr]))); \
568}
569show_fan_reg(fan);
570show_fan_reg(fan_min);
571
572static ssize_t
Yuan Mu412fec82006-02-05 23:24:16 +0100573show_fan_div(struct device *dev, struct device_attribute *attr,
574 char *buf)
Jean Delvare08e7e272005-04-25 22:43:25 +0200575{
576 struct w83627ehf_data *data = w83627ehf_update_device(dev);
Yuan Mu412fec82006-02-05 23:24:16 +0100577 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
578 int nr = sensor_attr->index;
579 return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
Jean Delvare08e7e272005-04-25 22:43:25 +0200580}
581
582static ssize_t
Yuan Mu412fec82006-02-05 23:24:16 +0100583store_fan_min(struct device *dev, struct device_attribute *attr,
584 const char *buf, size_t count)
Jean Delvare08e7e272005-04-25 22:43:25 +0200585{
586 struct i2c_client *client = to_i2c_client(dev);
587 struct w83627ehf_data *data = i2c_get_clientdata(client);
Yuan Mu412fec82006-02-05 23:24:16 +0100588 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
589 int nr = sensor_attr->index;
Jean Delvare08e7e272005-04-25 22:43:25 +0200590 unsigned int val = simple_strtoul(buf, NULL, 10);
591 unsigned int reg;
592 u8 new_div;
593
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100594 mutex_lock(&data->update_lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200595 if (!val) {
596 /* No min limit, alarm disabled */
597 data->fan_min[nr] = 255;
598 new_div = data->fan_div[nr]; /* No change */
599 dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
600 } else if ((reg = 1350000U / val) >= 128 * 255) {
601 /* Speed below this value cannot possibly be represented,
602 even with the highest divider (128) */
603 data->fan_min[nr] = 254;
604 new_div = 7; /* 128 == (1 << 7) */
605 dev_warn(dev, "fan%u low limit %u below minimum %u, set to "
606 "minimum\n", nr + 1, val, fan_from_reg(254, 128));
607 } else if (!reg) {
608 /* Speed above this value cannot possibly be represented,
609 even with the lowest divider (1) */
610 data->fan_min[nr] = 1;
611 new_div = 0; /* 1 == (1 << 0) */
612 dev_warn(dev, "fan%u low limit %u above maximum %u, set to "
Jean Delvareb9110b12005-05-02 23:08:22 +0200613 "maximum\n", nr + 1, val, fan_from_reg(1, 1));
Jean Delvare08e7e272005-04-25 22:43:25 +0200614 } else {
615 /* Automatically pick the best divider, i.e. the one such
616 that the min limit will correspond to a register value
617 in the 96..192 range */
618 new_div = 0;
619 while (reg > 192 && new_div < 7) {
620 reg >>= 1;
621 new_div++;
622 }
623 data->fan_min[nr] = reg;
624 }
625
626 /* Write both the fan clock divider (if it changed) and the new
627 fan min (unconditionally) */
628 if (new_div != data->fan_div[nr]) {
629 if (new_div > data->fan_div[nr])
630 data->fan[nr] >>= (data->fan_div[nr] - new_div);
631 else
632 data->fan[nr] <<= (new_div - data->fan_div[nr]);
633
634 dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
635 nr + 1, div_from_reg(data->fan_div[nr]),
636 div_from_reg(new_div));
637 data->fan_div[nr] = new_div;
638 w83627ehf_write_fan_div(client, nr);
639 }
640 w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr],
641 data->fan_min[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100642 mutex_unlock(&data->update_lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200643
644 return count;
645}
646
Yuan Mu412fec82006-02-05 23:24:16 +0100647static struct sensor_device_attribute sda_fan_input[] = {
648 SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
649 SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
650 SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
651 SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
652 SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
653};
Jean Delvare08e7e272005-04-25 22:43:25 +0200654
Jean Delvarea4589db2006-03-23 16:30:29 +0100655static struct sensor_device_attribute sda_fan_alarm[] = {
656 SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6),
657 SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7),
658 SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11),
659 SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10),
660 SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23),
661};
662
Yuan Mu412fec82006-02-05 23:24:16 +0100663static struct sensor_device_attribute sda_fan_min[] = {
664 SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
665 store_fan_min, 0),
666 SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
667 store_fan_min, 1),
668 SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
669 store_fan_min, 2),
670 SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
671 store_fan_min, 3),
672 SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min,
673 store_fan_min, 4),
674};
Jean Delvare08e7e272005-04-25 22:43:25 +0200675
Yuan Mu412fec82006-02-05 23:24:16 +0100676static struct sensor_device_attribute sda_fan_div[] = {
677 SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0),
678 SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
679 SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
680 SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
681 SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
682};
Jean Delvare08e7e272005-04-25 22:43:25 +0200683
Yuan Mu412fec82006-02-05 23:24:16 +0100684static void device_create_file_fan(struct device *dev, int i)
685{
686 device_create_file(dev, &sda_fan_input[i].dev_attr);
Jean Delvarea4589db2006-03-23 16:30:29 +0100687 device_create_file(dev, &sda_fan_alarm[i].dev_attr);
Yuan Mu412fec82006-02-05 23:24:16 +0100688 device_create_file(dev, &sda_fan_div[i].dev_attr);
689 device_create_file(dev, &sda_fan_min[i].dev_attr);
690}
Jean Delvare08e7e272005-04-25 22:43:25 +0200691
692#define show_temp1_reg(reg) \
693static ssize_t \
Greg Kroah-Hartman6f637a62005-06-21 21:01:59 -0700694show_##reg(struct device *dev, struct device_attribute *attr, \
695 char *buf) \
Jean Delvare08e7e272005-04-25 22:43:25 +0200696{ \
697 struct w83627ehf_data *data = w83627ehf_update_device(dev); \
698 return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \
699}
700show_temp1_reg(temp1);
701show_temp1_reg(temp1_max);
702show_temp1_reg(temp1_max_hyst);
703
704#define store_temp1_reg(REG, reg) \
705static ssize_t \
Greg Kroah-Hartman6f637a62005-06-21 21:01:59 -0700706store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
707 const char *buf, size_t count) \
Jean Delvare08e7e272005-04-25 22:43:25 +0200708{ \
709 struct i2c_client *client = to_i2c_client(dev); \
710 struct w83627ehf_data *data = i2c_get_clientdata(client); \
711 u32 val = simple_strtoul(buf, NULL, 10); \
712 \
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100713 mutex_lock(&data->update_lock); \
Jean Delvare08e7e272005-04-25 22:43:25 +0200714 data->temp1_##reg = temp1_to_reg(val); \
715 w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
716 data->temp1_##reg); \
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100717 mutex_unlock(&data->update_lock); \
Jean Delvare08e7e272005-04-25 22:43:25 +0200718 return count; \
719}
720store_temp1_reg(OVER, max);
721store_temp1_reg(HYST, max_hyst);
722
Jean Delvare08e7e272005-04-25 22:43:25 +0200723#define show_temp_reg(reg) \
724static ssize_t \
Yuan Mu412fec82006-02-05 23:24:16 +0100725show_##reg(struct device *dev, struct device_attribute *attr, \
726 char *buf) \
Jean Delvare08e7e272005-04-25 22:43:25 +0200727{ \
728 struct w83627ehf_data *data = w83627ehf_update_device(dev); \
Yuan Mu412fec82006-02-05 23:24:16 +0100729 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
730 int nr = sensor_attr->index; \
Jean Delvare08e7e272005-04-25 22:43:25 +0200731 return sprintf(buf, "%d\n", \
732 LM75_TEMP_FROM_REG(data->reg[nr])); \
733}
734show_temp_reg(temp);
735show_temp_reg(temp_max);
736show_temp_reg(temp_max_hyst);
737
738#define store_temp_reg(REG, reg) \
739static ssize_t \
Yuan Mu412fec82006-02-05 23:24:16 +0100740store_##reg(struct device *dev, struct device_attribute *attr, \
741 const char *buf, size_t count) \
Jean Delvare08e7e272005-04-25 22:43:25 +0200742{ \
743 struct i2c_client *client = to_i2c_client(dev); \
744 struct w83627ehf_data *data = i2c_get_clientdata(client); \
Yuan Mu412fec82006-02-05 23:24:16 +0100745 struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
746 int nr = sensor_attr->index; \
Jean Delvare08e7e272005-04-25 22:43:25 +0200747 u32 val = simple_strtoul(buf, NULL, 10); \
748 \
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100749 mutex_lock(&data->update_lock); \
Jean Delvare08e7e272005-04-25 22:43:25 +0200750 data->reg[nr] = LM75_TEMP_TO_REG(val); \
751 w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \
752 data->reg[nr]); \
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100753 mutex_unlock(&data->update_lock); \
Jean Delvare08e7e272005-04-25 22:43:25 +0200754 return count; \
755}
756store_temp_reg(OVER, temp_max);
757store_temp_reg(HYST, temp_max_hyst);
758
Yuan Mu412fec82006-02-05 23:24:16 +0100759static struct sensor_device_attribute sda_temp[] = {
760 SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0),
761 SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0),
762 SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1),
763 SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max,
764 store_temp1_max, 0),
765 SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
766 store_temp_max, 0),
767 SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
768 store_temp_max, 1),
769 SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst,
770 store_temp1_max_hyst, 0),
771 SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
772 store_temp_max_hyst, 0),
773 SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
774 store_temp_max_hyst, 1),
Jean Delvarea4589db2006-03-23 16:30:29 +0100775 SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4),
776 SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
777 SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
Yuan Mu412fec82006-02-05 23:24:16 +0100778};
Jean Delvare08e7e272005-04-25 22:43:25 +0200779
780/*
781 * Driver and client management
782 */
783
784static struct i2c_driver w83627ehf_driver;
785
786static void w83627ehf_init_client(struct i2c_client *client)
787{
788 int i;
789 u8 tmp;
790
791 /* Start monitoring is needed */
792 tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG);
793 if (!(tmp & 0x01))
794 w83627ehf_write_value(client, W83627EHF_REG_CONFIG,
795 tmp | 0x01);
796
797 /* Enable temp2 and temp3 if needed */
798 for (i = 0; i < 2; i++) {
799 tmp = w83627ehf_read_value(client,
800 W83627EHF_REG_TEMP_CONFIG[i]);
801 if (tmp & 0x01)
802 w83627ehf_write_value(client,
803 W83627EHF_REG_TEMP_CONFIG[i],
804 tmp & 0xfe);
805 }
806}
807
Jean Delvare2d8672c2005-07-19 23:56:35 +0200808static int w83627ehf_detect(struct i2c_adapter *adapter)
Jean Delvare08e7e272005-04-25 22:43:25 +0200809{
810 struct i2c_client *client;
811 struct w83627ehf_data *data;
Yuan Mu412fec82006-02-05 23:24:16 +0100812 struct device *dev;
Jean Delvare08e7e272005-04-25 22:43:25 +0200813 int i, err = 0;
814
Petr Vandrovecada0c2f2005-10-07 23:11:03 +0200815 if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100816 w83627ehf_driver.driver.name)) {
Jean Delvare08e7e272005-04-25 22:43:25 +0200817 err = -EBUSY;
818 goto exit;
819 }
820
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200821 if (!(data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) {
Jean Delvare08e7e272005-04-25 22:43:25 +0200822 err = -ENOMEM;
823 goto exit_release;
824 }
Jean Delvare08e7e272005-04-25 22:43:25 +0200825
826 client = &data->client;
827 i2c_set_clientdata(client, data);
828 client->addr = address;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100829 mutex_init(&data->lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200830 client->adapter = adapter;
831 client->driver = &w83627ehf_driver;
832 client->flags = 0;
Yuan Mu412fec82006-02-05 23:24:16 +0100833 dev = &client->dev;
Jean Delvare08e7e272005-04-25 22:43:25 +0200834
835 strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
836 data->valid = 0;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100837 mutex_init(&data->update_lock);
Jean Delvare08e7e272005-04-25 22:43:25 +0200838
839 /* Tell the i2c layer a new client has arrived */
840 if ((err = i2c_attach_client(client)))
841 goto exit_free;
842
843 /* Initialize the chip */
844 w83627ehf_init_client(client);
845
846 /* A few vars need to be filled upon startup */
847 for (i = 0; i < 5; i++)
848 data->fan_min[i] = w83627ehf_read_value(client,
849 W83627EHF_REG_FAN_MIN[i]);
850
851 /* It looks like fan4 and fan5 pins can be alternatively used
852 as fan on/off switches */
853 data->has_fan = 0x07; /* fan1, fan2 and fan3 */
854 i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
855 if (i & (1 << 2))
856 data->has_fan |= (1 << 3);
857 if (i & (1 << 0))
858 data->has_fan |= (1 << 4);
859
860 /* Register sysfs hooks */
Yuan Mu412fec82006-02-05 23:24:16 +0100861 data->class_dev = hwmon_device_register(dev);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400862 if (IS_ERR(data->class_dev)) {
863 err = PTR_ERR(data->class_dev);
864 goto exit_detach;
865 }
866
Rudolf Marekcf0676f2006-03-23 16:25:22 +0100867 for (i = 0; i < 10; i++)
868 device_create_file_in(dev, i);
869
Yuan Mu412fec82006-02-05 23:24:16 +0100870 for (i = 0; i < 5; i++) {
871 if (data->has_fan & (1 << i))
872 device_create_file_fan(dev, i);
Jean Delvare08e7e272005-04-25 22:43:25 +0200873 }
Yuan Mu412fec82006-02-05 23:24:16 +0100874 for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
875 device_create_file(dev, &sda_temp[i].dev_attr);
Jean Delvare08e7e272005-04-25 22:43:25 +0200876
877 return 0;
878
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400879exit_detach:
880 i2c_detach_client(client);
Jean Delvare08e7e272005-04-25 22:43:25 +0200881exit_free:
882 kfree(data);
883exit_release:
Petr Vandrovecada0c2f2005-10-07 23:11:03 +0200884 release_region(address + REGION_OFFSET, REGION_LENGTH);
Jean Delvare08e7e272005-04-25 22:43:25 +0200885exit:
886 return err;
887}
888
Jean Delvare08e7e272005-04-25 22:43:25 +0200889static int w83627ehf_detach_client(struct i2c_client *client)
890{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400891 struct w83627ehf_data *data = i2c_get_clientdata(client);
Jean Delvare08e7e272005-04-25 22:43:25 +0200892 int err;
893
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400894 hwmon_device_unregister(data->class_dev);
895
Jean Delvare7bef5592005-07-27 22:14:49 +0200896 if ((err = i2c_detach_client(client)))
Jean Delvare08e7e272005-04-25 22:43:25 +0200897 return err;
Petr Vandrovecada0c2f2005-10-07 23:11:03 +0200898 release_region(client->addr + REGION_OFFSET, REGION_LENGTH);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400899 kfree(data);
Jean Delvare08e7e272005-04-25 22:43:25 +0200900
901 return 0;
902}
903
904static struct i2c_driver w83627ehf_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100905 .driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100906 .name = "w83627ehf",
907 },
Jean Delvare2d8672c2005-07-19 23:56:35 +0200908 .attach_adapter = w83627ehf_detect,
Jean Delvare08e7e272005-04-25 22:43:25 +0200909 .detach_client = w83627ehf_detach_client,
910};
911
Jean Delvare2d8672c2005-07-19 23:56:35 +0200912static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
Jean Delvare08e7e272005-04-25 22:43:25 +0200913{
914 u16 val;
915
916 REG = sioaddr;
917 VAL = sioaddr + 1;
918 superio_enter();
919
920 val = (superio_inb(SIO_REG_DEVID) << 8)
921 | superio_inb(SIO_REG_DEVID + 1);
922 if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
923 superio_exit();
924 return -ENODEV;
925 }
926
927 superio_select(W83627EHF_LD_HWM);
928 val = (superio_inb(SIO_REG_ADDR) << 8)
929 | superio_inb(SIO_REG_ADDR + 1);
Petr Vandrovecada0c2f2005-10-07 23:11:03 +0200930 *addr = val & REGION_ALIGNMENT;
Jean Delvare2d8672c2005-07-19 23:56:35 +0200931 if (*addr == 0) {
Jean Delvare08e7e272005-04-25 22:43:25 +0200932 superio_exit();
933 return -ENODEV;
934 }
935
936 /* Activate logical device if needed */
937 val = superio_inb(SIO_REG_ENABLE);
938 if (!(val & 0x01))
939 superio_outb(SIO_REG_ENABLE, val | 0x01);
940
941 superio_exit();
942 return 0;
943}
944
945static int __init sensors_w83627ehf_init(void)
946{
Jean Delvare2d8672c2005-07-19 23:56:35 +0200947 if (w83627ehf_find(0x2e, &address)
948 && w83627ehf_find(0x4e, &address))
Jean Delvare08e7e272005-04-25 22:43:25 +0200949 return -ENODEV;
950
Jean Delvarefde09502005-07-19 23:51:07 +0200951 return i2c_isa_add_driver(&w83627ehf_driver);
Jean Delvare08e7e272005-04-25 22:43:25 +0200952}
953
954static void __exit sensors_w83627ehf_exit(void)
955{
Jean Delvarefde09502005-07-19 23:51:07 +0200956 i2c_isa_del_driver(&w83627ehf_driver);
Jean Delvare08e7e272005-04-25 22:43:25 +0200957}
958
959MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
960MODULE_DESCRIPTION("W83627EHF driver");
961MODULE_LICENSE("GPL");
962
963module_init(sensors_w83627ehf_init);
964module_exit(sensors_w83627ehf_exit);