root/lm-sensors/trunk/kernel/chips/ds1621.c @ 935

Revision 935, 14.3 KB (checked in by mds, 12 years ago)

(mds) add ds1621 driver from Christian Zuckschwerdt (zany@…)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
3             monitoring
4    Christian W. Zuckschwerdt  <zany@triq.net>  2000-11-23
5    based on lm75.c by Frodo Looijaard <frodol@dds.nl>
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/* Supports DS1621. See doc/chips/ds1621 for details */
23
24#include <linux/version.h>
25#include <linux/module.h>
26#include <linux/malloc.h>
27#include <linux/i2c.h>
28#include "sensors.h"
29#include "i2c-isa.h"
30#include "version.h"
31#include <linux/init.h>
32
33
34#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
35    (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
36#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
37#endif
38
39#ifndef THIS_MODULE
40#define THIS_MODULE NULL
41#endif
42
43/* Addresses to scan */
44static unsigned short normal_i2c[] = { SENSORS_I2C_END };
45static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END };
46static unsigned int normal_isa[] = { SENSORS_ISA_END };
47static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
48
49/* Insmod parameters */
50SENSORS_INSMOD_1(ds1621);
51
52/* Many DS1621 constants specified below - most aren't used */
53
54/* Config register used for detection         */
55/*  7    6    5    4    3    2    1    0      */
56/* |Done|THF |TLF |NVB | 1  | 0  |POL |1SHOT| */
57#define DS1621_REG_CONFIG_MASK 0x0C
58#define DS1621_REG_CONFIG_VAL 0x08
59
60/* The DS1621 registers */
61#define DS1621_REG_TEMP 0xAA /* word, RO */
62#define DS1621_REG_TEMP_OVER 0xA1 /* word, RW */
63#define DS1621_REG_TEMP_HYST 0xA2 /* word, RW -- it's a low temp trigger */
64#define DS1621_REG_CONF 0xAC /* byte, RW */
65#define DS1621_REG_TEMP_COUNTER 0xA8 /* byte, RO */
66#define DS1621_REG_TEMP_SLOPE 0xA9 /* byte, RO */
67#define DS1621_COM_START 0xEE /* no data */
68#define DS1621_COM_STOP 0x22 /* no data */
69
70/* Conversions. Rounding and limit checking is only done on the TO_REG
71   variants. Note that you should be a bit careful with which arguments
72   these macros are called: arguments may be evaluated more than once.
73   Fixing this is just not worth it. */
74#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | \
75                            ((val & 0x8000)?-256:0))
76#define TEMP_TO_REG(val)   (SENSORS_LIMIT((val<0 ? (0x200+((val)/5))<<7 : \
77                                          (((val) + 2) / 5) << 7),0,0xffff))
78#define ALARMS_FROM_REG(val) ((val) & \
79                              (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
80
81/* Initial values */
82#define DS1621_INIT_TEMP_OVER 600
83#define DS1621_INIT_TEMP_HYST 0 /* 500 would cause an alarm at room temp. */
84
85/* Each client has this additional data */
86struct ds1621_data {
87        int sysctl_id;
88
89        struct semaphore update_lock;
90        char valid;             /* !=0 if following fields are valid */
91        unsigned long last_updated;     /* In jiffies */
92
93        u16 temp, temp_over, temp_hyst; /* Register values */
94        u8 conf;                        /* Register encoding, combined */
95};
96
97#ifdef MODULE
98extern int init_module(void);
99extern int cleanup_module(void);
100#endif                          /* MODULE */
101
102#ifdef MODULE
103static
104#else
105extern
106#endif
107int __init sensors_ds1621_init(void);
108static int __init ds1621_cleanup(void);
109static int ds1621_attach_adapter(struct i2c_adapter *adapter);
110static int ds1621_detect(struct i2c_adapter *adapter, int address,
111                         unsigned short flags, int kind);
112static void ds1621_init_client(struct i2c_client *client);
113static int ds1621_detach_client(struct i2c_client *client);
114static int ds1621_command(struct i2c_client *client, unsigned int cmd,
115                          void *arg);
116static void ds1621_inc_use(struct i2c_client *client);
117static void ds1621_dec_use(struct i2c_client *client);
118static u16 swap_bytes(u16 val);
119static int ds1621_read_value(struct i2c_client *client, u8 reg);
120static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value);
121static void ds1621_temp(struct i2c_client *client, int operation,
122                        int ctl_name, int *nrels_mag, long *results);
123static void ds1621_alarms(struct i2c_client *client, int operation,
124                          int ctl_name, int *nrels_mag, long *results);
125static void ds1621_enable(struct i2c_client *client, int operation,
126                          int ctl_name, int *nrels_mag, long *results);
127static void ds1621_update_client(struct i2c_client *client);
128
129
130/* This is the driver that will be inserted */
131static struct i2c_driver ds1621_driver = {
132        /* name */ "DS1621 sensor driver",
133        /* id */ I2C_DRIVERID_DS1621,
134        /* flags */ I2C_DF_NOTIFY,
135        /* attach_adapter */ &ds1621_attach_adapter,
136        /* detach_client */ &ds1621_detach_client,
137        /* command */ &ds1621_command,
138        /* inc_use */ &ds1621_inc_use,
139        /* dec_use */ &ds1621_dec_use
140};
141
142/* These files are created for each detected DS1621. This is just a template;
143   though at first sight, you might think we could use a statically
144   allocated list, we need some way to get back to the parent - which
145   is done through one of the 'extra' fields which are initialized
146   when a new copy is allocated. */
147static ctl_table ds1621_dir_table_template[] = {
148        {DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
149         &sensors_sysctl_real, NULL, &ds1621_temp},
150        {DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
151         &sensors_proc_real, &sensors_sysctl_real, NULL, &ds1621_alarms},
152        {DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL,
153         &sensors_proc_real, &sensors_sysctl_real, NULL, &ds1621_enable},
154        {0}
155};
156
157/* Used by init/cleanup */
158static int __initdata ds1621_initialized = 0;
159
160static int ds1621_id = 0;
161
162int ds1621_attach_adapter(struct i2c_adapter *adapter)
163{
164        return sensors_detect(adapter, &addr_data, ds1621_detect);
165}
166
167/* This function is called by sensors_detect */
168int ds1621_detect(struct i2c_adapter *adapter, int address,
169                unsigned short flags, int kind)
170{
171        int i, conf;
172        struct i2c_client *new_client;
173        struct ds1621_data *data;
174        int err = 0;
175        const char *type_name, *client_name;
176
177        /* Make sure we aren't probing the ISA bus!! This is just a safety check
178           at this moment; sensors_detect really won't call us. */
179#ifdef DEBUG
180        if (i2c_is_isa_adapter(adapter)) {
181                printk
182                 ("ds1621.o: ds1621_detect called for an ISA bus adapter?!?\n");
183                return 0;
184        }
185#endif
186
187        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
188                                     I2C_FUNC_SMBUS_WORD_DATA))
189                    goto ERROR0;
190
191        /* OK. For now, we presume we have a valid client. We now create the
192           client structure, even though we cannot fill it completely yet.
193           But it allows us to access ds1621_{read,write}_value. */
194        if (!(new_client = kmalloc(sizeof(struct i2c_client) +
195                                   sizeof(struct ds1621_data),
196                                   GFP_KERNEL))) {
197                err = -ENOMEM;
198                goto ERROR0;
199        }
200
201        data = (struct ds1621_data *) (new_client + 1);
202        new_client->addr = address;
203        new_client->data = data;
204        new_client->adapter = adapter;
205        new_client->driver = &ds1621_driver;
206        new_client->flags = 0;
207
208        /* Now, we do the remaining detection. It is lousy. */
209        if (kind < 0) {
210                conf = i2c_smbus_read_byte_data(new_client,
211                                                DS1621_REG_CONF);
212                if ((conf & DS1621_REG_CONFIG_MASK)
213                    != DS1621_REG_CONFIG_VAL)
214                        goto ERROR1;
215        }
216
217        /* Determine the chip type - only one kind supported! */
218        if (kind <= 0)
219                kind = ds1621;
220
221        if (kind == ds1621) {
222                type_name = "ds1621";
223                client_name = "DS1621 chip";
224        } else {
225#ifdef DEBUG
226                printk("ds1621.o: Internal error: unknown kind (%d)?!?",
227                       kind);
228#endif
229                goto ERROR1;
230        }
231
232        /* Fill in remaining client fields and put it into the global list */
233        strcpy(new_client->name, client_name);
234
235        new_client->id = ds1621_id++;
236        data->valid = 0;
237        init_MUTEX(&data->update_lock);
238
239        /* Tell the I2C layer a new client has arrived */
240        if ((err = i2c_attach_client(new_client)))
241                goto ERROR3;
242
243        /* Register a new directory entry with module sensors */
244        if ((i = sensors_register_entry(new_client, type_name,
245                                        ds1621_dir_table_template,
246                                        THIS_MODULE)) < 0) {
247                err = i;
248                goto ERROR4;
249        }
250        data->sysctl_id = i;
251
252        ds1621_init_client(new_client);
253        return 0;
254
255/* OK, this is not exactly good programming practice, usually. But it is
256   very code-efficient in this case. */
257
258      ERROR4:
259        i2c_detach_client(new_client);
260      ERROR3:
261      ERROR1:
262        kfree(new_client);
263      ERROR0:
264        return err;
265}
266
267int ds1621_detach_client(struct i2c_client *client)
268{
269        int err;
270
271#ifdef MODULE
272        if (MOD_IN_USE)
273                return -EBUSY;
274#endif
275
276
277        sensors_deregister_entry(((struct ds1621_data *) (client->data))->
278                                 sysctl_id);
279
280        if ((err = i2c_detach_client(client))) {
281                printk
282           ("ds1621.o: Client deregistration failed, client not detached.\n");
283                return err;
284        }
285
286        kfree(client);
287
288        return 0;
289}
290
291
292/* No commands defined yet */
293int ds1621_command(struct i2c_client *client, unsigned int cmd, void *arg)
294{
295        return 0;
296}
297
298/* Nothing here yet */
299void ds1621_inc_use(struct i2c_client *client)
300{
301#ifdef MODULE
302        MOD_INC_USE_COUNT;
303#endif
304}
305
306/* Nothing here yet */
307void ds1621_dec_use(struct i2c_client *client)
308{
309#ifdef MODULE
310        MOD_DEC_USE_COUNT;
311#endif
312}
313
314u16 swap_bytes(u16 val)
315{
316        return (val >> 8) | (val << 8);
317}
318
319/* All registers are word-sized, except for the configuration register.
320   DS1621 uses a high-byte first convention, which is exactly opposite to
321   the usual practice. */
322int ds1621_read_value(struct i2c_client *client, u8 reg)
323{
324        if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER)
325            || (reg == DS1621_REG_TEMP_SLOPE))
326                return i2c_smbus_read_byte_data(client, reg);
327        else
328                return swap_bytes(i2c_smbus_read_word_data(client, reg));
329}
330
331/* All registers are word-sized, except for the configuration register.
332   DS1621 uses a high-byte first convention, which is exactly opposite to
333   the usual practice. */
334int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
335{
336        if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER)
337            || (reg == DS1621_REG_TEMP_SLOPE))
338                return i2c_smbus_write_byte_data(client, reg, value);
339        else
340                return i2c_smbus_write_word_data(client, reg,
341                                                 swap_bytes(value));
342}
343
344void ds1621_init_client(struct i2c_client *client)
345{
346        /* Initialize the DS1621 chip */
347        ds1621_write_value(client, DS1621_REG_TEMP_OVER,
348                         TEMP_TO_REG(DS1621_INIT_TEMP_OVER));
349        ds1621_write_value(client, DS1621_REG_TEMP_HYST,
350                         TEMP_TO_REG(DS1621_INIT_TEMP_HYST));
351        ds1621_write_value(client, DS1621_REG_CONF, 0);
352
353        /* perhaps we should start the continous conversion? For now */
354        /* you got to do that yourself using the "enable" in proc */
355}
356
357void ds1621_update_client(struct i2c_client *client)
358{
359        struct ds1621_data *data = client->data;
360        u8 new_conf;
361
362        down(&data->update_lock);
363
364        if ((jiffies - data->last_updated > HZ + HZ / 2) ||
365            (jiffies < data->last_updated) || !data->valid) {
366
367#ifdef DEBUG
368                printk("Starting ds1621 update\n");
369#endif
370
371                data->conf = ds1621_read_value(client, DS1621_REG_CONF);
372                /* we should wait for the DONE bit... */
373                data->temp = ds1621_read_value(client, DS1621_REG_TEMP);
374                data->temp_over = ds1621_read_value(client,
375                                                    DS1621_REG_TEMP_OVER);
376                data->temp_hyst =
377                    ds1621_read_value(client, DS1621_REG_TEMP_HYST);
378
379                /* reset alarms if neccessary */
380                new_conf = data->conf;
381                if (data->temp < data->temp_over)
382                        new_conf &= ~DS1621_ALARM_TEMP_HIGH;
383                if (data->temp > data->temp_hyst)
384                        new_conf &= ~DS1621_ALARM_TEMP_LOW;
385                if (data->conf != new_conf)
386                        ds1621_write_value(client, DS1621_REG_CONF,
387                                           new_conf);
388
389                data->last_updated = jiffies;
390                data->valid = 1;
391        }
392
393        up(&data->update_lock);
394}
395
396
397void ds1621_temp(struct i2c_client *client, int operation, int ctl_name,
398                 int *nrels_mag, long *results)
399{
400        struct ds1621_data *data = client->data;
401        if (operation == SENSORS_PROC_REAL_INFO)
402                *nrels_mag = 1;
403        else if (operation == SENSORS_PROC_REAL_READ) {
404                ds1621_update_client(client);
405                results[0] = TEMP_FROM_REG(data->temp_over);
406                results[1] = TEMP_FROM_REG(data->temp_hyst);
407                results[2] = TEMP_FROM_REG(data->temp);
408                *nrels_mag = 3;
409        } else if (operation == SENSORS_PROC_REAL_WRITE) {
410                if (*nrels_mag >= 1) {
411                        data->temp_over = TEMP_TO_REG(results[0]);
412                        ds1621_write_value(client, DS1621_REG_TEMP_OVER,
413                                         data->temp_over);
414                }
415                if (*nrels_mag >= 2) {
416                        data->temp_hyst = TEMP_TO_REG(results[1]);
417                        ds1621_write_value(client, DS1621_REG_TEMP_HYST,
418                                         data->temp_hyst);
419                }
420        }
421}
422
423void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name,
424                   int *nrels_mag, long *results)
425{
426        struct ds1621_data *data = client->data;
427        if (operation == SENSORS_PROC_REAL_INFO)
428                *nrels_mag = 0;
429        else if (operation == SENSORS_PROC_REAL_READ) {
430                ds1621_update_client(client);
431                results[0] = ALARMS_FROM_REG(data->conf);
432                *nrels_mag = 1;
433        }
434}
435
436void ds1621_enable(struct i2c_client *client, int operation, int ctl_name,
437                   int *nrels_mag, long *results)
438{
439        /* If you really screw up your chip (like I did) this is */
440        /* sometimes needed to (re)start the continous conversion */
441        /* there is no data to read so this might hang your SMBus! */
442
443        if (operation == SENSORS_PROC_REAL_INFO)
444                *nrels_mag = 0;
445        else if (operation == SENSORS_PROC_REAL_READ) {
446                *nrels_mag = 0;
447        } else if (operation == SENSORS_PROC_REAL_WRITE) {
448                if (*nrels_mag >= 1) {
449                        if (results[0]) {
450                                ds1621_read_value(client, DS1621_COM_START);
451                        } else {
452                                ds1621_read_value(client, DS1621_COM_STOP);
453                        }
454                } else {
455                        ds1621_read_value(client, DS1621_COM_START);
456                }
457        }
458}
459
460int __init sensors_ds1621_init(void)
461{
462        int res;
463
464        printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE);
465        ds1621_initialized = 0;
466        if ((res = i2c_add_driver(&ds1621_driver))) {
467                printk
468            ("ds1621.o: Driver registration failed, module not inserted.\n");
469                ds1621_cleanup();
470                return res;
471        }
472        ds1621_initialized++;
473        return 0;
474}
475
476int __init ds1621_cleanup(void)
477{
478        int res;
479
480        if (ds1621_initialized >= 1) {
481                if ((res = i2c_del_driver(&ds1621_driver))) {
482                        printk
483                            ("ds1621.o: Driver deregistration failed, module not removed.\n");
484                        return res;
485                }
486                ds1621_initialized--;
487        }
488
489        return 0;
490}
491
492EXPORT_NO_SYMBOLS;
493
494#ifdef MODULE
495
496MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
497MODULE_DESCRIPTION("DS1621 driver");
498
499int init_module(void)
500{
501        return sensors_ds1621_init();
502}
503
504int cleanup_module(void)
505{
506        return ds1621_cleanup();
507}
508
509#endif                          /* MODULE */
Note: See TracBrowser for help on using the browser.