root/lm-sensors/trunk/kernel/chips/gl518sm.c @ 293

Revision 293, 27.1 KB (checked in by kmalkki, 14 years ago)

(Kyösti) Small fixes for GL518.
Fan1 causes now alarm when rpm drops.
Fixed libsensors displaying incorrect temperature limits for rev 80.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
3                monitoring
4    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
5                              Kyösti Mälkki <kmalkki@cc.hut.fi>
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21*/
22
23#include <linux/module.h>
24#include <linux/malloc.h>
25#include "smbus.h"
26#include "sensors.h"
27#include "i2c.h"
28#include "version.h"
29#include "compat.h"
30
31/* Defining this will enable debug messages for the voltage iteration
32   code used with rev 0 ICs */
33#undef DEBUG_VIN
34
35/* Many GL518 constants specified below */
36
37/* The GL518 registers */
38#define GL518_REG_CHIP_ID 0x00
39#define GL518_REG_REVISION 0x01
40#define GL518_REG_VENDOR_ID 0x02
41#define GL518_REG_CONF 0x03
42#define GL518_REG_TEMP 0x04
43#define GL518_REG_TEMP_OVER 0x05
44#define GL518_REG_TEMP_HYST 0x06
45#define GL518_REG_FAN_COUNT 0x07
46#define GL518_REG_FAN_LIMIT 0x08
47#define GL518_REG_VIN1_LIMIT 0x09
48#define GL518_REG_VIN2_LIMIT 0x0a
49#define GL518_REG_VIN3_LIMIT 0x0b
50#define GL518_REG_VDD_LIMIT 0x0c
51#define GL518_REG_VIN3 0x0d
52#define GL518_REG_MISC 0x0f
53#define GL518_REG_ALARM 0x10
54#define GL518_REG_MASK 0x11
55#define GL518_REG_INT 0x12
56#define GL518_REG_VIN2 0x13
57#define GL518_REG_VIN1 0x14
58#define GL518_REG_VDD 0x15
59
60
61/* Conversions. Rounding is only done on the TO_REG variants. */
62
63#define TEMP_TO_REG(val) (((((val)<0?(val)-5:(val)+5) / 10) + 119) & 0xff)
64#define TEMP_FROM_REG(val) (((val) - 119) * 10)
65
66#define FAN_TO_REG(val,div) \
67 (( (val)==0 ? 255 : ((960000+(val)*(div))/(2*(val)*(div))) ) & 0xff)
68#define FAN_FROM_REG(val,div) \
69 ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/(2*(val)*(div))) )
70
71#define IN_TO_REG(val) ((((val)*10+8)/19) & 0xff)
72#define IN_FROM_REG(val) (((val)*19)/10)
73
74#define VDD_TO_REG(val) ((((val)*10+11)/23) & 0xff)
75#define VDD_FROM_REG(val) (((val)*23)/10)
76
77#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
78#define DIV_FROM_REG(val) (1 << (val))
79
80#define ALARMS_FROM_REG(val) val
81
82#define BEEP_ENABLE_TO_REG(val) ((val)?0:1)
83#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1)
84
85#define BEEPS_TO_REG(val) ((val) & 0x7f)
86#define BEEPS_FROM_REG(val) (val)
87
88/* Initial values */
89#define GL518_INIT_TEMP_OVER 600
90#define GL518_INIT_TEMP_HYST 500
91#define GL518_INIT_FAN_MIN_1 3000
92#define GL518_INIT_FAN_MIN_2 3000
93
94/* These are somewhat sane */
95#define GL518_INIT_VIN_1 330    /* 3.3 V */
96#define GL518_INIT_VIN_2 286    /* 12 V */
97#define GL518_INIT_VIN_3 260    /* Vcore */
98#define GL518_INIT_VDD 500      /* 5 V */
99
100#define GL518_INIT_PERCENTAGE 10
101
102#define GL518_INIT_VIN_MIN_1 \
103        (GL518_INIT_VIN_1 - GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
104#define GL518_INIT_VIN_MAX_1 \
105        (GL518_INIT_VIN_1 + GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
106#define GL518_INIT_VIN_MIN_2 \
107        (GL518_INIT_VIN_2 - GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
108#define GL518_INIT_VIN_MAX_2 \
109        (GL518_INIT_VIN_2 + GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
110#define GL518_INIT_VIN_MIN_3 \
111        (GL518_INIT_VIN_3 - GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
112#define GL518_INIT_VIN_MAX_3 \
113        (GL518_INIT_VIN_3 + GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
114#define GL518_INIT_VDD_MIN \
115        (GL518_INIT_VDD - GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
116#define GL518_INIT_VDD_MAX \
117        (GL518_INIT_VDD + GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
118
119
120/* Each client has this additional data */
121struct gl518_data {
122         int sysctl_id;
123
124         u8 revision;
125
126         struct semaphore update_lock;
127         char valid;                 /* !=0 if following fields are valid */
128         unsigned long last_updated; /* In jiffies */
129         unsigned long last_updated_v00; 
130                                     /* In jiffies (used only by rev00 chips) */
131
132         u8 voltage[4];              /* Register values; [0] = VDD */
133         u8 voltage_min[4];          /* Register values; [0] = VDD */
134         u8 voltage_max[4];          /* Register values; [0] = VDD */
135         u8 fan[2];
136         u8 fan_min[2];
137         u8 temp;                    /* Register values */
138         u8 temp_over;               /* Register values */
139         u8 temp_hyst;               /* Register values */
140         u8 alarms,beeps;            /* Register value */
141         u8 fan_div[2];              /* Register encoding, shifted right */
142         u8 beep_enable;             /* Boolean */       
143};
144
145/* load time parameter that says if we want to spend 10 seconds for
146   reading all voltages from a GL518 rev 0, or if we want to read zeros */
147int readall = 0;
148
149#ifdef MODULE
150extern int init_module(void);
151extern int cleanup_module(void);
152MODULE_PARM(readall,"i");
153#endif /* MODULE */
154
155static int gl518_init(void);
156static int gl518_cleanup(void);
157static int gl518_attach_adapter(struct i2c_adapter *adapter);
158static int gl518_detach_client(struct i2c_client *client);
159static int gl518_command(struct i2c_client *client, unsigned int cmd,
160                        void *arg);
161static void gl518_inc_use (struct i2c_client *client);
162static void gl518_dec_use (struct i2c_client *client);
163static u16 swap_bytes(u16 val);
164static int gl518_read_value(struct i2c_client *client, u8 reg);
165static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value);
166static void gl518_update_client(struct i2c_client *client);
167
168static void gl518_vin(struct i2c_client *client, int operation, 
169                      int ctl_name, int *nrels_mag, long *results);
170static void gl518_fan(struct i2c_client *client, int operation, 
171                      int ctl_name, int *nrels_mag, long *results);
172static void gl518_temp(struct i2c_client *client, int operation, 
173                       int ctl_name, int *nrels_mag, long *results);
174static void gl518_fan_div(struct i2c_client *client, int operation, 
175                          int ctl_name, int *nrels_mag, long *results);
176static void gl518_alarms(struct i2c_client *client, int operation, 
177                         int ctl_name, int *nrels_mag, long *results);
178static void gl518_beep(struct i2c_client *client, int operation, int ctl_name, 
179                int *nrels_mag, long *results);
180
181/* This is the driver that will be inserted */
182static struct i2c_driver gl518_driver = {
183  /* name */            "GL518SM sensor chip driver",
184  /* id */              I2C_DRIVERID_GL518,
185  /* flags */           DF_NOTIFY,
186  /* attach_adapter */  &gl518_attach_adapter,
187  /* detach_client */   &gl518_detach_client,
188  /* command */         &gl518_command,
189  /* inc_use */         &gl518_inc_use,
190  /* dec_use */         &gl518_dec_use
191};
192
193/* These files are created for each detected GL518. This is just a template;
194   though at first sight, you might think we could use a statically
195   allocated list, we need some way to get back to the parent - which
196   is done through one of the 'extra' fields which are initialized
197   when a new copy is allocated. */
198static ctl_table gl518_dir_table_template[] = {
199  { GL518_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &sensors_proc_real,
200    &sensors_sysctl_real, NULL, &gl518_vin },
201  { GL518_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &sensors_proc_real,
202    &sensors_sysctl_real, NULL, &gl518_vin },
203  { GL518_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &sensors_proc_real,
204    &sensors_sysctl_real, NULL, &gl518_vin },
205  { GL518_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &sensors_proc_real,
206    &sensors_sysctl_real, NULL, &gl518_vin },
207  { GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
208    &sensors_sysctl_real, NULL, &gl518_fan },
209  { GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
210    &sensors_sysctl_real, NULL, &gl518_fan },
211  { GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
212    &sensors_sysctl_real, NULL, &gl518_temp },
213  { GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
214    &sensors_sysctl_real, NULL, &gl518_fan_div },
215  { GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
216    &sensors_sysctl_real, NULL, &gl518_alarms },
217  { GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
218    &sensors_sysctl_real, NULL, &gl518_beep },
219  { 0 }
220};
221
222/* Used by init/cleanup */
223static int gl518_initialized = 0;
224
225/* I choose here for semi-static LM78 allocation. Complete dynamic
226   allocation could also be used; the code needed for this would probably
227   take more memory than the datastructure takes now. */
228#define MAX_GL518_NR 4
229static struct i2c_client *gl518_list[MAX_GL518_NR];
230
231
232int gl518_attach_adapter(struct i2c_adapter *adapter)
233{
234  int address,err,i;
235  struct i2c_client *new_client;
236  struct gl518_data *data;
237  u8 revision;
238  char name[11];
239
240  err = 0;
241
242  /* OK, this is no detection. I know. It will do for now, though.  */
243
244  /* Set err only if a global error would make registering other clients
245     impossible too (like out-of-memory). */
246  for (address = 0x2c; (! err) && (address <= 0x2d); address ++) {
247
248    /* Later on, we will keep a list of registered addresses for each
249       adapter, and check whether they are used here */
250   
251    if ((i = smbus_read_byte_data(adapter,address,GL518_REG_CHIP_ID)) < 0)
252      continue;
253
254    if (i != 0x80)
255      continue;
256
257    revision = smbus_read_byte_data(adapter,address,GL518_REG_REVISION);
258    if ((revision != 0x00) && (revision != 0x80))
259      continue;
260
261    /* Real detection code goes here */
262
263    /* Allocate space for a new client structure */
264    if (! (new_client =  kmalloc(sizeof(struct i2c_client) +
265                                sizeof(struct gl518_data),
266                               GFP_KERNEL))) {
267      err = -ENOMEM;
268      continue;
269    }
270
271    /* Find a place in our global list */
272    for (i = 0; i < MAX_GL518_NR; i++)
273      if (! gl518_list[i])
274         break;
275    if (i == MAX_GL518_NR) {
276      err = -ENOMEM;
277      printk("gl518sm.o: No empty slots left, recompile and heighten "
278             "MAX_GL518_NR!\n");
279      goto ERROR1;
280    }
281    gl518_list[i] = new_client;
282   
283    /* Fill the new client structure with data */
284    data = (struct gl518_data *) (new_client + 1);
285    new_client->data = data;
286    new_client->id = i;
287    new_client->addr = address;
288    new_client->adapter = adapter;
289    new_client->driver = &gl518_driver;
290    sprintf(new_client->name,"GL518SM chip rev. %02x",revision);
291    data->valid = 0;
292    data->update_lock = MUTEX;
293    data->revision = revision;
294   
295    /* Tell i2c-core a new client has arrived */
296    if ((err = i2c_attach_client(new_client)))
297      goto ERROR2;
298   
299    sprintf(name,"gl518sm-r%02x",revision);
300    /* Register a new directory entry with module sensors */
301    if ((err = sensors_register_entry(new_client,name,
302                                      gl518_dir_table_template)) < 0)
303      goto ERROR3;
304    data->sysctl_id = err;
305    err = 0;
306
307    /* Initialize the GL518SM chip */
308    /* Power-on defaults (bit 7=1) */
309    gl518_write_value(new_client,GL518_REG_CONF,0x80); 
310    /* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0),
311       standby mode (bit6=0) */
312    gl518_write_value(new_client,GL518_REG_CONF,0x04); 
313    /* Never interrupts */
314    gl518_write_value(new_client,GL518_REG_MASK,0x00);
315   
316    gl518_write_value(new_client,GL518_REG_TEMP_HYST,
317                      TEMP_TO_REG(GL518_INIT_TEMP_HYST));
318    gl518_write_value(new_client,GL518_REG_TEMP_OVER,
319                      TEMP_TO_REG(GL518_INIT_TEMP_OVER));
320    gl518_write_value(new_client,GL518_REG_MISC,(DIV_TO_REG(2) << 6) | 
321                                                (DIV_TO_REG(2) << 4));
322    gl518_write_value(new_client,GL518_REG_FAN_LIMIT,
323                      (FAN_TO_REG(GL518_INIT_FAN_MIN_1,2) << 8) |
324                      FAN_TO_REG(GL518_INIT_FAN_MIN_2,2));
325    gl518_write_value(new_client,GL518_REG_VIN1_LIMIT,
326                      (IN_TO_REG(GL518_INIT_VIN_MAX_1) << 8) |
327                      IN_TO_REG(GL518_INIT_VIN_MIN_1));
328    gl518_write_value(new_client,GL518_REG_VIN2_LIMIT,
329                      (IN_TO_REG(GL518_INIT_VIN_MAX_2) << 8) |
330                      IN_TO_REG(GL518_INIT_VIN_MIN_2));
331    gl518_write_value(new_client,GL518_REG_VIN3_LIMIT,
332                      (IN_TO_REG(GL518_INIT_VIN_MAX_3) << 8) |
333                      IN_TO_REG(GL518_INIT_VIN_MIN_3));
334    gl518_write_value(new_client,GL518_REG_VDD_LIMIT,
335                      (VDD_TO_REG(GL518_INIT_VDD_MAX) << 8) |
336                      VDD_TO_REG(GL518_INIT_VDD_MIN));
337    /* Clear status register (bit 5=1), start (bit6=1) */
338    gl518_write_value(new_client,GL518_REG_CONF,0x24);
339    gl518_write_value(new_client,GL518_REG_CONF,0x44);
340
341    continue;
342/* OK, this is not exactly good programming practice, usually. But it is
343   very code-efficient in this case. */
344
345ERROR3:
346    i2c_detach_client(new_client);
347ERROR2:
348    gl518_list[i] = NULL;
349ERROR1:
350    kfree(new_client);
351  }
352  return err;
353}
354
355int gl518_detach_client(struct i2c_client *client)
356{
357  int err,i;
358  for (i = 0; i < MAX_GL518_NR; i++)
359    if (client == gl518_list[i])
360      break;
361  if ((i == MAX_GL518_NR)) {
362    printk("gl518sm.o: Client to detach not found.\n");
363    return -ENOENT;
364  }
365
366  sensors_deregister_entry(((struct gl518_data *)(client->data))->sysctl_id);
367
368  if ((err = i2c_detach_client(client))) {
369    printk("gl518sm.o: Client deregistration failed, client not detached.\n");
370    return err;
371  }
372
373  gl518_list[i] = NULL;
374  kfree(client);
375  return 0;
376}
377
378
379/* No commands defined yet */
380int gl518_command(struct i2c_client *client, unsigned int cmd, void *arg)
381{
382  return 0;
383}
384
385/* Nothing here yet */
386void gl518_inc_use (struct i2c_client *client)
387{
388#ifdef MODULE
389  MOD_INC_USE_COUNT;
390#endif
391}
392
393/* Nothing here yet */
394void gl518_dec_use (struct i2c_client *client)
395{
396#ifdef MODULE
397  MOD_DEC_USE_COUNT;
398#endif
399}
400
401u16 swap_bytes(u16 val)
402{
403  return (val >> 8) | (val << 8);
404}
405
406/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
407   GL518 uses a high-byte first convention, which is exactly opposite to
408   the usual practice. */
409int gl518_read_value(struct i2c_client *client, u8 reg)
410{
411  if ((reg >= 0x07) && (reg <= 0x0c)) 
412    return swap_bytes(smbus_read_word_data(client->adapter,client->addr,reg));
413  else
414    return smbus_read_byte_data(client->adapter,client->addr,reg);
415}
416
417/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
418   GL518 uses a high-byte first convention, which is exactly opposite to
419   the usual practice. */
420int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
421{
422  if ((reg >= 0x07) && (reg <= 0x0c)) 
423    return smbus_write_word_data(client->adapter,client->addr,reg,
424                                 swap_bytes(value));
425  else
426    return smbus_write_byte_data(client->adapter,client->addr,reg,value);
427}
428
429void gl518_update_client(struct i2c_client *client)
430{
431  struct gl518_data *data = client->data;
432  int val;
433
434  down(&data->update_lock);
435
436  if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
437      (jiffies < data->last_updated) || ! data->valid) {
438
439#ifdef DEBUG
440    printk("Starting gl518 update\n");
441#endif
442
443    data->alarms = gl518_read_value(client,GL518_REG_INT);
444    data->beeps = gl518_read_value(client,GL518_REG_ALARM);
445
446    val = gl518_read_value(client,GL518_REG_VDD_LIMIT);
447    data->voltage_min[0] = val & 0xff;
448    data->voltage_max[0] = (val >> 8) & 0xff;
449    val = gl518_read_value(client,GL518_REG_VIN1_LIMIT);
450    data->voltage_min[1] = val & 0xff;
451    data->voltage_max[1] = (val >> 8) & 0xff;
452    val = gl518_read_value(client,GL518_REG_VIN2_LIMIT);
453    data->voltage_min[2] = val & 0xff;
454    data->voltage_max[2] = (val >> 8) & 0xff;
455    val = gl518_read_value(client,GL518_REG_VIN3_LIMIT);
456    data->voltage_min[3] = val & 0xff;
457    data->voltage_max[3] = (val >> 8) & 0xff;
458
459    if (data->revision != 0x00) {
460      data->voltage[0] = gl518_read_value(client,GL518_REG_VDD);
461      data->voltage[1] = gl518_read_value(client,GL518_REG_VIN1);
462      data->voltage[2] = gl518_read_value(client,GL518_REG_VIN2);
463    } else {
464      if (!readall) {
465        data->voltage[0] = 0;
466        data->voltage[1] = 0;
467        data->voltage[2] = 0;
468      }
469    }
470    data->voltage[3] = gl518_read_value(client,GL518_REG_VIN3);
471
472    val = gl518_read_value(client,GL518_REG_FAN_COUNT);
473    data->fan[0] = (val >> 8) & 0xff;
474    data->fan[1] = val & 0xff;
475
476    val = gl518_read_value(client,GL518_REG_FAN_LIMIT);
477    data->fan_min[0] = (val >> 8) & 0xff;
478    data->fan_min[1] = val & 0xff;
479
480    data->temp = gl518_read_value(client,GL518_REG_TEMP);
481    data->temp_over = gl518_read_value(client,GL518_REG_TEMP_OVER);
482    data->temp_hyst = gl518_read_value(client,GL518_REG_TEMP_HYST);
483
484    val = gl518_read_value(client,GL518_REG_MISC);
485    data->fan_div[0] = (val >> 6) & 0x03;
486    data->fan_div[1] = (val >> 4) & 0x03;
487    data->beep_enable = (gl518_read_value(client,GL518_REG_CONF) >> 2) & 1;
488
489    data->last_updated = jiffies;
490    data->valid = 1;
491  }
492
493  up(&data->update_lock);
494}
495
496/* Similar to gl518_update_client() but updates vdd, vin1, vin2 values
497   by doing slow and multiple comparisons for the GL518SM rev 00 that
498   lacks support for direct reading of these values.
499   (added by Ludovic Drolez (ldrolez@usa.net) */
500
501void gl518_update_client_rev00(struct i2c_client *client)
502{
503  struct gl518_data *data = client->data;
504  struct wait_queue *wait = NULL;
505  int j, min, max[3], delta;
506
507  down(&data->update_lock);
508
509#ifndef DEBUG_VIN
510  /* as that update is slow, we consider the data valid for 30 seconds */
511  if (jiffies - data->last_updated_v00 > 30*HZ ) {
512#else
513  if (jiffies - data->last_updated_v00 > HZ+HZ/2 ) {
514#endif
515
516     /* disable audible alarm */
517     gl518_write_value(client,GL518_REG_CONF,
518                        (gl518_read_value(client,GL518_REG_CONF) & 0xfb));
519
520    min = 1;
521    max[0] = VDD_TO_REG(GL518_INIT_VDD);
522    max[1] = IN_TO_REG(GL518_INIT_VIN_1); 
523    max[2] = IN_TO_REG(GL518_INIT_VIN_2);
524    delta = 32;
525    do {                       
526      gl518_write_value(client,GL518_REG_VDD_LIMIT,(max[0] << 8) | min);
527      gl518_write_value(client,GL518_REG_VIN1_LIMIT,(max[1] << 8) | min);
528      gl518_write_value(client,GL518_REG_VIN2_LIMIT,(max[2] << 8) | min);
529
530      /* we wait now 1.5 seconds before comparing */
531      current->state = TASK_INTERRUPTIBLE;
532      schedule_timeout(HZ + HZ/2);
533
534      /* read comparators */   
535      j = gl518_read_value(client,GL518_REG_INT);
536      if ((j & 1) == 0) {
537        max[0] = max[0] - delta;   
538      } else {
539        max[0] = max[0] + delta;
540      }
541      if ((j & 2) == 0) {
542        max[1] = max[1] - delta;   
543      } else {
544        max[1] = max[1] + delta;
545      }
546      if ((j & 4) == 0) {
547        max[2] = max[2] - delta;   
548      } else {
549        max[2] = max[2] + delta;
550      }
551      delta >>= 1;
552#ifdef DEBUG_VIN
553      printk("Iterations: %3d %3d %3d \n", max[0], max[1], max[2]);
554#endif
555    } while (delta != 0);
556   
557    /* do a final comparison to get to least significant bit */
558    gl518_write_value(client,GL518_REG_VDD_LIMIT,(max[0] << 8) | min);
559    gl518_write_value(client,GL518_REG_VIN1_LIMIT,(max[1] << 8) | min);
560    gl518_write_value(client,GL518_REG_VIN2_LIMIT,(max[2] << 8) | min);
561 
562    /* we wait now 1.5 seconds before comparing */
563    current->state = TASK_INTERRUPTIBLE;
564    schedule_timeout(HZ + HZ/2);
565
566    /* read comparators */     
567    j = gl518_read_value(client,GL518_REG_INT);
568    if ((j & 1) == 0) max[0]--;
569    if ((j & 2) == 0) max[1]--;
570    if ((j & 4) == 0) max[2]--;
571
572#ifdef DEBUG_VIN
573  if (data->revision) {
574     printk("Avdd: Meter: %3d, Search: %3d, Diff: %3d mV\n",
575             data->voltage[0], max[0], (max[0] - data->voltage[0]) * 23);
576     printk("Vin1: Meter: %3d, Search: %3d, Diff: %3d mV\n",
577             data->voltage[1], max[1], (max[1] - data->voltage[1]) * 19);
578     printk("Vin2: Meter: %3d, Search: %3d, Diff: %3d mV\n",
579             data->voltage[2], max[2], (max[2] - data->voltage[2]) * 19);
580  } else
581     printk("No voltage meter to read on rev 00 ICs\n");     
582#endif 
583
584    /* update voltages values */   
585    data->voltage[0] = max[0];
586    data->voltage[1] = max[1];
587    data->voltage[2] = max[2];
588   
589    /* restore original comparator values */
590    gl518_write_value(client,GL518_REG_VDD_LIMIT,
591                      (data->voltage_max[0] << 8) | data->voltage_min[0]);
592    gl518_write_value(client,GL518_REG_VIN1_LIMIT,
593                      (data->voltage_max[1] << 8) | data->voltage_min[1]);
594    gl518_write_value(client,GL518_REG_VIN2_LIMIT,
595                      (data->voltage_max[2] << 8) | data->voltage_min[2]);
596
597    data->last_updated_v00 = jiffies;
598   
599    /* reset audible alarm state */
600    gl518_write_value(client,GL518_REG_CONF,
601           (gl518_read_value(client,GL518_REG_CONF) & 0xfb) | 
602           (data->beep_enable << 2));
603  }
604  up(&data->update_lock);
605}
606
607void gl518_temp(struct i2c_client *client, int operation, int ctl_name,
608                int *nrels_mag, long *results)
609{
610  struct gl518_data *data = client->data;
611  if (operation == SENSORS_PROC_REAL_INFO)
612    *nrels_mag = 1;
613  else if (operation == SENSORS_PROC_REAL_READ) {
614    gl518_update_client(client);
615    results[0] = TEMP_FROM_REG(data->temp_over);
616    results[1] = TEMP_FROM_REG(data->temp_hyst);
617    results[2] = TEMP_FROM_REG(data->temp);
618    *nrels_mag = 3;
619  } else if (operation == SENSORS_PROC_REAL_WRITE) {
620    if (*nrels_mag >= 1) {
621      data->temp_over = TEMP_TO_REG(results[0]);
622      gl518_write_value(client,GL518_REG_TEMP_OVER,data->temp_over);
623    }
624    if (*nrels_mag >= 2) {
625      data->temp_hyst = TEMP_TO_REG(results[1]);
626      gl518_write_value(client,GL518_REG_TEMP_HYST,data->temp_hyst);
627    }
628  }
629}
630
631void gl518_vin(struct i2c_client *client, int operation, int ctl_name, 
632               int *nrels_mag, long *results)
633{
634  struct gl518_data *data = client->data;
635  int nr = ctl_name - GL518_SYSCTL_VDD;
636  int regnr,old=0;
637
638  if (operation == SENSORS_PROC_REAL_INFO)
639    *nrels_mag = 2;
640  else if (operation == SENSORS_PROC_REAL_READ) {
641    gl518_update_client(client);
642#ifndef DEBUG_VIN   
643    if ((data->revision == 0x00) && (nr != 3) && readall)
644       /* only update VDD, VIN1, VIN2 voltages */
645       gl518_update_client_rev00(client);
646#else
647    gl518_update_client_rev00(client);
648#endif
649    results[0] = nr?IN_FROM_REG(data->voltage_min[nr]):
650                    VDD_FROM_REG(data->voltage_min[nr]);
651    results[1] = nr?IN_FROM_REG(data->voltage_max[nr]):
652                    VDD_FROM_REG(data->voltage_max[nr]);
653    results[2] = nr?IN_FROM_REG(data->voltage[nr]):
654                    VDD_FROM_REG(data->voltage[nr]);
655    *nrels_mag = 3;
656  } else if (operation == SENSORS_PROC_REAL_WRITE) {
657    regnr=nr==0?GL518_REG_VDD_LIMIT:nr==1?GL518_REG_VIN1_LIMIT:nr==2?
658                GL518_REG_VIN2_LIMIT:GL518_REG_VIN3_LIMIT;
659    if (*nrels_mag == 1)
660      old = gl518_read_value(client,regnr) & 0xff00;
661    if (*nrels_mag >= 2) {
662      data->voltage_max[nr] = nr?IN_TO_REG(results[1]):VDD_TO_REG(results[1]);
663      old = data->voltage_max[nr] << 8;
664    }
665    if (*nrels_mag >= 1) {
666      data->voltage_min[nr] = nr?IN_TO_REG(results[0]):VDD_TO_REG(results[0]);
667      old |= data->voltage_min[nr];
668      gl518_write_value(client,regnr,old);
669    }
670  }
671} 
672
673
674void gl518_fan(struct i2c_client *client, int operation, int ctl_name, 
675               int *nrels_mag, long *results)
676{
677  struct gl518_data *data = client->data;
678  int nr = ctl_name - GL518_SYSCTL_FAN1;
679  int old;
680
681  if (operation == SENSORS_PROC_REAL_INFO)
682    *nrels_mag = 0;
683  else if (operation == SENSORS_PROC_REAL_READ) {
684    gl518_update_client(client);
685    results[0] = FAN_FROM_REG(data->fan_min[nr],
686                 DIV_FROM_REG(data->fan_div[nr]));
687    results[1] = FAN_FROM_REG(data->fan[nr],DIV_FROM_REG(data->fan_div[nr]));
688    *nrels_mag = 2;
689  } else if (operation == SENSORS_PROC_REAL_WRITE) {
690    if (*nrels_mag >= 1) {
691      data->fan_min[nr] = FAN_TO_REG(results[0],
692                                     DIV_FROM_REG(data->fan_div[nr]));
693      old = gl518_read_value(client,GL518_REG_FAN_LIMIT);
694      if (nr == 0)
695        old = (old & 0x00ff) | (data->fan_min[nr] << 8);
696      else
697        old = (old & 0xff00) | data->fan_min[nr];
698      gl518_write_value(client,GL518_REG_FAN_LIMIT,old);
699    }
700  }
701}
702
703
704void gl518_alarms(struct i2c_client *client, int operation, int ctl_name, 
705                  int *nrels_mag, long *results)
706{
707  struct gl518_data *data = client->data;
708  if (operation == SENSORS_PROC_REAL_INFO)
709    *nrels_mag = 0;
710  else if (operation == SENSORS_PROC_REAL_READ) {
711    gl518_update_client(client);
712    results[0] = ALARMS_FROM_REG(data->alarms);
713    *nrels_mag = 1;
714  }
715}
716
717void gl518_beep(struct i2c_client *client, int operation, int ctl_name, 
718                int *nrels_mag, long *results)
719{
720  struct gl518_data *data = client->data;
721  if (operation == SENSORS_PROC_REAL_INFO)
722    *nrels_mag = 0;
723  else if (operation == SENSORS_PROC_REAL_READ) {
724    gl518_update_client(client);
725    results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
726    results[1] = BEEPS_FROM_REG(data->beeps);
727    *nrels_mag = 2;
728  } else if (operation == SENSORS_PROC_REAL_WRITE) {
729    if (*nrels_mag >= 1) {
730      data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
731      gl518_write_value(client,GL518_REG_CONF,
732                        (gl518_read_value(client,GL518_REG_CONF) & 0xfb) | 
733                         (data->beep_enable << 2));
734    }
735    if (*nrels_mag >= 2) {
736      data->beeps = BEEPS_TO_REG(results[1]);
737      gl518_write_value(client,GL518_REG_ALARM,data->beeps);
738    }
739  }
740}
741
742void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name,
743                   int *nrels_mag, long *results)
744{
745  struct gl518_data *data = client->data;
746  int old;
747  if (operation == SENSORS_PROC_REAL_INFO)
748    *nrels_mag = 0;
749  else if (operation == SENSORS_PROC_REAL_READ) {
750    gl518_update_client(client);
751    results[0] = DIV_FROM_REG(data->fan_div[0]);
752    results[1] = DIV_FROM_REG(data->fan_div[1]);
753    *nrels_mag = 2;
754  } else if (operation == SENSORS_PROC_REAL_WRITE) {
755    old = gl518_read_value(client,GL518_REG_MISC);
756    if (*nrels_mag >= 2) {
757      data->fan_div[1] = DIV_TO_REG(results[1]);
758      old = (old & 0xcf) | (data->fan_div[1] << 4);
759    }
760    if (*nrels_mag >= 1) {
761      data->fan_div[0] = DIV_TO_REG(results[0]);
762      old = (old & 0x3f) | (data->fan_div[0] << 6);
763    }
764    gl518_write_value(client,GL518_REG_MISC,old);
765  }
766}
767
768
769int gl518_init(void)
770{
771  int res;
772
773  printk("gl518sm.o version %s (%s)\n",LM_VERSION,LM_DATE);
774  gl518_initialized = 0;
775  if ((res = i2c_add_driver(&gl518_driver))) {
776    printk("gl518sm.o: Driver registration failed, module not inserted.\n");
777    gl518_cleanup();
778    return res;
779  }
780  gl518_initialized ++;
781  return 0;
782}
783
784int gl518_cleanup(void)
785{
786  int res;
787
788  if (gl518_initialized >= 1) {
789    if ((res = i2c_del_driver(&gl518_driver))) {
790      printk("gl518.o: Driver deregistration failed, module not removed.\n");
791      return res;
792    }
793    gl518_initialized --;
794  }
795
796  return 0;
797}
798
799
800#ifdef MODULE
801
802MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
803MODULE_DESCRIPTION("GL518SM driver");
804
805int init_module(void)
806{
807  return gl518_init();
808}
809
810int cleanup_module(void)
811{
812  return gl518_cleanup();
813}
814
815#endif /* MODULE */
Note: See TracBrowser for help on using the browser.