root/lm-sensors/trunk/src/adm9240.c @ 154

Revision 154, 26.0 KB (checked in by phil, 14 years ago)

(Phil) Added driver for ADM9240. It isn't full tested, but it is
mostly there and working.

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