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

Revision 2474, 27.9 KB (checked in by khali, 9 years ago)

Correctly handle the case where some of the logical devices are

disabled.
Add KERN constants to printks.
Reindent.
Fix error path in detect function.

  • 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) & 0x86)
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) & 0xCE)
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
441        superio_exit();
442        return 0;
443}
444
445/* We don't really care about the address.
446   Read from extra_isa instead. */
447int pc87360_detect(struct i2c_adapter *adapter, int address,
448                   unsigned short flags, int kind)
449{
450        int i;
451        struct i2c_client *new_client;
452        struct pc87360_data *data;
453        int err = 0;
454        const char *type_name = "pc87360";
455        const char *client_name = "PC8736x chip";
456        const ctl_table *template = pc87360_dir_table_template;
457
458        if (!i2c_is_isa_adapter(adapter)) {
459                return 0;
460        }
461
462        for (i = 0; i < 3; i++) {
463                if (extra_isa[i]
464                 && check_region(extra_isa[i], PC87360_EXTENT)) {
465                        printk(KERN_ERR "pc87360.o: region 0x%x already in "
466                               "use!\n", address);
467                        return -ENODEV;
468                }
469        }
470
471        if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL))) {
472                return -ENOMEM;
473        }
474
475        new_client = &data->client;
476        new_client->addr = address;
477        init_MUTEX(&data->lock);
478        new_client->data = data;
479        new_client->adapter = adapter;
480        new_client->driver = &pc87360_driver;
481        new_client->flags = 0;
482
483        data->fannr = 2;
484        data->innr = 0;
485        data->tempnr = 0;
486       
487        switch (devid) {
488        case 0xe8:
489                type_name = "pc87363";
490                break;
491        case 0xe4:
492                type_name = "pc87364";
493                data->fannr = 3;
494                break;
495        case 0xe5:
496                type_name = "pc87365";
497                template = pc87365_dir_table_template;
498                data->fannr = extra_isa[0] ? 3 : 0;
499                data->innr = extra_isa[1] ? 11 : 0;
500                data->tempnr = extra_isa[2] ? 2 : 0;
501                break;
502        case 0xe9:
503                type_name = "pc87366";
504                template = pc87365_dir_table_template;
505                data->fannr = extra_isa[0] ? 3 : 0;
506                data->innr = extra_isa[1] ? 11 : 0;
507                data->tempnr = extra_isa[2] ? 3 : 0;
508                break;
509        }
510
511        for (i = 0; i < 3; i++) {
512                if ((data->address[i] = extra_isa[i])) {
513                        request_region(extra_isa[i], PC87360_EXTENT, "pc87360");
514                }
515        }
516        strcpy(new_client->name, client_name);
517
518        new_client->id = pc87360_id++;
519        data->valid = 0;
520        init_MUTEX(&data->update_lock);
521
522        if ((err = i2c_attach_client(new_client)))
523                goto ERROR1;
524
525        if ((i = i2c_register_entry((struct i2c_client *) new_client,
526                                    type_name, template)) < 0) {
527                err = i;
528                goto ERROR2;
529        }
530        data->sysctl_id = i;
531
532        return 0;
533
534ERROR2:
535        i2c_detach_client(new_client);
536ERROR1:
537        for (i = 0; i < 3; i++) {
538                if (data->address[i]) {
539                        release_region(data->address[i], PC87360_EXTENT);
540                }
541        }
542        kfree(data);
543        return err;
544}
545
546static int pc87360_detach_client(struct i2c_client *client)
547{
548        struct pc87360_data *data = client->data;
549        int i, err;
550
551        i2c_deregister_entry(data->sysctl_id);
552
553        if ((err = i2c_detach_client(client))) {
554                printk(KERN_ERR "pc87360.o: Client deregistration failed, "
555                       "client not detached.\n");
556                return err;
557        }
558
559        for (i = 0; i < 3; i++) {
560                if (data->address[i]) {
561                        release_region(data->address[i], PC87360_EXTENT);
562                }
563        }
564        kfree(client->data);
565
566        return 0;
567}
568
569/* ldi is the logical device index:
570   0: fans
571   1: voltages
572   2: temperatures */
573static int pc87360_read_value(struct pc87360_data *data, int ldi, u8 reg)
574{
575        int res;
576
577        down(&(data->lock));
578        res = inb_p(data->address[ldi] + reg);
579        up(&(data->lock));
580        return res;
581}
582
583static int pc87360_write_value(struct pc87360_data *data, int ldi, u8 reg,
584                               u8 value)
585{
586        down(&(data->lock));
587        outb_p(value, data->address[ldi] + reg);
588        up(&(data->lock));
589        return 0;
590}
591
592static void pc87360_update_client(struct i2c_client *client)
593{
594        struct pc87360_data *data = client->data;
595        int i;
596
597        down(&data->update_lock);
598
599        if ((jiffies - data->last_updated > HZ + HZ / 2) ||
600            (jiffies < data->last_updated) || !data->valid) {
601                for (i = 0; i < data->fannr; i++) {
602                        data->fan[i] = pc87360_read_value(data, 0,
603                                       PC87360_REG_FAN(i));
604                        data->fan_min[i] = pc87360_read_value(data, 0,
605                                           PC87360_REG_FAN_MIN(i));
606                        data->fan_status[i] = pc87360_read_value(data, 0,
607                                              PC87360_REG_FAN_STATUS(i));
608                        data->pwm[i] = pc87360_read_value(data, 0,
609                                       PC87360_REG_PWM(i));
610                }
611
612                for (i = 0; i < data->innr; i++) {
613                        pc87360_write_value(data, 1, PC87365_REG_IN_BANK, i);
614                        data->in_status[i] = pc87360_read_value(data, 1,
615                                             PC87365_REG_IN_STATUS);
616                        if (data->in_status[i] & 0x01) {
617                                data->in[i] = pc87360_read_value(data, 1,
618                                              PC87365_REG_IN);
619                                data->in_min[i] = pc87360_read_value(data, 1,
620                                                  PC87365_REG_IN_MIN);
621                                data->in_max[i] = pc87360_read_value(data, 1,
622                                                  PC87365_REG_IN_MAX);
623                        }
624                        data->in_alarms = pc87360_read_value(data, 1,
625                                          PC87365_REG_IN_ALARMS1)
626                                        | (pc87360_read_value(data, 1,
627                                           PC87365_REG_IN_ALARMS2) << 8);
628                }
629
630                for (i = 0; i < data->tempnr; i++) {
631                        pc87360_write_value(data, 2, PC87365_REG_TEMP_BANK, i);
632                        data->temp_status[i] = pc87360_read_value(data, 2,
633                                               PC87365_REG_TEMP_STATUS);
634                        if (data->temp_status[i] & 0x01) {
635                                data->temp[i] = pc87360_read_value(data, 2,
636                                                PC87365_REG_TEMP);
637                                data->temp_min[i] = pc87360_read_value(data, 2,
638                                                    PC87365_REG_TEMP_MIN);
639                                data->temp_max[i] = pc87360_read_value(data, 2,
640                                                    PC87365_REG_TEMP_MAX);
641                                data->temp_crit[i] = pc87360_read_value(data, 2,
642                                                     PC87365_REG_TEMP_CRIT);
643                        }
644                        data->temp_alarms = pc87360_read_value(data, 2,
645                                            PC87365_REG_TEMP_ALARMS);
646                }
647
648                data->last_updated = jiffies;
649                data->valid = 1;
650        }
651
652        up(&data->update_lock);
653}
654
655
656void pc87365_alarms(struct i2c_client *client, int operation, int ctl_name,
657                    int *nrels_mag, long *results)
658{
659        struct pc87360_data *data = client->data;
660
661        if (operation == SENSORS_PROC_REAL_INFO)
662                *nrels_mag = 0;
663        else if (operation == SENSORS_PROC_REAL_READ) {
664                pc87360_update_client(client);
665                results[0] = data->in_alarms;
666                results[1] = data->temp_alarms;
667                *nrels_mag = 2;
668        }
669}
670
671void pc87360_fan(struct i2c_client *client, int operation, int ctl_name,
672                 int *nrels_mag, long *results)
673{
674        struct pc87360_data *data = client->data;
675        int nr = ctl_name - PC87360_SYSCTL_FAN1;
676
677        if (operation == SENSORS_PROC_REAL_INFO)
678                *nrels_mag = 0;
679        else if (operation == SENSORS_PROC_REAL_READ) {
680                pc87360_update_client(client);
681                results[0] = FAN_FROM_REG(data->fan_min[nr],
682                             FAN_DIV_FROM_REG(data->fan_status[nr]));
683                results[1] = FAN_FROM_REG(data->fan[nr],
684                             FAN_DIV_FROM_REG(data->fan_status[nr]));
685                *nrels_mag = 2;
686        }
687        /* We ignore National's recommendation */
688        else if (operation == SENSORS_PROC_REAL_WRITE) {
689                if (nr >= data->fannr)
690                        return;
691                if (*nrels_mag >= 1) {
692                        data->fan_min[nr] = FAN_TO_REG(results[0],
693                                            FAN_DIV_FROM_REG(data->fan_status[nr]));
694                        pc87360_write_value(data, 0, PC87360_REG_FAN_MIN(nr),
695                                            data->fan_min[nr]);
696                }
697        }
698}
699
700void pc87360_fan_div(struct i2c_client *client, int operation,
701                     int ctl_name, int *nrels_mag, long *results)
702{
703        struct pc87360_data *data = client->data;
704        int i;
705
706        if (operation == SENSORS_PROC_REAL_INFO)
707                *nrels_mag = 0;
708        else if (operation == SENSORS_PROC_REAL_READ) {
709                pc87360_update_client(client);
710                for (i = 0; i < data->fannr; i++) {
711                        results[i] = FAN_DIV_FROM_REG(data->fan_status[i]);
712                }
713                *nrels_mag = data->fannr;
714        }
715        /* We ignore National's recommendation */
716        else if (operation == SENSORS_PROC_REAL_WRITE) {
717                for (i = 0; i < data->fannr && i < *nrels_mag; i++) {
718                        /* Preserve fan min */
719                        int fan_min = FAN_FROM_REG(data->fan_min[i],
720                                      FAN_DIV_FROM_REG(data->fan_status[i]));
721                        data->fan_status[i] = (data->fan_status[i] & 0x9F)
722                                            | FAN_DIV_TO_REG(results[i]);
723                        pc87360_write_value(data, 0, PC87360_REG_FAN_STATUS(i),
724                                            data->fan_status[i]);
725                        data->fan_min[i] = FAN_TO_REG(fan_min,
726                                           FAN_DIV_FROM_REG(data->fan_status[i]));
727                        pc87360_write_value(data, 0, PC87360_REG_FAN_MIN(i),
728                                            data->fan_min[i]);
729                }
730        }
731}
732
733void pc87360_pwm(struct i2c_client *client, int operation, int ctl_name,
734                 int *nrels_mag, long *results)
735{
736        struct pc87360_data *data = client->data;
737        int nr = ctl_name - PC87360_SYSCTL_PWM1;
738
739        if (operation == SENSORS_PROC_REAL_INFO)
740                *nrels_mag = 0;
741        else if (operation == SENSORS_PROC_REAL_READ) {
742                pc87360_update_client(client);
743                results[0] = data->pwm[nr];
744                *nrels_mag = 1;
745        }
746        else if (operation == SENSORS_PROC_REAL_WRITE) {
747                if (*nrels_mag >= 1)
748                {
749                        data->pwm[nr] = SENSORS_LIMIT(results[0], 0, 255);
750                        pc87360_write_value(data, 0, PC87360_REG_PWM(nr),
751                                            data->pwm[nr]);
752                }
753        }
754}
755
756void pc87360_fan_status(struct i2c_client *client, int operation, int ctl_name,
757                        int *nrels_mag, long *results)
758{
759        struct pc87360_data *data = client->data;
760        int nr = ctl_name - PC87360_SYSCTL_FAN1_STATUS;
761
762        if (operation == SENSORS_PROC_REAL_INFO)
763                *nrels_mag = 0;
764        else if (operation == SENSORS_PROC_REAL_READ) {
765                pc87360_update_client(client);
766                results[0] = FAN_STATUS_FROM_REG(data->fan_status[nr]);
767                *nrels_mag = 1;
768        }
769}
770
771void pc87365_in(struct i2c_client *client, int operation, int ctl_name,
772                int *nrels_mag, long *results)
773{
774        struct pc87360_data *data = client->data;
775        int nr = ctl_name - PC87365_SYSCTL_IN0;
776
777        if (operation == SENSORS_PROC_REAL_INFO)
778                *nrels_mag = 2;
779        else if (operation == SENSORS_PROC_REAL_READ) {
780                pc87360_update_client(client);
781                results[0] = IN_FROM_REG(data->in_min[nr]);
782                results[1] = IN_FROM_REG(data->in_max[nr]);
783                results[2] = IN_FROM_REG(data->in[nr]);
784                *nrels_mag = 3;
785        }
786        else if (operation == SENSORS_PROC_REAL_WRITE) {
787                if (*nrels_mag >= 1) {
788                        data->in_min[nr] = IN_TO_REG(results[0]);
789                        pc87360_write_value(data, 1, PC87365_REG_IN_BANK, nr);
790                        pc87360_write_value(data, 1, PC87365_REG_IN_MIN,
791                                            data->in_min[nr]);
792                }
793                if (*nrels_mag >= 2) {
794                        data->in_max[nr] = IN_TO_REG(results[1]);
795                        pc87360_write_value(data, 1, PC87365_REG_IN_MAX,
796                                            data->in_max[nr]);
797                }
798        }
799}
800
801void pc87365_in_status(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_STATUS;
806
807        if (operation == SENSORS_PROC_REAL_INFO)
808                *nrels_mag = 0;
809        else if (operation == SENSORS_PROC_REAL_READ) {
810                pc87360_update_client(client);
811                results[0] = IN_STATUS_FROM_REG(data->in_status[nr]);
812                *nrels_mag = 1;
813        }
814}
815
816void pc87365_temp(struct i2c_client *client, int operation, int ctl_name,
817                  int *nrels_mag, long *results)
818{
819        struct pc87360_data *data = client->data;
820        int nr = ctl_name - PC87365_SYSCTL_TEMP1;
821
822        if (operation == SENSORS_PROC_REAL_INFO)
823                *nrels_mag = 0;
824        else if (operation == SENSORS_PROC_REAL_READ) {
825                pc87360_update_client(client);
826                results[0] = TEMP_FROM_REG(data->temp_max[nr]);
827                results[1] = TEMP_FROM_REG(data->temp_min[nr]);
828                results[1] = TEMP_FROM_REG(data->temp_crit[nr]);
829                results[2] = TEMP_FROM_REG(data->temp[nr]);
830                *nrels_mag = 4;
831        }
832        else if (operation == SENSORS_PROC_REAL_WRITE) {
833                if (nr >= data->tempnr)
834                        return;
835                if (*nrels_mag >= 1) {
836                        pc87360_write_value(data, 2, PC87365_REG_TEMP_BANK, nr);
837                        data->temp_max[nr] = TEMP_TO_REG(results[0]);
838                        pc87360_write_value(data, 2, PC87365_REG_TEMP_MAX,
839                                            data->temp_max[nr]);
840                }
841                if (*nrels_mag >= 2) {
842                        data->temp_min[nr] = TEMP_TO_REG(results[1]);
843                        pc87360_write_value(data, 2, PC87365_REG_TEMP_MAX,
844                                            data->temp_max[nr]);
845                }
846                if (*nrels_mag >= 3) {
847                        data->temp_crit[nr] = TEMP_TO_REG(results[2]);
848                        pc87360_write_value(data, 2, PC87365_REG_TEMP_CRIT,
849                                            data->temp_crit[nr]);
850                }
851        }
852}
853
854void pc87365_temp_status(struct i2c_client *client, int operation, int ctl_name,
855                         int *nrels_mag, long *results)
856{
857        struct pc87360_data *data = client->data;
858        int nr = ctl_name - PC87365_SYSCTL_TEMP1_STATUS;
859
860        if (operation == SENSORS_PROC_REAL_INFO)
861                *nrels_mag = 0;
862        else if (operation == SENSORS_PROC_REAL_READ) {
863                pc87360_update_client(client);
864                results[0] = TEMP_STATUS_FROM_REG(data->temp_status[nr]);
865                *nrels_mag = 1;
866        }
867}
868
869
870static int __init pc87360_init(void)
871{
872        int i;
873
874        printk(KERN_INFO "pc87360.o version %s (%s)\n", LM_VERSION, LM_DATE);
875
876        if (pc87360_find(&devid, extra_isa)) {
877                printk(KERN_WARNING "pc87360.o: PC8736x not detected, "
878                       "module not inserted.\n");
879                return -ENODEV;
880        }
881
882        /* Arbitrarily pick one of the addresses */
883        for (i = 0; i < 3; i++) {
884                if (extra_isa[i] != 0x0000) {
885                        normal_isa[0] = extra_isa[i];
886                        break;
887                }
888        }
889
890        if (normal_isa[0] == 0x0000) {
891                printk(KERN_WARNING "pc87360.o: No active logical device, "
892                       "module not inserted.\n");
893                return -ENODEV;
894       
895        }
896
897        return i2c_add_driver(&pc87360_driver);
898}
899
900static void __exit pc87360_exit(void)
901{
902        i2c_del_driver(&pc87360_driver);
903}
904
905
906MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
907MODULE_DESCRIPTION("PC8736x hardware monitor");
908MODULE_LICENSE("GPL");
909
910module_init(pc87360_init);
911module_exit(pc87360_exit);
Note: See TracBrowser for help on using the browser.