root/lm-sensors/trunk/kernel/chips/pc87360.c @ 2477

Revision 2477, 28.7 KB (checked in by khali, 9 years ago)

Add some debug output.

Show more status bits (voltages and temperatures).
Fix incorrect use of const.
Clear status bits on update.
Fix temperature reads (min, crit and current).
Fix temperature writes (min).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 *  pc87360.c - Part of lm_sensors, Linux kernel modules
3 *              for hardware monitoring
4 *  Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
5 *
6 *  Copied from smsc47m1.c:
7 *  Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 *  Supports the following chips:
24 *
25 *  Chip        #vin    #fan    #pwm    #temp   devid
26 *  PC87360     -       2       2       -       0xE1
27 *  PC87363     -       2       2       -       0xE8
28 *  PC87364     -       3       3       -       0xE4
29 *  PC87365     11      3       3       2       0xE5
30 *  PC87366     11      3       3       3       0xE9
31 */
32
33#include <linux/module.h>
34#include <linux/slab.h>
35#include <linux/ioport.h>
36#include <linux/i2c.h>
37#include <linux/i2c-proc.h>
38#include <linux/init.h>
39#include <asm/io.h>
40#include "version.h"
41
42static unsigned short normal_i2c[] = { SENSORS_I2C_END };
43static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
44static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
45static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
46static u8 devid;
47static unsigned int extra_isa[] = { 0x0000, 0x0000, 0x0000 };
48
49SENSORS_INSMOD_5(pc87360, pc87363, pc87364, pc87365, pc87366);
50
51/*
52 * Super-I/O registers and operations
53 */
54
55#define REG     0x2e    /* The register to read/write */
56#define VAL     0x2f    /* The value to read/write */
57
58#define DEV     0x07    /* Register: Logical device select */
59#define DEVID   0x20    /* Register: Device ID */
60#define ACT     0x30    /* Register: Device activation */
61#define BASE    0x60    /* Register: Base address */
62
63#define FSCM    0x09    /* Logical device: fans */
64#define VLM     0x0d    /* Logical device: voltages */
65#define TMS     0x0e    /* Logical device: temperatures */
66static const u8 logdev[3] = { FSCM, VLM, TMS };
67
68static inline void superio_outb(int reg, int val)
69{
70        outb(reg, REG);
71        outb(val, VAL);
72}
73
74static inline int superio_inb(int reg)
75{
76        outb(reg, REG);
77        return inb(VAL);
78}
79
80static inline void superio_exit(void)
81{
82        outb(0x02, REG);
83        outb(0x02, VAL);
84}
85
86#define PC87360_EXTENT          0x10
87
88/*
89 * Fan registers and conversions
90 */
91
92/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */
93#define PC87360_REG_PRESCALE(nr)        (0x00 + 2 * (nr))
94#define PC87360_REG_PWM(nr)             (0x01 + 2 * (nr))
95#define PC87360_REG_FAN_MIN(nr)         (0x06 + 3 * (nr))
96#define PC87360_REG_FAN(nr)             (0x07 + 3 * (nr))
97#define PC87360_REG_FAN_STATUS(nr)      (0x08 + 3 * (nr))
98
99#define FAN_FROM_REG(val,div)           ((val)==0?-1:(val)==255?0: \
100                                         480000/((val)*(div)))
101#define FAN_TO_REG(val,div)             ((val)<=0?255: \
102                                         480000/((val)*(div)))
103#define FAN_DIV_FROM_REG(val)           (1 << ((val >> 5) & 0x03))
104#define FAN_DIV_TO_REG(val)             ((val)==8?0x60:(val)==4?0x40: \
105                                         (val)==1?0x00:0x20)
106#define FAN_STATUS_FROM_REG(val)        ((val) & 0x07)
107
108/*
109 * Voltage registers and conversions
110 */
111
112#define PC87365_REG_IN_BANK             0x09
113
114/* nr has to be 0 to 11 (PC87365/87366) */
115#define PC87365_REG_IN                  0x0B
116#define PC87365_REG_IN_MIN              0x0D
117#define PC87365_REG_IN_MAX              0x0C
118#define PC87365_REG_IN_STATUS           0x0A
119#define PC87365_REG_IN_ALARMS1          0x00
120#define PC87365_REG_IN_ALARMS2          0x01
121
122#define IN_FROM_REG(val)                (((val) * 297 + 127) / 255)
123#define IN_TO_REG(val)                  ((val)<0?0:(val)>297?255: \
124                                         ((val) * 255 + 148) / 297)
125#define IN_STATUS_FROM_REG(val)         ((val) & 0x97)
126
127/*
128 * Temperature registers and conversions
129 */
130
131#define PC87365_REG_TEMP_BANK           0x09
132
133/* nr has to be 0 to 1 (PC87365) or 2 (PC87366) */
134#define PC87365_REG_TEMP                0x0B
135#define PC87365_REG_TEMP_MIN            0x0D
136#define PC87365_REG_TEMP_MAX            0x0C
137#define PC87365_REG_TEMP_CRIT           0x0D
138#define PC87365_REG_TEMP_STATUS         0x0A
139#define PC87365_REG_TEMP_ALARMS         0x00
140
141#define TEMP_FROM_REG(val)              ((val)&0x80 ? (val) : (val) - 128)
142#define TEMP_TO_REG(val)                ((val)<-128?0x80:(val)>127?0x7F: \
143                                         (val)<0?(val)+0x80:(val))
144#define TEMP_STATUS_FROM_REG(val)       ((val) & 0xFF)
145
146struct pc87360_data {
147        struct i2c_client client;
148        struct semaphore lock;
149        int sysctl_id;
150        int address[3];
151
152        struct semaphore update_lock;
153        char valid;             /* !=0 if following fields are valid */
154        unsigned long last_updated;     /* In jiffies */
155
156        u8 fannr, innr, tempnr;
157
158        u8 fan[3];              /* Register value */
159        u8 fan_min[3];          /* Register value */
160        u8 fan_status[3];       /* Register value */
161        u8 pwm[3];              /* Register value */
162
163        u8 in[11];              /* Register value */
164        u8 in_min[11];          /* Register value */
165        u8 in_max[11];          /* Register value */
166        u8 in_status[11];       /* Register value */
167        u16 in_alarms;          /* Register values, combined */
168
169        u8 temp[3];             /* Register value */
170        u8 temp_min[3];         /* Register value */
171        u8 temp_max[3];         /* Register value */
172        u8 temp_crit[3];        /* Register value */
173        u8 temp_status[3];      /* Register value */
174        u8 temp_alarms;         /* Register value */
175};
176
177
178static int pc87360_attach_adapter(struct i2c_adapter *adapter);
179static int pc87360_detect(struct i2c_adapter *adapter, int address,
180                          unsigned short flags, int kind);
181static int pc87360_detach_client(struct i2c_client *client);
182
183static int pc87360_read_value(struct pc87360_data *data, int ldi, u8 reg);
184static int pc87360_write_value(struct pc87360_data *data, int ldi, u8 reg,
185                               u8 value);
186static void pc87360_update_client(struct i2c_client *client);
187static int pc87360_find(u8 *devid, int *address);
188
189
190void pc87365_alarms(struct i2c_client *client, int operation, int ctl_name,
191                    int *nrels_mag, long *results);
192
193static void pc87360_fan(struct i2c_client *client, int operation,
194                        int ctl_name, int *nrels_mag, long *results);
195static void pc87360_fan_status(struct i2c_client *client, int operation,
196                               int ctl_name, int *nrels_mag, long *results);
197static void pc87360_fan_div(struct i2c_client *client, int operation,
198                            int ctl_name, int *nrels_mag, long *results);
199static void pc87360_pwm(struct i2c_client *client, int operation,
200                        int ctl_name, int *nrels_mag, long *results);
201
202void pc87365_in(struct i2c_client *client, int operation, int ctl_name,
203                int *nrels_mag, long *results);
204void pc87365_in_status(struct i2c_client *client, int operation, int ctl_name,
205                       int *nrels_mag, long *results);
206
207void pc87365_temp(struct i2c_client *client, int operation, int ctl_name,
208                  int *nrels_mag, long *results);
209void pc87365_temp_status(struct i2c_client *client, int operation, int ctl_name,
210                         int *nrels_mag, long *results);
211
212static int pc87360_id = 0;
213
214static struct i2c_driver pc87360_driver = {
215        .owner          = THIS_MODULE,
216        .name           = "PC8736x hardware monitor",
217        .flags          = I2C_DF_NOTIFY,
218        .attach_adapter = pc87360_attach_adapter,
219        .detach_client  = pc87360_detach_client,
220};
221
222/* -- SENSORS SYSCTL START -- */
223
224#define PC87365_SYSCTL_ALARMS           100 /* bit field */
225
226#define PC87360_SYSCTL_FAN1             1101 /* Rotations/min */
227#define PC87360_SYSCTL_FAN2             1102
228#define PC87360_SYSCTL_FAN3             1103 /* not for PC87360/PC87363 */
229#define PC87360_SYSCTL_FAN_DIV          1201 /* 1, 2, 4 or 8 */
230#define PC87360_SYSCTL_FAN1_STATUS      1301 /* bit field */
231#define PC87360_SYSCTL_FAN2_STATUS      1302
232#define PC87360_SYSCTL_FAN3_STATUS      1303 /* not for PC87360/PC87363 */
233#define PC87360_SYSCTL_PWM1             1401 /* 0-255 */
234#define PC87360_SYSCTL_PWM2             1402
235#define PC87360_SYSCTL_PWM3             1403 /* not for PC87360/PC87363 */
236
237#define PC87360_STATUS_FAN_READY        0x01
238#define PC87360_STATUS_FAN_LOW          0x02
239#define PC87360_STATUS_FAN_OVERFLOW     0x04
240
241#define PC87365_SYSCTL_IN0              2100 /* mV */
242#define PC87365_SYSCTL_IN1              2101
243#define PC87365_SYSCTL_IN2              2102
244#define PC87365_SYSCTL_IN3              2103
245#define PC87365_SYSCTL_IN4              2104
246#define PC87365_SYSCTL_IN5              2105
247#define PC87365_SYSCTL_IN6              2106
248#define PC87365_SYSCTL_IN7              2107
249#define PC87365_SYSCTL_IN8              2108
250#define PC87365_SYSCTL_IN9              2109
251#define PC87365_SYSCTL_IN10             2110
252#define PC87365_SYSCTL_IN0_STATUS       2300 /* bit field */
253#define PC87365_SYSCTL_IN1_STATUS       2301
254#define PC87365_SYSCTL_IN2_STATUS       2302
255#define PC87365_SYSCTL_IN3_STATUS       2303
256#define PC87365_SYSCTL_IN4_STATUS       2304
257#define PC87365_SYSCTL_IN5_STATUS       2305
258#define PC87365_SYSCTL_IN6_STATUS       2306
259#define PC87365_SYSCTL_IN7_STATUS       2307
260#define PC87365_SYSCTL_IN8_STATUS       2308
261#define PC87365_SYSCTL_IN9_STATUS       2309
262#define PC87365_SYSCTL_IN10_STATUS      2310
263
264#define PC87365_STATUS_IN_MIN           0x02
265#define PC87365_STATUS_IN_MAX           0x04
266
267#define PC87365_SYSCTL_TEMP1            3101 /* degrees Celcius */
268#define PC87365_SYSCTL_TEMP2            3102
269#define PC87365_SYSCTL_TEMP3            3103 /* not for PC87365 */
270#define PC87365_SYSCTL_TEMP1_STATUS     3101 /* bit field */
271#define PC87365_SYSCTL_TEMP2_STATUS     3102
272#define PC87365_SYSCTL_TEMP3_STATUS     3103 /* not for PC87365 */
273
274#define PC87365_STATUS_TEMP_MIN         0x02
275#define PC87365_STATUS_TEMP_MAX         0x04
276#define PC87365_STATUS_TEMP_CRIT        0x08
277#define PC87365_STATUS_TEMP_OPEN        0x40
278
279/* -- SENSORS SYSCTL END -- */
280
281static ctl_table pc87360_dir_table_template[] = { /* PC87363 and PC87364 too */
282        {PC87360_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
283         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan},
284        {PC87360_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
285         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan},
286        {PC87360_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
287         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan},
288        {PC87360_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
289         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_div},
290        {PC87360_SYSCTL_FAN1_STATUS, "fan1_status", NULL, 0, 0444, NULL,
291         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status},
292        {PC87360_SYSCTL_FAN2_STATUS, "fan2_status", NULL, 0, 0444, NULL,
293         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status},
294        {PC87360_SYSCTL_FAN3_STATUS, "fan3_status", NULL, 0, 0444, NULL,
295         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status},
296        {PC87360_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
297         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm},
298        {PC87360_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL,
299         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm},
300        {PC87360_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL,
301         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm},
302        {0}
303};
304
305static ctl_table pc87365_dir_table_template[] = { /* PC87366 too */
306        {PC87365_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
307         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_alarms},
308        {PC87360_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
309         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan},
310        {PC87360_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
311         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan},
312        {PC87360_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
313         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan},
314        {PC87360_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
315         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_div},
316        {PC87360_SYSCTL_FAN1_STATUS, "fan1_status", NULL, 0, 0444, NULL,
317         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status},
318        {PC87360_SYSCTL_FAN2_STATUS, "fan2_status", NULL, 0, 0444, NULL,
319         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status},
320        {PC87360_SYSCTL_FAN3_STATUS, "fan3_status", NULL, 0, 0444, NULL,
321         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status},
322        {PC87360_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
323         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm},
324        {PC87360_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL,
325         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm},
326        {PC87360_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL,
327         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm},
328        {PC87365_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL,
329         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
330        {PC87365_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL,
331         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
332        {PC87365_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL,
333         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
334        {PC87365_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL,
335         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
336        {PC87365_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL,
337         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
338        {PC87365_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL,
339         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
340        {PC87365_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL,
341         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
342        {PC87365_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL,
343         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
344        {PC87365_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL,
345         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
346        {PC87365_SYSCTL_IN9, "in9", NULL, 0, 0644, NULL,
347         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
348        {PC87365_SYSCTL_IN10, "in10", NULL, 0, 0644, NULL,
349         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in},
350        {PC87365_SYSCTL_IN0_STATUS, "in0_status", NULL, 0, 0444, NULL,
351         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
352        {PC87365_SYSCTL_IN1_STATUS, "in1_status", NULL, 0, 0444, NULL,
353         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
354        {PC87365_SYSCTL_IN2_STATUS, "in2_status", NULL, 0, 0444, NULL,
355         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
356        {PC87365_SYSCTL_IN3_STATUS, "in3_status", NULL, 0, 0444, NULL,
357         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
358        {PC87365_SYSCTL_IN4_STATUS, "in4_status", NULL, 0, 0444, NULL,
359         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
360        {PC87365_SYSCTL_IN5_STATUS, "in5_status", NULL, 0, 0444, NULL,
361         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
362        {PC87365_SYSCTL_IN6_STATUS, "in6_status", NULL, 0, 0444, NULL,
363         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
364        {PC87365_SYSCTL_IN7_STATUS, "in7_status", NULL, 0, 0444, NULL,
365         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
366        {PC87365_SYSCTL_IN8_STATUS, "in8_status", NULL, 0, 0444, NULL,
367         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
368        {PC87365_SYSCTL_IN9_STATUS, "in9_status", NULL, 0, 0444, NULL,
369         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
370        {PC87365_SYSCTL_IN10_STATUS, "in10_status", NULL, 0, 0444, NULL,
371         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status},
372        {PC87365_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
373         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp},
374        {PC87365_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
375         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp},
376        {PC87365_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
377         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp},
378        {PC87365_SYSCTL_TEMP1_STATUS, "temp1_status", NULL, 0, 0644, NULL,
379         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp_status},
380        {PC87365_SYSCTL_TEMP2_STATUS, "temp2_status", NULL, 0, 0644, NULL,
381         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp_status},
382        {PC87365_SYSCTL_TEMP3_STATUS, "temp3_status", NULL, 0, 0644, NULL,
383         &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp_status},
384        {0}
385};
386
387static int pc87360_attach_adapter(struct i2c_adapter *adapter)
388{
389        return i2c_detect(adapter, &addr_data, pc87360_detect);
390}
391
392static int pc87360_find(u8 *devid, int *address)
393{
394        u16 val;
395        int i;
396        int nrdev; /* logical device count */
397
398        /* no superio_enter */
399
400        /* identify device */
401        val = superio_inb(DEVID);
402        switch (val) {
403        case 0xE1: /* PC87360 */
404        case 0xE8: /* PC87363 */
405        case 0xE4: /* PC87364 */
406                nrdev = 1;
407                break;
408        case 0xE5: /* PC87365 */
409        case 0xE9: /* PC87366 */
410                nrdev = 3;
411                break;
412        default:
413                superio_exit();
414                return -ENODEV;
415        }
416        /* remember the device id */
417        *devid = val;
418
419        for (i = 0; i < nrdev; i++) {
420                /* select logical device */
421                superio_outb(DEV, logdev[i]);
422
423                val = superio_inb(ACT);
424                if (!(val & 0x01)) {
425                        printk(KERN_INFO "pc87360.o: device 0x%02x not "
426                               "activated\n", logdev[i]);
427                        continue;
428                }
429
430                val = (superio_inb(BASE) << 8)
431                    | superio_inb(BASE + 1);
432                if (!val) {
433                        printk(KERN_INFO "pc87360.o: base address not set for "
434                               "device 0x%02x\n", logdev[i]);
435                        continue;
436                }
437
438                address[i] = val;
439
440#ifdef DEBUG
441                if (i==0) { /* Fans */
442                        val = superio_inb(0x70)
443                            | (superio_inb(0x71) << 8);
444                       
445                        printk(KERN_DEBUG "pc87360.o: Fan 1: mon=%d "
446                               "ctrl=%d inv=%d\n", (val>>2)&1, (val>>3)&1,
447                               (val>>4)&1);
448                        printk(KERN_DEBUG "pc87360.o: Fan 2: mon=%d "
449                               "ctrl=%d inv=%d\n", (val>>5)&1, (val>>6)&1,
450                               (val>>7)&1);
451                        printk(KERN_DEBUG "pc87360.o: Fan 3: mon=%d "
452                               "ctrl=%d inv=%d\n", (val>>8)&1, (val>>9)&1,
453                               (val>>10)&1);
454                }
455#endif
456        }
457
458        superio_exit();
459        return 0;
460}
461
462/* We don't really care about the address.
463   Read from extra_isa instead. */
464int pc87360_detect(struct i2c_adapter *adapter, int address,
465                   unsigned short flags, int kind)
466{
467        int i;
468        struct i2c_client *new_client;
469        struct pc87360_data *data;
470        int err = 0;
471        const char *type_name = "pc87360";
472        const char *client_name = "PC8736x chip";
473        ctl_table *template = pc87360_dir_table_template;
474
475        if (!i2c_is_isa_adapter(adapter)) {
476                return 0;
477        }
478
479        for (i = 0; i < 3; i++) {
480                if (extra_isa[i]
481                 && check_region(extra_isa[i], PC87360_EXTENT)) {
482                        printk(KERN_ERR "pc87360.o: region 0x%x already in "
483                               "use!\n", address);
484                        return -ENODEV;
485                }
486        }
487
488        if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL))) {
489                return -ENOMEM;
490        }
491
492        new_client = &data->client;
493        new_client->addr = address;
494        init_MUTEX(&data->lock);
495        new_client->data = data;
496        new_client->adapter = adapter;
497        new_client->driver = &pc87360_driver;
498        new_client->flags = 0;
499
500        data->fannr = 2;
501        data->innr = 0;
502        data->tempnr = 0;
503       
504        switch (devid) {
505        case 0xe8:
506                type_name = "pc87363";
507                break;
508        case 0xe4:
509                type_name = "pc87364";
510                data->fannr = 3;
511                break;
512        case 0xe5:
513                type_name = "pc87365";
514                template = pc87365_dir_table_template;
515                data->fannr = extra_isa[0] ? 3 : 0;
516                data->innr = extra_isa[1] ? 11 : 0;
517                data->tempnr = extra_isa[2] ? 2 : 0;
518                break;
519        case 0xe9:
520                type_name = "pc87366";
521                template = pc87365_dir_table_template;
522                data->fannr = extra_isa[0] ? 3 : 0;
523                data->innr = extra_isa[1] ? 11 : 0;
524                data->tempnr = extra_isa[2] ? 3 : 0;
525                break;
526        }
527
528        for (i = 0; i < 3; i++) {
529                if ((data->address[i] = extra_isa[i])) {
530                        request_region(extra_isa[i], PC87360_EXTENT, "pc87360");
531                }
532        }
533        strcpy(new_client->name, client_name);
534
535        new_client->id = pc87360_id++;
536        data->valid = 0;
537        init_MUTEX(&data->update_lock);
538
539        if ((err = i2c_attach_client(new_client)))
540                goto ERROR1;
541
542        if ((i = i2c_register_entry((struct i2c_client *) new_client,
543                                    type_name, template)) < 0) {
544                err = i;
545                goto ERROR2;
546        }
547        data->sysctl_id = i;
548
549        return 0;
550
551ERROR2:
552        i2c_detach_client(new_client);
553ERROR1:
554        for (i = 0; i < 3; i++) {
555                if (data->address[i]) {
556                        release_region(data->address[i], PC87360_EXTENT);
557                }
558        }
559        kfree(data);
560        return err;
561}
562
563static int pc87360_detach_client(struct i2c_client *client)
564{
565        struct pc87360_data *data = client->data;
566        int i, err;
567
568        i2c_deregister_entry(data->sysctl_id);
569
570        if ((err = i2c_detach_client(client))) {
571                printk(KERN_ERR "pc87360.o: Client deregistration failed, "
572                       "client not detached.\n");
573                return err;
574        }
575
576        for (i = 0; i < 3; i++) {
577                if (data->address[i]) {
578                        release_region(data->address[i], PC87360_EXTENT);
579                }
580        }
581        kfree(client->data);
582
583        return 0;
584}
585
586/* ldi is the logical device index:
587   0: fans
588   1: voltages
589   2: temperatures */
590static int pc87360_read_value(struct pc87360_data *data, int ldi, u8 reg)
591{
592        int res;
593
594        down(&(data->lock));
595        res = inb_p(data->address[ldi] + reg);
596        up(&(data->lock));
597        return res;
598}
599
600static int pc87360_write_value(struct pc87360_data *data, int ldi, u8 reg,
601                               u8 value)
602{
603        down(&(data->lock));
604        outb_p(value, data->address[ldi] + reg);
605        up(&(data->lock));
606        return 0;
607}
608
609static void pc87360_update_client(struct i2c_client *client)
610{
611        struct pc87360_data *data = client->data;
612        int i;
613
614        down(&data->update_lock);
615
616        if ((jiffies - data->last_updated > HZ * 2) ||
617            (jiffies < data->last_updated) || !data->valid) {
618#ifdef DEBUG
619                printk(KERN_DEBUG "pc87360.o: Data update\n");
620#endif
621
622                /* Fans */
623                for (i = 0; i < data->fannr; i++) {
624                        data->fan[i] = pc87360_read_value(data, 0,
625                                       PC87360_REG_FAN(i));
626                        data->fan_min[i] = pc87360_read_value(data, 0,
627                                           PC87360_REG_FAN_MIN(i));
628                        data->fan_status[i] = pc87360_read_value(data, 0,
629                                              PC87360_REG_FAN_STATUS(i));
630                        data->pwm[i] = pc87360_read_value(data, 0,
631                                       PC87360_REG_PWM(i));
632                }
633
634                /* Voltages */
635                for (i = 0; i < data->innr; i++) {
636                        pc87360_write_value(data, 1, PC87365_REG_IN_BANK, i);
637                        data->in_status[i] = pc87360_read_value(data, 1,
638                                             PC87365_REG_IN_STATUS);
639                        /* Clear bits */
640                        pc87360_write_value(data, 1, PC87365_REG_IN_STATUS,
641                                            data->in_status[i] | 0x86);
642                        if (data->in_status[i] & 0x01) {
643                                data->in[i] = pc87360_read_value(data, 1,
644                                              PC87365_REG_IN);
645                                data->in_min[i] = pc87360_read_value(data, 1,
646                                                  PC87365_REG_IN_MIN);
647                                data->in_max[i] = pc87360_read_value(data, 1,
648                                                  PC87365_REG_IN_MAX);
649                        }
650                        data->in_alarms = pc87360_read_value(data, 1,
651                                          PC87365_REG_IN_ALARMS1)
652                                        | (pc87360_read_value(data, 1,
653                                           PC87365_REG_IN_ALARMS2) << 8);
654                }
655
656                /* Temperatures */
657                for (i = 0; i < data->tempnr; i++) {
658                        pc87360_write_value(data, 2, PC87365_REG_TEMP_BANK, i);
659                        data->temp_status[i] = pc87360_read_value(data, 2,
660                                               PC87365_REG_TEMP_STATUS);
661                        /* Clear bits */
662                        pc87360_write_value(data, 2, PC87365_REG_TEMP_STATUS,
663                                            data->temp_status[i] | 0xCE);
664                        if (data->temp_status[i] & 0x01) {
665                                data->temp[i] = pc87360_read_value(data, 2,
666                                                PC87365_REG_TEMP);
667                                data->temp_min[i] = pc87360_read_value(data, 2,
668                                                    PC87365_REG_TEMP_MIN);
669                                data->temp_max[i] = pc87360_read_value(data, 2,
670                                                    PC87365_REG_TEMP_MAX);
671                                data->temp_crit[i] = pc87360_read_value(data, 2,
672                                                     PC87365_REG_TEMP_CRIT);
673                        }
674                        data->temp_alarms = pc87360_read_value(data, 2,
675                                            PC87365_REG_TEMP_ALARMS);
676                }
677
678                data->last_updated = jiffies;
679                data->valid = 1;
680        }
681
682        up(&data->update_lock);
683}
684
685
686void pc87365_alarms(struct i2c_client *client, int operation, int ctl_name,
687                    int *nrels_mag, long *results)
688{
689        struct pc87360_data *data = client->data;
690
691        if (operation == SENSORS_PROC_REAL_INFO)
692                *nrels_mag = 0;
693        else if (operation == SENSORS_PROC_REAL_READ) {
694                pc87360_update_client(client);
695                results[0] = data->in_alarms;
696                results[1] = data->temp_alarms;
697                *nrels_mag = 2;
698        }
699}
700
701void pc87360_fan(struct i2c_client *client, int operation, int ctl_name,
702                 int *nrels_mag, long *results)
703{
704        struct pc87360_data *data = client->data;
705        int nr = ctl_name - PC87360_SYSCTL_FAN1;
706
707        if (operation == SENSORS_PROC_REAL_INFO)
708                *nrels_mag = 0;
709        else if (operation == SENSORS_PROC_REAL_READ) {
710                pc87360_update_client(client);
711                results[0] = FAN_FROM_REG(data->fan_min[nr],
712                             FAN_DIV_FROM_REG(data->fan_status[nr]));
713                results[1] = FAN_FROM_REG(data->fan[nr],
714                             FAN_DIV_FROM_REG(data->fan_status[nr]));
715                *nrels_mag = 2;
716        }
717        /* We ignore National's recommendation */
718        else if (operation == SENSORS_PROC_REAL_WRITE) {
719                if (nr >= data->fannr)
720                        return;
721                if (*nrels_mag >= 1) {
722                        data->fan_min[nr] = FAN_TO_REG(results[0],
723                                            FAN_DIV_FROM_REG(data->fan_status[nr]));
724                        pc87360_write_value(data, 0, PC87360_REG_FAN_MIN(nr),
725                                            data->fan_min[nr]);
726                }
727        }
728}
729
730void pc87360_fan_div(struct i2c_client *client, int operation,
731                     int ctl_name, int *nrels_mag, long *results)
732{
733        struct pc87360_data *data = client->data;
734        int i;
735
736        if (operation == SENSORS_PROC_REAL_INFO)
737                *nrels_mag = 0;
738        else if (operation == SENSORS_PROC_REAL_READ) {
739                pc87360_update_client(client);
740                for (i = 0; i < data->fannr; i++) {
741                        results[i] = FAN_DIV_FROM_REG(data->fan_status[i]);
742                }
743                *nrels_mag = data->fannr;
744        }
745        /* We ignore National's recommendation */
746        else if (operation == SENSORS_PROC_REAL_WRITE) {
747                for (i = 0; i < data->fannr && i < *nrels_mag; i++) {
748                        /* Preserve fan min */
749                        int fan_min = FAN_FROM_REG(data->fan_min[i],
750                                      FAN_DIV_FROM_REG(data->fan_status[i]));
751                        data->fan_status[i] = (data->fan_status[i] & 0x9F)
752                                            | FAN_DIV_TO_REG(results[i]);
753                        pc87360_write_value(data, 0, PC87360_REG_FAN_STATUS(i),
754                                            data->fan_status[i]);
755                        data->fan_min[i] = FAN_TO_REG(fan_min,
756                                           FAN_DIV_FROM_REG(data->fan_status[i]));
757                        pc87360_write_value(data, 0, PC87360_REG_FAN_MIN(i),
758                                            data->fan_min[i]);
759                }
760        }
761}
762
763void pc87360_pwm(struct i2c_client *client, int operation, int ctl_name,
764                 int *nrels_mag, long *results)
765{
766        struct pc87360_data *data = client->data;
767        int nr = ctl_name - PC87360_SYSCTL_PWM1;
768
769        if (operation == SENSORS_PROC_REAL_INFO)
770                *nrels_mag = 0;
771        else if (operation == SENSORS_PROC_REAL_READ) {
772                pc87360_update_client(client);
773                results[0] = data->pwm[nr];
774                *nrels_mag = 1;
775        }
776        else if (operation == SENSORS_PROC_REAL_WRITE) {
777                if (*nrels_mag >= 1)
778                {
779                        data->pwm[nr] = SENSORS_LIMIT(results[0], 0, 255);
780                        pc87360_write_value(data, 0, PC87360_REG_PWM(nr),
781                                            data->pwm[nr]);
782                }
783        }
784}
785
786void pc87360_fan_status(struct i2c_client *client, int operation, int ctl_name,
787                        int *nrels_mag, long *results)
788{
789        struct pc87360_data *data = client->data;
790        int nr = ctl_name - PC87360_SYSCTL_FAN1_STATUS;
791
792        if (operation == SENSORS_PROC_REAL_INFO)
793                *nrels_mag = 0;
794        else if (operation == SENSORS_PROC_REAL_READ) {
795                pc87360_update_client(client);
796                results[0] = FAN_STATUS_FROM_REG(data->fan_status[nr]);
797                *nrels_mag = 1;
798        }
799}
800
801void pc87365_in(struct i2c_client *client, int operation, int ctl_name,
802                int *nrels_mag, long *results)
803{
804        struct pc87360_data *data = client->data;
805        int nr = ctl_name - PC87365_SYSCTL_IN0;
806
807        if (operation == SENSORS_PROC_REAL_INFO)
808                *nrels_mag = 2;
809        else if (operation == SENSORS_PROC_REAL_READ) {
810                pc87360_update_client(client);
811                results[0] = IN_FROM_REG(data->in_min[nr]);
812                results[1] = IN_FROM_REG(data->in_max[nr]);
813                results[2] = IN_FROM_REG(data->in[nr]);
814                *nrels_mag = 3;
815        }
816        else if (operation == SENSORS_PROC_REAL_WRITE) {
817                if (*nrels_mag >= 1) {
818                        data->in_min[nr] = IN_TO_REG(results[0]);
819                        pc87360_write_value(data, 1, PC87365_REG_IN_BANK, nr);
820                        pc87360_write_value(data, 1, PC87365_REG_IN_MIN,
821                                            data->in_min[nr]);
822                }
823                if (*nrels_mag >= 2) {
824                        data->in_max[nr] = IN_TO_REG(results[1]);
825                        pc87360_write_value(data, 1, PC87365_REG_IN_MAX,
826                                            data->in_max[nr]);
827                }
828        }
829}
830
831void pc87365_in_status(struct i2c_client *client, int operation, int ctl_name,
832                       int *nrels_mag, long *results)
833{
834        struct pc87360_data *data = client->data;
835        int nr = ctl_name - PC87365_SYSCTL_IN0_STATUS;
836
837        if (operation == SENSORS_PROC_REAL_INFO)
838                *nrels_mag = 0;
839        else if (operation == SENSORS_PROC_REAL_READ) {
840                pc87360_update_client(client);
841                results[0] = IN_STATUS_FROM_REG(data->in_status[nr]);
842                *nrels_mag = 1;
843        }
844}
845
846void pc87365_temp(struct i2c_client *client, int operation, int ctl_name,
847                  int *nrels_mag, long *results)
848{
849        struct pc87360_data *data = client->data;
850        int nr = ctl_name - PC87365_SYSCTL_TEMP1;
851
852        if (operation == SENSORS_PROC_REAL_INFO)
853                *nrels_mag = 0;
854        else if (operation == SENSORS_PROC_REAL_READ) {
855                pc87360_update_client(client);
856                results[0] = TEMP_FROM_REG(data->temp_max[nr]);
857                results[1] = TEMP_FROM_REG(data->temp_min[nr]);
858                results[2] = TEMP_FROM_REG(data->temp_crit[nr]);
859                results[3] = TEMP_FROM_REG(data->temp[nr]);
860                *nrels_mag = 4;
861        }
862        else if (operation == SENSORS_PROC_REAL_WRITE) {
863                if (nr >= data->tempnr)
864                        return;
865                if (*nrels_mag >= 1) {
866                        pc87360_write_value(data, 2, PC87365_REG_TEMP_BANK, nr);
867                        data->temp_max[nr] = TEMP_TO_REG(results[0]);
868                        pc87360_write_value(data, 2, PC87365_REG_TEMP_MAX,
869                                            data->temp_max[nr]);
870                }
871                if (*nrels_mag >= 2) {
872                        data->temp_min[nr] = TEMP_TO_REG(results[1]);
873                        pc87360_write_value(data, 2, PC87365_REG_TEMP_MIN,
874                                            data->temp_min[nr]);
875                }
876                if (*nrels_mag >= 3) {
877                        data->temp_crit[nr] = TEMP_TO_REG(results[2]);
878                        pc87360_write_value(data, 2, PC87365_REG_TEMP_CRIT,
879                                            data->temp_crit[nr]);
880                }
881        }
882}
883
884void pc87365_temp_status(struct i2c_client *client, int operation, int ctl_name,
885                         int *nrels_mag, long *results)
886{
887        struct pc87360_data *data = client->data;
888        int nr = ctl_name - PC87365_SYSCTL_TEMP1_STATUS;
889
890        if (operation == SENSORS_PROC_REAL_INFO)
891                *nrels_mag = 0;
892        else if (operation == SENSORS_PROC_REAL_READ) {
893                pc87360_update_client(client);
894                results[0] = TEMP_STATUS_FROM_REG(data->temp_status[nr]);
895                *nrels_mag = 1;
896        }
897}
898
899
900static int __init pc87360_init(void)
901{
902        int i;
903
904        printk(KERN_INFO "pc87360.o version %s (%s)\n", LM_VERSION, LM_DATE);
905
906        if (pc87360_find(&devid, extra_isa)) {
907                printk(KERN_WARNING "pc87360.o: PC8736x not detected, "
908                       "module not inserted.\n");
909                return -ENODEV;
910        }
911
912        /* Arbitrarily pick one of the addresses */
913        for (i = 0; i < 3; i++) {
914                if (extra_isa[i] != 0x0000) {
915                        normal_isa[0] = extra_isa[i];
916                        break;
917                }
918        }
919
920        if (normal_isa[0] == 0x0000) {
921                printk(KERN_WARNING "pc87360.o: No active logical device, "
922                       "module not inserted.\n");
923                return -ENODEV;
924       
925        }
926
927        return i2c_add_driver(&pc87360_driver);
928}
929
930static void __exit pc87360_exit(void)
931{
932        i2c_del_driver(&pc87360_driver);
933}
934
935
936MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
937MODULE_DESCRIPTION("PC8736x hardware monitor");
938MODULE_LICENSE("GPL");
939
940module_init(pc87360_init);
941module_exit(pc87360_exit);
Note: See TracBrowser for help on using the browser.