root/lm-sensors/trunk/kernel/chips/adm9240.c @ 351

Revision 351, 27.4 KB (checked in by mds, 16 years ago)

got rid of compile warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    adm9240.c - Part of lm_sensors, Linux kernel modules for hardware
3             monitoring
4    Copyright (c) 1999  Frodo Looijaard <frodol@dds.nl>
5    and Philip Edelbrock <phil@netroedge.com>
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        A couple notes about the ADM9240:
24
25* It claims to be 'LM7x' register compatible.  This must be in reference
26  to only the LM78, because it is missing stuff to emulate LM75's as well.
27  (like the Winbond W83781 does)
28 
29* This driver was written from rev. 0 of the PDF, but it seems well
30  written and complete (unlike the W83781 which is horrible and has
31  supposidly gone through a few revisions.. rev 0 of that one must
32  have been in crayon on construction paper...)
33 
34* All analog inputs can range from 0 to 2.5, eventhough some inputs are
35  marked as being 5V, 12V, etc.  I don't have any real voltages going
36  into my prototype, so I'm not sure that things are computed right,
37  but at least the limits seem to be working OK.
38 
39* Another curiousity is that the fan_div seems to be read-only.  I.e.,
40  any written value to it doesn't seem to make any difference.  The
41  fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases).
42 
43 
44  --Phil
45
46*/
47
48#include <linux/module.h>
49#include <linux/malloc.h>
50#include <linux/proc_fs.h>
51#include <linux/ioport.h>
52#include <linux/sysctl.h>
53#include <asm/errno.h>
54#include <asm/io.h>
55#include <linux/types.h>
56#include "smbus.h"
57#include "version.h"
58#include "i2c-isa.h"
59#include "sensors.h"
60#include "i2c.h"
61#include "compat.h"
62
63/* Many ADM9240 constants specified below */
64
65#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2)
66#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2)
67#define ADM9240_REG_IN(nr) (0x20 + (nr))
68
69/* The ADM9240 registers */
70#define ADM9240_REG_TEST 0x15
71#define ADM9240_REG_ANALOG_OUT 0x19
72/* These are all read-only */
73#define ADM9240_REG_2_5V 0x20
74#define ADM9240_REG_VCCP1 0x21
75#define ADM9240_REG_3_3V 0x22
76#define ADM9240_REG_5V 0x23
77#define ADM9240_REG_12V 0x24
78#define ADM9240_REG_VCCP2 0x25
79#define ADM9240_REG_TEMP 0x27
80#define ADM9240_REG_FAN1 0x28
81#define ADM9240_REG_FAN2 0x29
82#define ADM9240_REG_COMPANY_ID 0x3E  /* 0x23 for ADM9240; 0xDA for DS1780 */
83#define ADM9240_REG_DIE_REV 0x3F
84/* These are read/write */
85#define ADM9240_REG_2_5V_HIGH 0x2B
86#define ADM9240_REG_2_5V_LOW 0x2C
87#define ADM9240_REG_VCCP1_HIGH 0x2D
88#define ADM9240_REG_VCCP1_LOW 0x2E
89#define ADM9240_REG_3_3V_HIGH 0x2F
90#define ADM9240_REG_3_3V_LOW 0x30
91#define ADM9240_REG_5V_HIGH 0x31
92#define ADM9240_REG_5V_LOW 0x32
93#define ADM9240_REG_12V_HIGH 0x33
94#define ADM9240_REG_12V_LOW 0x34
95#define ADM9240_REG_VCCP2_HIGH 0x35
96#define ADM9240_REG_VCCP2_LOW 0x36
97#define ADM9240_REG_TOS 0x39
98#define ADM9240_REG_THYST 0x3A
99#define ADM9240_REG_FAN1_MIN 0x3B
100#define ADM9240_REG_FAN2_MIN 0x3C
101
102#define ADM9240_REG_CONFIG 0x40
103#define ADM9240_REG_INT1_STAT 0x41
104#define ADM9240_REG_INT2_STAT 0x42
105#define ADM9240_REG_INT1_MASK 0x43
106#define ADM9240_REG_INT2_MASK 0x44
107
108#define ADM9240_REG_COMPAT 0x45 /* dummy compat. register for other drivers? */
109#define ADM9240_REG_CHASSIS_CLEAR 0x46
110#define ADM9240_REG_VID_FAN_DIV 0x47
111#define ADM9240_REG_I2C_ADDR 0x48
112#define ADM9240_REG_VID4 0x49
113#define ADM9240_REG_TEMP_CONFIG 0x4B
114
115/* Conversions. Rounding is only done on the TO_REG variants. */
116#define IN_TO_REG(val,nr) ((val) & 0xff)
117#define IN_FROM_REG(val,nr) (val)
118
119static inline unsigned char
120FAN_TO_REG (unsigned rpm, unsigned divisor)
121{
122  unsigned val;
123 
124  if (rpm == 0)
125      return 255;
126
127  val = (1350000 + rpm * divisor / 2) / (rpm * divisor);
128  if (val > 255)
129      val = 255;
130  return val;
131}
132#define FAN_FROM_REG(val,div) ((val)==0?-1:\
133                               (val)==255?0:1350000/((div)*(val)))
134
135#define TEMP_FROM_REG(val) adm9240_temp_from_reg(val)
136
137#define TEMP_LIMIT_TO_REG(val) (((val)<0?(((val)-50)/100)&0xff:\
138                                           ((val)+50)/100) & 0xff)
139#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100)
140
141#define ALARMS_FROM_REG(val) (val)
142
143#define DIV_FROM_REG(val) (1 << val)
144#define DIV_TO_REG(val) (val==1?0:(val==2?1:(val==4?2:3)))
145
146#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
147                           (val)>=0x06?0:205-(val)*5)
148
149/* Initial limits */
150#define ADM9240_INIT_IN_0 190
151#define ADM9240_INIT_IN_1 190
152#define ADM9240_INIT_IN_2 190
153#define ADM9240_INIT_IN_3 190
154#define ADM9240_INIT_IN_4 190
155#define ADM9240_INIT_IN_5 190
156
157#define ADM9240_INIT_IN_PERCENTAGE 10
158
159#define ADM9240_INIT_IN_MIN_0 \
160        (ADM9240_INIT_IN_0 - ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
161#define ADM9240_INIT_IN_MAX_0 \
162        (ADM9240_INIT_IN_0 + ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
163#define ADM9240_INIT_IN_MIN_1 \
164        (ADM9240_INIT_IN_1 - ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
165#define ADM9240_INIT_IN_MAX_1 \
166        (ADM9240_INIT_IN_1 + ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
167#define ADM9240_INIT_IN_MIN_2 \
168        (ADM9240_INIT_IN_2 - ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
169#define ADM9240_INIT_IN_MAX_2 \
170        (ADM9240_INIT_IN_2 + ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
171#define ADM9240_INIT_IN_MIN_3 \
172        (ADM9240_INIT_IN_3 - ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
173#define ADM9240_INIT_IN_MAX_3 \
174        (ADM9240_INIT_IN_3 + ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
175#define ADM9240_INIT_IN_MIN_4 \
176        (ADM9240_INIT_IN_4 - ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
177#define ADM9240_INIT_IN_MAX_4 \
178        (ADM9240_INIT_IN_4 + ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
179#define ADM9240_INIT_IN_MIN_5 \
180        (ADM9240_INIT_IN_5 - ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
181#define ADM9240_INIT_IN_MAX_5 \
182        (ADM9240_INIT_IN_5 + ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
183
184#define ADM9240_INIT_FAN_MIN_1 3000
185#define ADM9240_INIT_FAN_MIN_2 3000
186
187#define ADM9240_INIT_TEMP_OS_MAX 600
188#define ADM9240_INIT_TEMP_OS_HYST 500
189#define ADM9240_INIT_TEMP_HOT_MAX 700
190#define ADM9240_INIT_TEMP_HOT_HYST 600
191
192#ifdef MODULE
193extern int init_module(void);
194extern int cleanup_module(void);
195#endif /* MODULE */
196
197/* For each registered ADM9240, we need to keep some data in memory. That
198   data is pointed to by adm9240_list[NR]->data. The structure itself is
199   dynamically allocated, at the same time when a new adm9240 client is
200   allocated. */
201struct adm9240_data {
202         int sysctl_id;
203
204         struct semaphore update_lock;
205         char valid;                 /* !=0 if following fields are valid */
206         unsigned long last_updated; /* In jiffies */
207
208         u8 in[6];                   /* Register value */
209         u8 in_max[6];               /* Register value */
210         u8 in_min[6];               /* Register value */
211         u8 fan[2];                  /* Register value */
212         u8 fan_min[2];              /* Register value */
213         u8 fan_div[2];              /* Register encoding, shifted right */
214         int temp;                   /* Temp, shifted right */
215         u8 temp_os_max;             /* Register value */
216         u8 temp_os_hyst;            /* Register value */
217         u16 alarms;                 /* Register encoding, combined */
218         u8 analog_out;              /* Register value */
219         u8 vid;                     /* Register value combined */
220};
221
222
223static int adm9240_init(void);
224static int adm9240_cleanup(void);
225
226static int adm9240_attach_adapter(struct i2c_adapter *adapter);
227static int adm9240_detach_client(struct i2c_client *client);
228static int adm9240_new_client(struct i2c_adapter *adapter,
229                           struct i2c_client *new_client);
230static void adm9240_remove_client(struct i2c_client *client);
231static int adm9240_command(struct i2c_client *client, unsigned int cmd, 
232                        void *arg);
233static void adm9240_inc_use (struct i2c_client *client);
234static void adm9240_dec_use (struct i2c_client *client);
235
236static long adm9240_temp_from_reg(u16 regs);
237
238static int adm9240_read_value(struct i2c_client *client, u8 register);
239static int adm9240_write_value(struct i2c_client *client, u8 register, u8 value);
240static void adm9240_update_client(struct i2c_client *client);
241static void adm9240_init_client(struct i2c_client *client);
242
243
244static void adm9240_in(struct i2c_client *client, int operation, int ctl_name,
245                    int *nrels_mag, long *results);
246static void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
247                     int *nrels_mag, long *results);
248static void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
249                      int *nrels_mag, long *results);
250static void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
251                        int *nrels_mag, long *results);
252static void adm9240_fan_div(struct i2c_client *client, int operation, int ctl_name,
253                         int *nrels_mag, long *results);
254static void adm9240_analog_out(struct i2c_client *client, int operation, int ctl_name,
255                         int *nrels_mag, long *results);
256static void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
257                         int *nrels_mag, long *results);
258
259/* I choose here for semi-static ADM9240 allocation. Complete dynamic
260   allocation could also be used; the code needed for this would probably
261   take more memory than the datastructure takes now. */
262#define MAX_ADM9240_NR 4
263static struct i2c_client *adm9240_list[MAX_ADM9240_NR];
264
265/* The driver. I choose to use type i2c_driver, as at is identical to both
266   smbus_driver and isa_driver, and clients could be of either kind */
267static struct i2c_driver adm9240_driver = {
268  /* name */            "ADM9240 sensor driver",
269  /* id */              I2C_DRIVERID_ADM9240,
270  /* flags */           DF_NOTIFY,
271  /* attach_adapter */  &adm9240_attach_adapter,
272  /* detach_client */   &adm9240_detach_client,
273  /* command */         &adm9240_command,
274  /* inc_use */         &adm9240_inc_use,
275  /* dec_use */         &adm9240_dec_use
276};
277
278/* Used by adm9240_init/cleanup */
279static int adm9240_initialized = 0;
280
281/* The /proc/sys entries */
282/* These files are created for each detected ADM9240. This is just a template;
283   though at first sight, you might think we could use a statically
284   allocated list, we need some way to get back to the parent - which
285   is done through one of the 'extra' fields which are initialized
286   when a new copy is allocated. */
287static ctl_table adm9240_dir_table_template[] = {
288  { ADM9240_SYSCTL_IN0, "2.5V", NULL, 0, 0644, NULL, &sensors_proc_real,
289    &sensors_sysctl_real, NULL, &adm9240_in },
290  { ADM9240_SYSCTL_IN1, "Vccp1", NULL, 0, 0644, NULL, &sensors_proc_real,
291    &sensors_sysctl_real, NULL, &adm9240_in },
292  { ADM9240_SYSCTL_IN2, "3.3V", NULL, 0, 0644, NULL, &sensors_proc_real,
293    &sensors_sysctl_real, NULL, &adm9240_in },
294  { ADM9240_SYSCTL_IN3, "5V", NULL, 0, 0644, NULL, &sensors_proc_real,
295    &sensors_sysctl_real, NULL, &adm9240_in },
296  { ADM9240_SYSCTL_IN4, "12V", NULL, 0, 0644, NULL, &sensors_proc_real,
297    &sensors_sysctl_real, NULL, &adm9240_in },
298  { ADM9240_SYSCTL_IN5, "Vccp2", NULL, 0, 0644, NULL, &sensors_proc_real,
299    &sensors_sysctl_real, NULL, &adm9240_in },
300  { ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
301    &sensors_sysctl_real, NULL, &adm9240_fan },
302  { ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
303    &sensors_sysctl_real, NULL, &adm9240_fan },
304  { ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
305    &sensors_sysctl_real, NULL, &adm9240_temp },
306  { ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
307    &sensors_sysctl_real, NULL, &adm9240_fan_div },
308  { ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &sensors_proc_real,
309    &sensors_sysctl_real, NULL, &adm9240_alarms },
310  { ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
311    &sensors_sysctl_real, NULL, &adm9240_analog_out },
312  { ADM9240_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &sensors_proc_real,
313    &sensors_sysctl_real, NULL, &adm9240_vid },
314  { 0 }
315};
316
317
318int adm9240_attach_adapter(struct i2c_adapter *adapter)
319{
320  int address,err,temp;
321  struct i2c_client *new_client;
322  const char *type_name,*client_name;
323
324  err = 0;
325  /* Make sure we aren't probing the ISA bus!! */
326  if (i2c_is_isa_adapter(adapter)) return 0;
327 
328  /* The address of the ADM9240 must at least start somewhere in
329     0x2C to 0x2F, but can be changed to be anyelse after that.
330     (But, why??) */
331  for (address = 0x2C; (! err) && (address <= 0x2f); address ++) {
332
333    /* Later on, we will keep a list of registered addresses for each
334       adapter, and check whether they are used here */
335
336    temp = smbus_read_byte_data(adapter,address,ADM9240_REG_COMPANY_ID);
337    if (temp == 0x23) {
338      type_name = "adm9240";
339      client_name = "ADM9240 chip";
340    } else if (temp == 0xDA) { 
341      type_name = "ds1780";
342      client_name = "DS1780 chip";
343    } else     
344      continue;
345    temp=smbus_read_byte_data(adapter,address,ADM9240_REG_DIE_REV);
346    printk("adm9240.o: %s detected with die rev.: 0x%X\n",
347            client_name, temp);
348
349
350
351    /* Allocate space for a new client structure. To counter memory
352       fragmentation somewhat, we only do one kmalloc. */
353    if (! (new_client = kmalloc(sizeof(struct i2c_client) + 
354                                sizeof(struct adm9240_data),
355                               GFP_KERNEL))) {
356      err = -ENOMEM;
357      continue;
358    }
359
360    /* Fill the new client structure with data */
361    new_client->data = (struct adm9240_data *) (new_client + 1);
362    new_client->addr = address;
363    strcpy(new_client->name,client_name);
364    if ((err = adm9240_new_client(adapter,new_client)))
365      goto ERROR2;
366
367    /* Tell i2c-core a new client has arrived */
368    if ((err = i2c_attach_client(new_client))) 
369      goto ERROR3;
370
371    /* Register a new directory entry with module sensors */
372    if ((err = sensors_register_entry(new_client,type_name,
373                                      adm9240_dir_table_template)) < 0)
374      goto ERROR4;
375    ((struct adm9240_data *) (new_client->data))->sysctl_id = err;
376    err = 0;
377
378    /* Initialize the ADM9240 chip */
379    adm9240_init_client(new_client);
380    continue;
381
382/* OK, this is not exactly good programming practice, usually. But it is
383   very code-efficient in this case. */
384ERROR4:
385    i2c_detach_client(new_client);
386ERROR3:
387    adm9240_remove_client((struct i2c_client *) new_client);
388ERROR2:
389    kfree(new_client);
390  }
391  return err;
392}
393
394int adm9240_detach_client(struct i2c_client *client)
395{
396  int err,i;
397  for (i = 0; i < MAX_ADM9240_NR; i++)
398    if (client == adm9240_list[i])
399      break;
400  if ((i == MAX_ADM9240_NR)) {
401    printk("adm9240.o: Client to detach not found.\n");
402    return -ENOENT;
403  }
404
405  sensors_deregister_entry(((struct adm9240_data *)(client->data))->sysctl_id);
406
407  if ((err = i2c_detach_client(client))) {
408    printk("adm9240.o: Client deregistration failed, client not detached.\n");
409    return err;
410  }
411  adm9240_remove_client(client);
412  kfree(client);
413  return 0;
414}
415
416
417/* Find a free slot, and initialize most of the fields */
418int adm9240_new_client(struct i2c_adapter *adapter,
419                    struct i2c_client *new_client)
420{
421  int i;
422  struct adm9240_data *data;
423
424  /* First, seek out an empty slot */
425  for(i = 0; i < MAX_ADM9240_NR; i++)
426    if (! adm9240_list[i])
427      break;
428  if (i == MAX_ADM9240_NR) {
429    printk("adm9240.o: No empty slots left, recompile and heighten "
430           "MAX_ADM9240_NR!\n");
431    return -ENOMEM;
432  }
433 
434  adm9240_list[i] = new_client;
435  new_client->id = i;
436  new_client->adapter = adapter;
437  new_client->driver = &adm9240_driver;
438  data = new_client->data;
439  data->valid = 0;
440  data->update_lock = MUTEX;
441  return 0;
442}
443
444/* Inverse of adm9240_new_client */
445void adm9240_remove_client(struct i2c_client *client)
446{
447  int i;
448  for (i = 0; i < MAX_ADM9240_NR; i++)
449    if (client == adm9240_list[i]) 
450      adm9240_list[i] = NULL;
451}
452
453/* No commands defined yet */
454int adm9240_command(struct i2c_client *client, unsigned int cmd, void *arg)
455{
456  return 0;
457}
458
459void adm9240_inc_use (struct i2c_client *client)
460{
461#ifdef MODULE
462  MOD_INC_USE_COUNT;
463#endif
464}
465
466void adm9240_dec_use (struct i2c_client *client)
467{
468#ifdef MODULE
469  MOD_DEC_USE_COUNT;
470#endif
471}
472 
473
474long adm9240_temp_from_reg(u16 temp)
475{
476  if (temp < 256)
477   return (((temp & 0x1fe) >> 1) * 10) + ((temp & 1) * 5);
478  else
479   return ((((temp & 0x01fe) >> 1) - 255) * 10) - ((temp & 1) * 5);
480}
481
482int adm9240_read_value(struct i2c_client *client, u8 reg)
483{
484  return 0xFF & smbus_read_byte_data(client->adapter,client->addr, reg);
485}
486
487int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value)
488{
489  return smbus_write_byte_data(client->adapter, client->addr, reg,value);
490}
491
492/* Called when we have found a new ADM9240. It should set limits, etc. */
493void adm9240_init_client(struct i2c_client *client)
494{
495  /* Reset all except Watchdog values and last conversion values
496     This sets fan-divs to 2, among others. This makes most other
497     initializations unnecessary */
498  adm9240_write_value(client,ADM9240_REG_CONFIG,0x80);
499
500  adm9240_write_value(client,ADM9240_REG_IN_MIN(0),IN_TO_REG(ADM9240_INIT_IN_MIN_0,0));
501  adm9240_write_value(client,ADM9240_REG_IN_MAX(0),IN_TO_REG(ADM9240_INIT_IN_MAX_0,0));
502  adm9240_write_value(client,ADM9240_REG_IN_MIN(1),IN_TO_REG(ADM9240_INIT_IN_MIN_1,1));
503  adm9240_write_value(client,ADM9240_REG_IN_MAX(1),IN_TO_REG(ADM9240_INIT_IN_MAX_1,1));
504  adm9240_write_value(client,ADM9240_REG_IN_MIN(2),IN_TO_REG(ADM9240_INIT_IN_MIN_2,2));
505  adm9240_write_value(client,ADM9240_REG_IN_MAX(2),IN_TO_REG(ADM9240_INIT_IN_MAX_2,2));
506  adm9240_write_value(client,ADM9240_REG_IN_MIN(3),IN_TO_REG(ADM9240_INIT_IN_MIN_3,3));
507  adm9240_write_value(client,ADM9240_REG_IN_MAX(3),IN_TO_REG(ADM9240_INIT_IN_MAX_3,3));
508  adm9240_write_value(client,ADM9240_REG_IN_MIN(4),IN_TO_REG(ADM9240_INIT_IN_MIN_4,4));
509  adm9240_write_value(client,ADM9240_REG_IN_MAX(4),IN_TO_REG(ADM9240_INIT_IN_MAX_4,4));
510  adm9240_write_value(client,ADM9240_REG_IN_MIN(5),IN_TO_REG(ADM9240_INIT_IN_MIN_5,5));
511  adm9240_write_value(client,ADM9240_REG_IN_MAX(5),IN_TO_REG(ADM9240_INIT_IN_MAX_5,5));
512  adm9240_write_value(client,ADM9240_REG_FAN1_MIN,FAN_TO_REG(ADM9240_INIT_FAN_MIN_1,2));
513  adm9240_write_value(client,ADM9240_REG_FAN2_MIN,FAN_TO_REG(ADM9240_INIT_FAN_MIN_2,2));
514  adm9240_write_value(client,ADM9240_REG_TOS,
515                   TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_MAX));
516  adm9240_write_value(client,ADM9240_REG_THYST,
517                   TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_HYST));
518  adm9240_write_value(client,ADM9240_REG_TEMP_CONFIG,0x00);
519
520  /* Start monitoring */
521  adm9240_write_value(client,ADM9240_REG_CONFIG,0x01);
522}
523
524void adm9240_update_client(struct i2c_client *client)
525{
526  struct adm9240_data *data = client->data;
527  u8 i;
528
529  down(&data->update_lock);
530
531  if ((jiffies - data->last_updated > 2*HZ ) ||
532      (jiffies < data->last_updated) || ! data->valid) {
533
534#ifdef DEBUG
535    printk("Starting adm9240 update\n");
536#endif
537    for (i = 0; i <= 5; i++) {
538      data->in[i]     = adm9240_read_value(client,ADM9240_REG_IN(i));
539      data->in_min[i] = adm9240_read_value(client,ADM9240_REG_IN_MIN(i));
540      data->in_max[i] = adm9240_read_value(client,ADM9240_REG_IN_MAX(i));
541    }
542    data->fan[0] = adm9240_read_value(client,ADM9240_REG_FAN1);
543    data->fan_min[0] = adm9240_read_value(client,ADM9240_REG_FAN1_MIN);
544    data->fan[1] = adm9240_read_value(client,ADM9240_REG_FAN2);
545    data->fan_min[1] = adm9240_read_value(client,ADM9240_REG_FAN2_MIN);
546    data->temp = (adm9240_read_value(client,ADM9240_REG_TEMP) << 1) +
547                 ((adm9240_read_value(client,ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7);
548    data->temp_os_max = adm9240_read_value(client,ADM9240_REG_TOS);
549    data->temp_os_hyst = adm9240_read_value(client,ADM9240_REG_THYST);
550
551    i = adm9240_read_value(client,ADM9240_REG_VID_FAN_DIV);
552    data->fan_div[0] = (i >> 4) & 0x03;
553    data->fan_div[1] = (i >> 6) & 0x03;
554    data->vid = i & 0x0f;
555    data->vid |= (adm9240_read_value(client,ADM9240_REG_VID4) & 0x01) << 4;
556
557    data->alarms = adm9240_read_value(client,ADM9240_REG_INT1_STAT) +
558                   (adm9240_read_value(client,ADM9240_REG_INT2_STAT) << 8);
559    data->analog_out = adm9240_read_value(client,ADM9240_REG_ANALOG_OUT);
560    data->last_updated = jiffies;
561    data->valid = 1;
562  }
563
564  up(&data->update_lock);
565}
566
567
568/* The next few functions are the call-back functions of the /proc/sys and
569   sysctl files. Which function is used is defined in the ctl_table in
570   the extra1 field.
571   Each function must return the magnitude (power of 10 to divide the date
572   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
573   put a maximum of *nrels elements in results reflecting the data of this
574   file, and set *nrels to the number it actually put in it, if operation==
575   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
576   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
577   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
578   large enough (by checking the incoming value of *nrels). This is not very
579   good practice, but as long as you put less than about 5 values in results,
580   you can assume it is large enough. */
581void adm9240_in(struct i2c_client *client, int operation, int ctl_name, 
582             int *nrels_mag, long *results)
583{
584
585 int scales[6]={250, 270, 330, 500, 1200, 270};
586
587  struct adm9240_data *data = client->data;
588  int nr = ctl_name - ADM9240_SYSCTL_IN0;
589
590  if (operation == SENSORS_PROC_REAL_INFO)
591    *nrels_mag = 2;
592  else if (operation == SENSORS_PROC_REAL_READ) {
593    adm9240_update_client(client);
594    results[0] = IN_FROM_REG(data->in_min[nr],nr) * scales[nr] / 192;
595    results[1] = IN_FROM_REG(data->in_max[nr],nr) * scales[nr] / 192;
596    results[2] = IN_FROM_REG(data->in[nr],nr) * scales[nr] / 192;
597    *nrels_mag = 3;
598  } else if (operation == SENSORS_PROC_REAL_WRITE) {
599      if (*nrels_mag >= 1) {
600        data->in_min[nr] = IN_TO_REG((results[0]*192)/scales[nr],nr);
601        adm9240_write_value(client,ADM9240_REG_IN_MIN(nr),data->in_min[nr]);
602      }
603      if (*nrels_mag >= 2) {
604        data->in_max[nr] = IN_TO_REG((results[1]*192)/scales[nr],nr);
605        adm9240_write_value(client,ADM9240_REG_IN_MAX(nr),data->in_max[nr]);
606      }
607  }
608}
609
610void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
611              int *nrels_mag, long *results)
612{
613  struct adm9240_data *data = client->data;
614  int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1;
615
616  if (operation == SENSORS_PROC_REAL_INFO)
617    *nrels_mag = 0;
618  else if (operation == SENSORS_PROC_REAL_READ) {
619    adm9240_update_client(client);
620    results[0] = FAN_FROM_REG(data->fan_min[nr-1],
621                              DIV_FROM_REG(data->fan_div[nr-1]));
622    results[1] = FAN_FROM_REG(data->fan[nr-1],
623                              DIV_FROM_REG(data->fan_div[nr-1]));
624    *nrels_mag = 2;
625  } else if (operation == SENSORS_PROC_REAL_WRITE) {
626    if (*nrels_mag >= 1) {
627      data->fan_min[nr-1] = FAN_TO_REG(results[0],
628                            DIV_FROM_REG(data->fan_div[nr-1]));
629      adm9240_write_value(client,nr==1?ADM9240_REG_FAN1_MIN:ADM9240_REG_FAN2_MIN,
630                       data->fan_min[nr-1]);
631    }
632  }
633}
634
635
636void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
637               int *nrels_mag, long *results)
638{
639  struct adm9240_data *data = client->data;
640  if (operation == SENSORS_PROC_REAL_INFO)
641    *nrels_mag = 1;
642  else if (operation == SENSORS_PROC_REAL_READ) {
643    adm9240_update_client(client);
644    results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
645    results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
646    results[2] = TEMP_FROM_REG(data->temp);
647    *nrels_mag = 3;
648  } else if (operation == SENSORS_PROC_REAL_WRITE) {
649    if (*nrels_mag >= 1) {
650      data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]);
651      adm9240_write_value(client,ADM9240_REG_TOS,data->temp_os_max);
652    }
653    if (*nrels_mag >= 2) {
654      data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
655      adm9240_write_value(client,ADM9240_REG_THYST,data->temp_os_hyst);
656    }
657  }
658}
659
660void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
661                 int *nrels_mag, long *results)
662{
663  struct adm9240_data *data = client->data;
664  if (operation == SENSORS_PROC_REAL_INFO)
665    *nrels_mag = 0;
666  else if (operation == SENSORS_PROC_REAL_READ) {
667    adm9240_update_client(client);
668    results[0] = ALARMS_FROM_REG(data->alarms);
669    *nrels_mag = 1;
670  }
671}
672
673void adm9240_fan_div(struct i2c_client *client, int operation, int ctl_name,
674                  int *nrels_mag, long *results)
675{
676  struct adm9240_data *data = client->data;
677  int old;
678
679  if (operation == SENSORS_PROC_REAL_INFO)
680    *nrels_mag = 0;
681  else if (operation == SENSORS_PROC_REAL_READ) {
682    adm9240_update_client(client);
683    results[0] = DIV_FROM_REG(data->fan_div[0]);
684    results[1] = DIV_FROM_REG(data->fan_div[1]);
685    *nrels_mag = 2;
686  } else if (operation == SENSORS_PROC_REAL_WRITE) {
687    old = adm9240_read_value(client,ADM9240_REG_VID_FAN_DIV);
688    if (*nrels_mag >= 2) {
689      data->fan_div[1] = DIV_TO_REG(results[1]);
690      old = (old & 0xcf) | (data->fan_div[1] << 6);
691    }
692    if (*nrels_mag >= 1) {
693      data->fan_div[0] = DIV_TO_REG(results[0]);
694      old = (old & 0x3f) | (data->fan_div[0] << 4);
695      adm9240_write_value(client,ADM9240_REG_VID_FAN_DIV,old);
696    }
697  }
698}
699
700void adm9240_analog_out(struct i2c_client *client, int operation, int ctl_name,
701                  int *nrels_mag, long *results)
702{
703  struct adm9240_data *data = client->data;
704
705  if (operation == SENSORS_PROC_REAL_INFO)
706    *nrels_mag = 0;
707  else if (operation == SENSORS_PROC_REAL_READ) {
708    adm9240_update_client(client);
709    results[0] = data->analog_out;
710    *nrels_mag = 1;
711  } else if (operation == SENSORS_PROC_REAL_WRITE) {
712    if (*nrels_mag >= 1) {
713      data->analog_out = results[0];
714      adm9240_write_value(client,ADM9240_REG_ANALOG_OUT,data->analog_out);
715    }
716  }
717}
718
719void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
720              int *nrels_mag, long *results)
721{
722  struct adm9240_data *data = client->data;
723 
724  if (operation == SENSORS_PROC_REAL_INFO)
725    *nrels_mag = 2;
726  else if (operation == SENSORS_PROC_REAL_READ) {
727    adm9240_update_client(client);
728    results[0] = VID_FROM_REG(data->vid);
729    *nrels_mag = 1;
730  }
731}
732
733int adm9240_init(void)
734{
735  int res;
736
737  printk("adm9240.o version %s (%s)\n",LM_VERSION,LM_DATE);
738  adm9240_initialized = 0;
739
740  if ((res =i2c_add_driver(&adm9240_driver))) {
741    printk("adm9240.o: Driver registration failed, module not inserted.\n");
742    adm9240_cleanup();
743    return res;
744  }
745  adm9240_initialized ++;
746  return 0;
747}
748
749int adm9240_cleanup(void)
750{
751  int res;
752
753  if (adm9240_initialized >= 1) {
754    if ((res = i2c_del_driver(&adm9240_driver))) {
755      printk("adm9240.o: Driver deregistration failed, module not removed.\n");
756      return res;
757    }
758    adm9240_initialized --;
759  }
760  return 0;
761}
762
763
764#ifdef MODULE
765
766MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
767MODULE_DESCRIPTION("ADM9240 driver");
768
769int init_module(void)
770{
771  return adm9240_init();
772}
773
774int cleanup_module(void)
775{
776  return adm9240_cleanup();
777}
778
779#endif /* MODULE */
780
Note: See TracBrowser for help on using the browser.