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

Revision 173, 26.9 KB (checked in by phil, 14 years ago)

(Phil) Added adm9240 and adm1021 entries into the lib and added VID
output to ADM9240. Status/alarms values need to be implemented instead
of a raw output, but I'll do that too.

  • 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 ADM2940:
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 "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  /* Should always read 0x23 */
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
119#define FAN_TO_REG(val,div) ((val)==0?255:\
120                             ((1350000+(val)*(div)/2)/((val)*(div))) & 0xff)
121#define FAN_FROM_REG(val,div) ((val)==0?-1:\
122                               (val)==255?0:1350000/((div)*(val)))
123
124#define TEMP_FROM_REG(val) adm9240_temp_from_reg(val)
125
126#define TEMP_LIMIT_TO_REG(val) (((val)<0?(((val)-50)/100)&0xff:\
127                                           ((val)+50)/100) & 0xff)
128#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100)
129
130#define ALARMS_FROM_REG(val) (val)
131
132#define DIV_FROM_REG(val) (1 << val)
133#define DIV_TO_REG(val) (val==1?0:(val==2?1:(val==4?2:3)))
134
135#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
136                           (val)>=0x06?0:205-(val)*5)
137
138/* Initial limits */
139#define ADM9240_INIT_IN_0 190
140#define ADM9240_INIT_IN_1 190
141#define ADM9240_INIT_IN_2 190
142#define ADM9240_INIT_IN_3 190
143#define ADM9240_INIT_IN_4 190
144#define ADM9240_INIT_IN_5 190
145
146#define ADM9240_INIT_IN_PERCENTAGE 10
147
148#define ADM9240_INIT_IN_MIN_0 \
149        (ADM9240_INIT_IN_0 - ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
150#define ADM9240_INIT_IN_MAX_0 \
151        (ADM9240_INIT_IN_0 + ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
152#define ADM9240_INIT_IN_MIN_1 \
153        (ADM9240_INIT_IN_1 - ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
154#define ADM9240_INIT_IN_MAX_1 \
155        (ADM9240_INIT_IN_1 + ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
156#define ADM9240_INIT_IN_MIN_2 \
157        (ADM9240_INIT_IN_2 - ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
158#define ADM9240_INIT_IN_MAX_2 \
159        (ADM9240_INIT_IN_2 + ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
160#define ADM9240_INIT_IN_MIN_3 \
161        (ADM9240_INIT_IN_3 - ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
162#define ADM9240_INIT_IN_MAX_3 \
163        (ADM9240_INIT_IN_3 + ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
164#define ADM9240_INIT_IN_MIN_4 \
165        (ADM9240_INIT_IN_4 - ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
166#define ADM9240_INIT_IN_MAX_4 \
167        (ADM9240_INIT_IN_4 + ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
168#define ADM9240_INIT_IN_MIN_5 \
169        (ADM9240_INIT_IN_5 - ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
170#define ADM9240_INIT_IN_MAX_5 \
171        (ADM9240_INIT_IN_5 + ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
172
173#define ADM9240_INIT_FAN_MIN_1 3000
174#define ADM9240_INIT_FAN_MIN_2 3000
175
176#define ADM9240_INIT_TEMP_OS_MAX 600
177#define ADM9240_INIT_TEMP_OS_HYST 500
178#define ADM9240_INIT_TEMP_HOT_MAX 700
179#define ADM9240_INIT_TEMP_HOT_HYST 600
180
181#ifdef MODULE
182extern int init_module(void);
183extern int cleanup_module(void);
184#endif /* MODULE */
185
186/* For each registered ADM9240, we need to keep some data in memory. That
187   data is pointed to by adm9240_list[NR]->data. The structure itself is
188   dynamically allocated, at the same time when a new adm9240 client is
189   allocated. */
190struct adm9240_data {
191         int sysctl_id;
192
193         struct semaphore update_lock;
194         char valid;                 /* !=0 if following fields are valid */
195         unsigned long last_updated; /* In jiffies */
196
197         u8 in[6];                   /* Register value */
198         u8 in_max[6];               /* Register value */
199         u8 in_min[6];               /* Register value */
200         u8 fan[2];                  /* Register value */
201         u8 fan_min[2];              /* Register value */
202         u8 fan_div[2];              /* Register encoding, shifted right */
203         int temp;                   /* Temp, shifted right */
204         u8 temp_os_max;             /* Register value */
205         u8 temp_os_hyst;            /* Register value */
206         u16 alarms;                 /* Register encoding, combined */
207         u8 analog_out;              /* Register value */
208         u8 vid;                     /* Register value combined */
209};
210
211
212static int adm9240_init(void);
213static int adm9240_cleanup(void);
214
215static int adm9240_attach_adapter(struct i2c_adapter *adapter);
216static int adm9240_detach_client(struct i2c_client *client);
217static int adm9240_new_client(struct i2c_adapter *adapter,
218                           struct i2c_client *new_client);
219static void adm9240_remove_client(struct i2c_client *client);
220static int adm9240_command(struct i2c_client *client, unsigned int cmd, 
221                        void *arg);
222static void adm9240_inc_use (struct i2c_client *client);
223static void adm9240_dec_use (struct i2c_client *client);
224
225static long adm9240_temp_from_reg(u16 regs);
226
227static int adm9240_read_value(struct i2c_client *client, u8 register);
228static int adm9240_write_value(struct i2c_client *client, u8 register, u8 value);
229static void adm9240_update_client(struct i2c_client *client);
230static void adm9240_init_client(struct i2c_client *client);
231
232
233static void adm9240_in(struct i2c_client *client, int operation, int ctl_name,
234                    int *nrels_mag, long *results);
235static void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
236                     int *nrels_mag, long *results);
237static void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
238                      int *nrels_mag, long *results);
239static void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
240                        int *nrels_mag, long *results);
241static void adm9240_fan_div(struct i2c_client *client, int operation, int ctl_name,
242                         int *nrels_mag, long *results);
243static void adm9240_analog_out(struct i2c_client *client, int operation, int ctl_name,
244                         int *nrels_mag, long *results);
245static void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
246                         int *nrels_mag, long *results);
247
248/* I choose here for semi-static ADM9240 allocation. Complete dynamic
249   allocation could also be used; the code needed for this would probably
250   take more memory than the datastructure takes now. */
251#define MAX_ADM9240_NR 4
252static struct i2c_client *adm9240_list[MAX_ADM9240_NR];
253
254/* The driver. I choose to use type i2c_driver, as at is identical to both
255   smbus_driver and isa_driver, and clients could be of either kind */
256static struct i2c_driver adm9240_driver = {
257  /* name */            "ADM9240 sensor driver",
258  /* id */              I2C_DRIVERID_ADM9240,
259  /* flags */           DF_NOTIFY,
260  /* attach_adapter */  &adm9240_attach_adapter,
261  /* detach_client */   &adm9240_detach_client,
262  /* command */         &adm9240_command,
263  /* inc_use */         &adm9240_inc_use,
264  /* dec_use */         &adm9240_dec_use
265};
266
267/* Used by adm9240_init/cleanup */
268static int adm9240_initialized = 0;
269
270/* The /proc/sys entries */
271/* These files are created for each detected ADM9240. This is just a template;
272   though at first sight, you might think we could use a statically
273   allocated list, we need some way to get back to the parent - which
274   is done through one of the 'extra' fields which are initialized
275   when a new copy is allocated. */
276static ctl_table adm9240_dir_table_template[] = {
277  { ADM9240_SYSCTL_IN0, "2.5V", NULL, 0, 0644, NULL, &sensors_proc_real,
278    &sensors_sysctl_real, NULL, &adm9240_in },
279  { ADM9240_SYSCTL_IN1, "Vccp1", NULL, 0, 0644, NULL, &sensors_proc_real,
280    &sensors_sysctl_real, NULL, &adm9240_in },
281  { ADM9240_SYSCTL_IN2, "3.3V", NULL, 0, 0644, NULL, &sensors_proc_real,
282    &sensors_sysctl_real, NULL, &adm9240_in },
283  { ADM9240_SYSCTL_IN3, "5V", NULL, 0, 0644, NULL, &sensors_proc_real,
284    &sensors_sysctl_real, NULL, &adm9240_in },
285  { ADM9240_SYSCTL_IN4, "12V", NULL, 0, 0644, NULL, &sensors_proc_real,
286    &sensors_sysctl_real, NULL, &adm9240_in },
287  { ADM9240_SYSCTL_IN5, "Vccp2", NULL, 0, 0644, NULL, &sensors_proc_real,
288    &sensors_sysctl_real, NULL, &adm9240_in },
289  { ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
290    &sensors_sysctl_real, NULL, &adm9240_fan },
291  { ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
292    &sensors_sysctl_real, NULL, &adm9240_fan },
293  { ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
294    &sensors_sysctl_real, NULL, &adm9240_temp },
295  { ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
296    &sensors_sysctl_real, NULL, &adm9240_fan_div },
297  { ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
298    &sensors_sysctl_real, NULL, &adm9240_alarms },
299  { ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
300    &sensors_sysctl_real, NULL, &adm9240_analog_out },
301  { ADM9240_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &sensors_proc_real,
302    &sensors_sysctl_real, NULL, &adm9240_vid },
303  { 0 }
304};
305
306
307int adm9240_attach_adapter(struct i2c_adapter *adapter)
308{
309  int address,err,temp;
310  struct i2c_client *new_client;
311  const char *type_name,*client_name;
312
313  err = 0;
314  /* The address of the ADM2940 must at least start somewhere in
315     0x2C to 0x2F, but can be changed to be anyelse after that.
316     (But, why??) */
317  for (address = 0x2C; (! err) && (address <= 0x2f); address ++) {
318
319    /* Later on, we will keep a list of registered addresses for each
320       adapter, and check whether they are used here */
321
322    if (smbus_read_byte_data(adapter,address,ADM9240_REG_COMPANY_ID) != 0x23) 
323      continue;
324
325    temp=smbus_read_byte_data(adapter,address,ADM9240_REG_DIE_REV);
326    printk("adm9240.o: ADM9240 detected with die rev.: 0x%X\n",temp);
327    type_name = "adm9240";
328    client_name = "ADM9240 chip";
329
330
331    /* Allocate space for a new client structure. To counter memory
332       fragmentation somewhat, we only do one kmalloc. */
333    if (! (new_client = kmalloc(sizeof(struct i2c_client) + 
334                                sizeof(struct adm9240_data),
335                               GFP_KERNEL))) {
336      err = -ENOMEM;
337      continue;
338    }
339
340    /* Fill the new client structure with data */
341    new_client->data = (struct adm9240_data *) (new_client + 1);
342    new_client->addr = address;
343    strcpy(new_client->name,client_name);
344    if ((err = adm9240_new_client(adapter,new_client)))
345      goto ERROR2;
346
347    /* Tell i2c-core a new client has arrived */
348    if ((err = i2c_attach_client(new_client))) 
349      goto ERROR3;
350
351    /* Register a new directory entry with module sensors */
352    if ((err = sensors_register_entry(new_client,type_name,
353                                      adm9240_dir_table_template)) < 0)
354      goto ERROR4;
355    ((struct adm9240_data *) (new_client->data))->sysctl_id = err;
356    err = 0;
357
358    /* Initialize the ADM9240 chip */
359    adm9240_init_client(new_client);
360    continue;
361
362/* OK, this is not exactly good programming practice, usually. But it is
363   very code-efficient in this case. */
364ERROR4:
365    i2c_detach_client(new_client);
366ERROR3:
367    adm9240_remove_client((struct i2c_client *) new_client);
368ERROR2:
369    kfree(new_client);
370  }
371  return err;
372}
373
374int adm9240_detach_client(struct i2c_client *client)
375{
376  int err,i;
377  for (i = 0; i < MAX_ADM9240_NR; i++)
378    if (client == adm9240_list[i])
379      break;
380  if ((i == MAX_ADM9240_NR)) {
381    printk("adm9240.o: Client to detach not found.\n");
382    return -ENOENT;
383  }
384
385  sensors_deregister_entry(((struct adm9240_data *)(client->data))->sysctl_id);
386
387  if ((err = i2c_detach_client(client))) {
388    printk("adm9240.o: Client deregistration failed, client not detached.\n");
389    return err;
390  }
391  adm9240_remove_client(client);
392  kfree(client);
393  return 0;
394}
395
396
397/* Find a free slot, and initialize most of the fields */
398int adm9240_new_client(struct i2c_adapter *adapter,
399                    struct i2c_client *new_client)
400{
401  int i;
402  struct adm9240_data *data;
403
404  /* First, seek out an empty slot */
405  for(i = 0; i < MAX_ADM9240_NR; i++)
406    if (! adm9240_list[i])
407      break;
408  if (i == MAX_ADM9240_NR) {
409    printk("adm9240.o: No empty slots left, recompile and heighten "
410           "MAX_ADM9240_NR!\n");
411    return -ENOMEM;
412  }
413 
414  adm9240_list[i] = new_client;
415  new_client->id = i;
416  new_client->adapter = adapter;
417  new_client->driver = &adm9240_driver;
418  data = new_client->data;
419  data->valid = 0;
420  data->update_lock = MUTEX;
421  return 0;
422}
423
424/* Inverse of adm9240_new_client */
425void adm9240_remove_client(struct i2c_client *client)
426{
427  int i;
428  for (i = 0; i < MAX_ADM9240_NR; i++)
429    if (client == adm9240_list[i]) 
430      adm9240_list[i] = NULL;
431}
432
433/* No commands defined yet */
434int adm9240_command(struct i2c_client *client, unsigned int cmd, void *arg)
435{
436  return 0;
437}
438
439void adm9240_inc_use (struct i2c_client *client)
440{
441#ifdef MODULE
442  MOD_INC_USE_COUNT;
443#endif
444}
445
446void adm9240_dec_use (struct i2c_client *client)
447{
448#ifdef MODULE
449  MOD_DEC_USE_COUNT;
450#endif
451}
452 
453
454long adm9240_temp_from_reg(u16 temp)
455{
456  if (temp < 256)
457   return (((temp & 0x1fe) >> 1) * 10) + ((temp & 1) * 5);
458  else
459   return ((((temp & 0x01fe) >> 1) - 255) * 10) - ((temp & 1) * 5);
460}
461
462int adm9240_read_value(struct i2c_client *client, u8 reg)
463{
464  return 0xFF & smbus_read_byte_data(client->adapter,client->addr, reg);
465}
466
467int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value)
468{
469  return smbus_write_byte_data(client->adapter, client->addr, reg,value);
470}
471
472/* Called when we have found a new ADM9240. It should set limits, etc. */
473void adm9240_init_client(struct i2c_client *client)
474{
475  /* Reset all except Watchdog values and last conversion values
476     This sets fan-divs to 2, among others. This makes most other
477     initializations unnecessary */
478  adm9240_write_value(client,ADM9240_REG_CONFIG,0x80);
479
480  adm9240_write_value(client,ADM9240_REG_IN_MIN(0),IN_TO_REG(ADM9240_INIT_IN_MIN_0,0));
481  adm9240_write_value(client,ADM9240_REG_IN_MAX(0),IN_TO_REG(ADM9240_INIT_IN_MAX_0,0));
482  adm9240_write_value(client,ADM9240_REG_IN_MIN(1),IN_TO_REG(ADM9240_INIT_IN_MIN_1,1));
483  adm9240_write_value(client,ADM9240_REG_IN_MAX(1),IN_TO_REG(ADM9240_INIT_IN_MAX_1,1));
484  adm9240_write_value(client,ADM9240_REG_IN_MIN(2),IN_TO_REG(ADM9240_INIT_IN_MIN_2,2));
485  adm9240_write_value(client,ADM9240_REG_IN_MAX(2),IN_TO_REG(ADM9240_INIT_IN_MAX_2,2));
486  adm9240_write_value(client,ADM9240_REG_IN_MIN(3),IN_TO_REG(ADM9240_INIT_IN_MIN_3,3));
487  adm9240_write_value(client,ADM9240_REG_IN_MAX(3),IN_TO_REG(ADM9240_INIT_IN_MAX_3,3));
488  adm9240_write_value(client,ADM9240_REG_IN_MIN(4),IN_TO_REG(ADM9240_INIT_IN_MIN_4,4));
489  adm9240_write_value(client,ADM9240_REG_IN_MAX(4),IN_TO_REG(ADM9240_INIT_IN_MAX_4,4));
490  adm9240_write_value(client,ADM9240_REG_IN_MIN(5),IN_TO_REG(ADM9240_INIT_IN_MIN_5,5));
491  adm9240_write_value(client,ADM9240_REG_IN_MAX(5),IN_TO_REG(ADM9240_INIT_IN_MAX_5,5));
492  adm9240_write_value(client,ADM9240_REG_FAN1_MIN,FAN_TO_REG(ADM9240_INIT_FAN_MIN_1,2));
493  adm9240_write_value(client,ADM9240_REG_FAN2_MIN,FAN_TO_REG(ADM9240_INIT_FAN_MIN_2,2));
494  adm9240_write_value(client,ADM9240_REG_TOS,
495                   TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_MAX));
496  adm9240_write_value(client,ADM9240_REG_THYST,
497                   TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_HYST));
498  adm9240_write_value(client,ADM9240_REG_TEMP_CONFIG,0x00);
499
500  /* Start monitoring */
501  adm9240_write_value(client,ADM9240_REG_CONFIG,0x0B);
502}
503
504void adm9240_update_client(struct i2c_client *client)
505{
506  struct adm9240_data *data = client->data;
507  u8 i;
508
509  down(&data->update_lock);
510
511  if ((jiffies - data->last_updated > 2*HZ ) ||
512      (jiffies < data->last_updated) || ! data->valid) {
513
514#ifdef DEBUG
515    printk("Starting adm9240 update\n");
516#endif
517    for (i = 0; i <= 5; i++) {
518      data->in[i]     = adm9240_read_value(client,ADM9240_REG_IN(i));
519      data->in_min[i] = adm9240_read_value(client,ADM9240_REG_IN_MIN(i));
520      data->in_max[i] = adm9240_read_value(client,ADM9240_REG_IN_MAX(i));
521    }
522    data->fan[0] = adm9240_read_value(client,ADM9240_REG_FAN1);
523    data->fan_min[0] = adm9240_read_value(client,ADM9240_REG_FAN1_MIN);
524    data->fan[1] = adm9240_read_value(client,ADM9240_REG_FAN2);
525    data->fan_min[1] = adm9240_read_value(client,ADM9240_REG_FAN2_MIN);
526    data->temp = (adm9240_read_value(client,ADM9240_REG_TEMP) << 1) +
527                 ((adm9240_read_value(client,ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7);
528    data->temp_os_max = adm9240_read_value(client,ADM9240_REG_TOS);
529    data->temp_os_hyst = adm9240_read_value(client,ADM9240_REG_THYST);
530
531    i = adm9240_read_value(client,ADM9240_REG_VID_FAN_DIV);
532    data->fan_div[0] = (i >> 4) & 0x03;
533    data->fan_div[1] = (i >> 6) & 0x03;
534    data->vid = i & 0x0f;
535    data->vid |= (adm9240_read_value(client,ADM9240_REG_VID4) & 0x01) << 4;
536
537    data->alarms = adm9240_read_value(client,ADM9240_REG_INT1_STAT) +
538                   (adm9240_read_value(client,ADM9240_REG_INT2_STAT) << 8);
539    data->analog_out = adm9240_read_value(client,ADM9240_REG_ANALOG_OUT);
540    data->last_updated = jiffies;
541    data->valid = 1;
542  }
543
544  up(&data->update_lock);
545}
546
547
548/* The next few functions are the call-back functions of the /proc/sys and
549   sysctl files. Which function is used is defined in the ctl_table in
550   the extra1 field.
551   Each function must return the magnitude (power of 10 to divide the date
552   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
553   put a maximum of *nrels elements in results reflecting the data of this
554   file, and set *nrels to the number it actually put in it, if operation==
555   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
556   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
557   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
558   large enough (by checking the incoming value of *nrels). This is not very
559   good practice, but as long as you put less than about 5 values in results,
560   you can assume it is large enough. */
561void adm9240_in(struct i2c_client *client, int operation, int ctl_name, 
562             int *nrels_mag, long *results)
563{
564  struct adm9240_data *data = client->data;
565  int nr = ctl_name - ADM9240_SYSCTL_IN0;
566
567  if (operation == SENSORS_PROC_REAL_INFO)
568    *nrels_mag = 2;
569  else if (operation == SENSORS_PROC_REAL_READ) {
570    adm9240_update_client(client);
571    results[0] = IN_FROM_REG(data->in_min[nr],nr);
572    results[1] = IN_FROM_REG(data->in_max[nr],nr);
573    results[2] = IN_FROM_REG(data->in[nr],nr);
574    *nrels_mag = 3;
575  } else if (operation == SENSORS_PROC_REAL_WRITE) {
576      if (*nrels_mag >= 1) {
577        data->in_min[nr] = IN_TO_REG(results[0],nr);
578        adm9240_write_value(client,ADM9240_REG_IN_MIN(nr),data->in_min[nr]);
579      }
580      if (*nrels_mag >= 2) {
581        data->in_max[nr] = IN_TO_REG(results[1],nr);
582        adm9240_write_value(client,ADM9240_REG_IN_MAX(nr),data->in_max[nr]);
583      }
584  }
585}
586
587void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
588              int *nrels_mag, long *results)
589{
590  struct adm9240_data *data = client->data;
591  int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1;
592
593  if (operation == SENSORS_PROC_REAL_INFO)
594    *nrels_mag = 0;
595  else if (operation == SENSORS_PROC_REAL_READ) {
596    adm9240_update_client(client);
597    results[0] = FAN_FROM_REG(data->fan_min[nr-1],
598                              DIV_FROM_REG(data->fan_div[nr-1]));
599    results[1] = FAN_FROM_REG(data->fan[nr-1],
600                              DIV_FROM_REG(data->fan_div[nr-1]));
601    *nrels_mag = 2;
602  } else if (operation == SENSORS_PROC_REAL_WRITE) {
603    if (*nrels_mag >= 1) {
604      data->fan_min[nr-1] = FAN_TO_REG(results[0],
605                            DIV_FROM_REG(data->fan_div[nr-1]));
606      adm9240_write_value(client,nr==1?ADM9240_REG_FAN1_MIN:ADM9240_REG_FAN2_MIN,
607                       data->fan_min[nr-1]);
608    }
609  }
610}
611
612
613void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
614               int *nrels_mag, long *results)
615{
616  struct adm9240_data *data = client->data;
617  if (operation == SENSORS_PROC_REAL_INFO)
618    *nrels_mag = 1;
619  else if (operation == SENSORS_PROC_REAL_READ) {
620    adm9240_update_client(client);
621    results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
622    results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
623    results[2] = TEMP_FROM_REG(data->temp);
624    *nrels_mag = 3;
625  } else if (operation == SENSORS_PROC_REAL_WRITE) {
626    if (*nrels_mag >= 1) {
627      data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]);
628      adm9240_write_value(client,ADM9240_REG_TOS,data->temp_os_max);
629    }
630    if (*nrels_mag >= 2) {
631      data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
632      adm9240_write_value(client,ADM9240_REG_THYST,data->temp_os_hyst);
633    }
634  }
635}
636
637void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
638                 int *nrels_mag, long *results)
639{
640  struct adm9240_data *data = client->data;
641  if (operation == SENSORS_PROC_REAL_INFO)
642    *nrels_mag = 0;
643  else if (operation == SENSORS_PROC_REAL_READ) {
644    adm9240_update_client(client);
645    results[0] = ALARMS_FROM_REG(data->alarms);
646    *nrels_mag = 1;
647  }
648}
649
650void adm9240_fan_div(struct i2c_client *client, int operation, int ctl_name,
651                  int *nrels_mag, long *results)
652{
653  struct adm9240_data *data = client->data;
654  int old;
655
656  if (operation == SENSORS_PROC_REAL_INFO)
657    *nrels_mag = 0;
658  else if (operation == SENSORS_PROC_REAL_READ) {
659    adm9240_update_client(client);
660    results[0] = DIV_FROM_REG(data->fan_div[0]);
661    results[1] = DIV_FROM_REG(data->fan_div[1]);
662    *nrels_mag = 2;
663  } else if (operation == SENSORS_PROC_REAL_WRITE) {
664    old = adm9240_read_value(client,ADM9240_REG_VID_FAN_DIV);
665    if (*nrels_mag >= 2) {
666      data->fan_div[1] = DIV_TO_REG(results[1]);
667      old = (old & 0xcf) | (data->fan_div[1] << 6);
668    }
669    if (*nrels_mag >= 1) {
670      data->fan_div[0] = DIV_TO_REG(results[0]);
671      old = (old & 0x3f) | (data->fan_div[0] << 4);
672      adm9240_write_value(client,ADM9240_REG_VID_FAN_DIV,old);
673    }
674  }
675}
676
677void adm9240_analog_out(struct i2c_client *client, int operation, int ctl_name,
678                  int *nrels_mag, long *results)
679{
680  struct adm9240_data *data = client->data;
681
682  if (operation == SENSORS_PROC_REAL_INFO)
683    *nrels_mag = 0;
684  else if (operation == SENSORS_PROC_REAL_READ) {
685    adm9240_update_client(client);
686    results[0] = data->analog_out;
687    *nrels_mag = 1;
688  } else if (operation == SENSORS_PROC_REAL_WRITE) {
689    if (*nrels_mag >= 1) {
690      data->analog_out = results[0];
691      adm9240_write_value(client,ADM9240_REG_ANALOG_OUT,data->analog_out);
692    }
693  }
694}
695
696void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
697              int *nrels_mag, long *results)
698{
699  struct adm9240_data *data = client->data;
700 
701  if (operation == SENSORS_PROC_REAL_INFO)
702    *nrels_mag = 2;
703  else if (operation == SENSORS_PROC_REAL_READ) {
704    adm9240_update_client(client);
705    results[0] = VID_FROM_REG(data->vid);
706    *nrels_mag = 1;
707  }
708}
709
710int adm9240_init(void)
711{
712  int res;
713
714  printk("adm9240.o version %s (%s)\n",LM_VERSION,LM_DATE);
715  adm9240_initialized = 0;
716
717  if ((res =i2c_add_driver(&adm9240_driver))) {
718    printk("adm9240.o: Driver registration failed, module not inserted.\n");
719    adm9240_cleanup();
720    return res;
721  }
722  adm9240_initialized ++;
723  return 0;
724}
725
726int adm9240_cleanup(void)
727{
728  int res;
729
730  if (adm9240_initialized >= 1) {
731    if ((res = i2c_del_driver(&adm9240_driver))) {
732      printk("adm9240.o: Driver deregistration failed, module not removed.\n");
733      return res;
734    }
735    adm9240_initialized --;
736  }
737  return 0;
738}
739
740
741#ifdef MODULE
742
743MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
744MODULE_DESCRIPTION("ADM9240 driver");
745
746int init_module(void)
747{
748  return adm9240_init();
749}
750
751int cleanup_module(void)
752{
753  return adm9240_cleanup();
754}
755
756#endif /* MODULE */
757
Note: See TracBrowser for help on using the browser.