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

Revision 492, 28.1 KB (checked in by frodo, 14 years ago)

Detection bug for ADM9240 solved

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