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

Revision 330, 27.2 KB (checked in by mds, 14 years ago)

fixed off-by-factor-of-2 of FAN_FROM_REG in w83781d.
Improved bounds checking for FAN_TO_REG in all files.
For lm78-compatible drivers, minimum speed is 2657 RPM when the
divisor is 2. Attempts to set lower than that now are set to 0.
Before was set to a random number.
Thanks Jeff Dairiki.

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