root/lm-sensors/trunk/kernel/chips/ds1307.c @ 2867

Revision 2867, 7.8 KB (checked in by khali, 8 years ago)

Drop unused client id.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1
2/*
3 * linux/drivers/i2c/ds1307.c
4 *
5 * Author: Abraham van der Merwe <abraham@2d3d.co.za>
6 *
7 * Linux support for the Dallas Semiconductor DS1307 Serial Real-Time
8 * Clock.
9 *
10 * Based on code from the lm-sensors project which is available
11 * at http://www.lm-sensors.nu/ and Russell King's PCF8583 Real-Time
12 * Clock driver (linux/drivers/acorn/char/pcf8583.c).
13 *
14 * This source code is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * version 2 as published by the Free Software Foundation.
17 */
18
19#include <linux/config.h>
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/string.h>
23#include <linux/slab.h>
24#include <linux/init.h>
25#include <linux/i2c.h>
26#include <linux/i2c-proc.h>
27#include <asm/semaphore.h>
28#include "version.h"
29#include "ds1307.h"
30
31#define BCD_TO_BIN(x) (((x) & 15) + ((x) >> 4) * 10)
32#define BIN_TO_BCD(x) ((((x) / 10) << 4) + (x) % 10)
33
34static struct i2c_driver ds1307;
35static DECLARE_MUTEX (mutex);
36
37/*
38 * The DS1307 Real-Time Clock wants the address in a different
39 * message, so we can't use the normal i2c_master_recv() routine
40 * for receiving data.
41 */
42static int ds1307_i2c_recv (struct i2c_client *client,char *buf,char addr,int count)
43{
44        struct i2c_msg msg[] = {
45                { addr: client->addr, flags: 0,        len: 1,     buf: &addr },
46                { addr: client->addr, flags: I2C_M_RD, len: count, buf: buf }
47        };
48        int result = 0;
49
50        if (down_interruptible (&mutex))
51                return (-ERESTARTSYS);
52
53        if (i2c_transfer (client->adapter,msg,2) != 2)
54                result = -EIO;
55
56        up (&mutex);
57
58        return (result);
59}
60
61/*
62 * Would've been nice to specify the address to this as well, but then we
63 * would need to copy the buffer twice - not worth it...
64 */
65static int ds1307_i2c_send (struct i2c_client *client,const char *buf,int count)
66{
67        int result = 0;
68
69        if (down_interruptible (&mutex))
70                return (-ERESTARTSYS);
71
72        if (i2c_master_send (client,(const char *) buf,count) != count)
73                result = -EIO;
74
75        up (&mutex);
76
77        return (result);
78}
79
80static int ds1307_attach (struct i2c_adapter *adapter,int addr,unsigned short flags,int kind)
81{
82        struct i2c_client *client;
83        int result;
84
85        if ((client = (struct i2c_client *) kmalloc (sizeof (struct i2c_client),GFP_KERNEL)) == NULL)
86                return (-ENOMEM);
87
88        strcpy (client->name,ds1307.name);
89        client->flags = I2C_CLIENT_ALLOW_USE | I2C_CLIENT_ALLOW_MULTIPLE_USE;
90        client->addr = addr;
91        client->adapter = adapter;
92        client->driver = &ds1307;
93        client->data = NULL;
94
95        if ((result = i2c_attach_client (client))) {
96                kfree (client);
97                return (result);
98        }
99
100        return (0);
101}
102
103static int ds1307_attach_adapter (struct i2c_adapter *adapter)
104{
105        static unsigned short ignore[] = { I2C_CLIENT_END };
106        static unsigned short addr[] = { 0x68, I2C_CLIENT_END };
107        static struct i2c_client_address_data ds1307_addr_data = {
108                normal_i2c:                     addr,
109                normal_i2c_range:       ignore,
110                probe:                          ignore,
111                probe_range:            ignore,
112                ignore:                         ignore,
113                ignore_range:           ignore,
114                force:                          ignore
115        };
116
117        return (i2c_probe (adapter,&ds1307_addr_data,ds1307_attach));
118}
119
120static int ds1307_detach_client (struct i2c_client *client)
121{
122        int result;
123
124        if ((result = i2c_detach_client (client)))
125                return (result);
126
127        kfree (client);
128
129        return (0);
130}
131
132static int ds1307_getdate (struct i2c_client *client,void *arg)
133{
134        struct ds1307_date *date = (struct ds1307_date *) arg;
135        u8 buf[7];
136
137        /* this also enables the oscillator */
138        memset (buf,0,7);
139
140        /* enable 24-hour mode */
141        buf[2] = 0x40;
142
143        if (ds1307_i2c_recv (client,(char *) buf,0,7) < 0)
144                return (-EIO);
145
146        date->tm_sec = BCD_TO_BIN (buf[0] & ~0x80);
147        date->tm_min = BCD_TO_BIN (buf[1]);
148        date->tm_hour = BCD_TO_BIN (buf[2] & 0x3f);
149        date->tm_wday = BCD_TO_BIN (buf[3]) - 1;
150        date->tm_mday = BCD_TO_BIN (buf[4]);
151        date->tm_mon = BCD_TO_BIN (buf[5]) - 1;
152        date->tm_year = BCD_TO_BIN (buf[6]) + 100;
153
154        return (0);
155}
156
157static int ds1307_setdate (struct i2c_client *client,void *arg)
158{
159        struct ds1307_date *date = (struct ds1307_date *) arg;
160        u8 buf[8];
161
162        /* select address 0 */
163        buf[0] = 0;
164
165        buf[1] = BIN_TO_BCD (date->tm_sec);
166        buf[2] = BIN_TO_BCD (date->tm_min);
167        buf[3] = BIN_TO_BCD (date->tm_hour) | 0x40;
168        buf[4] = BIN_TO_BCD (date->tm_wday + 1);
169        buf[5] = BIN_TO_BCD (date->tm_mday);
170        buf[6] = BIN_TO_BCD (date->tm_mon + 1);
171        buf[7] = BIN_TO_BCD (date->tm_year - 100);
172
173        return (ds1307_i2c_send (client,(const char *) buf,8));
174}
175
176static int ds1307_enable (struct i2c_client *client,void *arg)
177{
178        u8 buf[2];
179
180        if (ds1307_i2c_recv (client,(char *) buf + 1,0,1) < 0)
181                return (-EIO);
182
183        if ((buf[1] & 0x80)) {
184                buf[0] = 0, buf[1] &= ~0x80;
185                return (ds1307_i2c_send (client,(const char *) buf,2));
186        }
187
188        return (0);
189}
190
191static int ds1307_irqon (struct i2c_client *client,void *arg)
192{
193        u8 buf[2];
194
195        if (ds1307_i2c_recv (client,(char *) buf + 1,7,1) < 0)
196                return (-EIO);
197
198        buf[0] = 7;
199        buf[1] |= 0x10;
200
201        return (ds1307_i2c_send (client,(const char *) buf,2));
202}
203
204static int ds1307_irqoff (struct i2c_client *client,void *arg)
205{
206        u8 buf[2];
207
208        if (ds1307_i2c_recv (client,(char *) buf + 1,7,1) < 0)
209                return (-EIO);
210
211        buf[0] = 7;
212        buf[1] &= ~0x10;
213
214        return (ds1307_i2c_send (client,(const char *) buf,2));
215}
216
217static int ds1307_getfreq (struct i2c_client *client,void *arg)
218{
219        u16 *freq = (u16 *) arg;
220        u8 buf;
221        static const u16 table[] = {
222                DS1307_FREQ_1HZ,
223                DS1307_FREQ_4KHZ,
224                DS1307_FREQ_8KHZ,
225                DS1307_FREQ_32KHZ
226        };
227
228        if (ds1307_i2c_recv (client,(char *) &buf,7,1) < 0)
229                return (-EIO);
230
231        *freq = table[buf & 3];
232
233        return (0);
234}
235
236static int ds1307_setfreq (struct i2c_client *client,void *arg)
237{
238        u16 *freq = (u16 *) arg;
239        u8 buf[2];
240
241        /* select address 7 */
242        buf[0] = 7;
243
244        /* default to 1HZ */
245        buf[1] = 0;
246
247        switch (*freq) {
248        case DS1307_FREQ_32KHZ:         buf[1]++;
249        case DS1307_FREQ_8KHZ:          buf[1]++;
250        case DS1307_FREQ_4KHZ:          buf[1]++;
251        case DS1307_FREQ_1HZ:           break;
252        default:
253                return (-EINVAL);
254        }
255
256        return (ds1307_i2c_send (client,(const char *) buf,2));
257}
258
259static int ds1307_read (struct i2c_client *client,void *arg)
260{
261        struct ds1307_memory *mem = (struct ds1307_memory *) arg;
262        u8 buf[DS1307_SIZE];
263
264        if (mem->offset >= DS1307_SIZE || mem->offset + mem->length > DS1307_SIZE)
265                return (-EINVAL);
266
267        if (ds1307_i2c_recv (client,(char *) buf,mem->offset + 8,mem->length) < 0)
268                return (-EIO);
269
270        memcpy (mem->buf,buf,mem->length);
271
272        return (0);
273}
274
275static int ds1307_write (struct i2c_client *client,void *arg)
276{
277        struct ds1307_memory *mem = (struct ds1307_memory *) arg;
278        u8 buf[DS1307_SIZE + 1];
279
280        if (mem->offset >= DS1307_SIZE || mem->offset + mem->length > DS1307_SIZE)
281                return (-EINVAL);
282
283        buf[0] = mem->offset + 8;
284
285        memcpy (buf + 1,mem->buf,mem->length);
286
287        return (ds1307_i2c_send (client,(const char *) buf,mem->length + 1));
288}
289
290static int ds1307_command (struct i2c_client *client,unsigned int cmd,void *arg)
291{
292        static const struct {
293                int cmd;
294                int (*function) (struct i2c_client *,void *arg);
295        } ioctl[] = {
296                { DS1307_ENABLE, ds1307_enable },
297                { DS1307_GET_DATE, ds1307_getdate },
298                { DS1307_SET_DATE, ds1307_setdate },
299                { DS1307_IRQ_ON, ds1307_irqon },
300                { DS1307_IRQ_OFF, ds1307_irqoff },
301                { DS1307_GET_FREQ, ds1307_getfreq },
302                { DS1307_SET_FREQ, ds1307_setfreq },
303                { DS1307_READ, ds1307_read },
304                { DS1307_WRITE, ds1307_write }
305        };
306        int i;
307
308        for (i = 0; i < sizeof (ioctl) / sizeof (ioctl[0]); i++)
309                if (ioctl[i].cmd == cmd)
310                        return (ioctl[i].function (client,arg));
311
312        return (-EINVAL);
313}
314
315
316static struct i2c_driver ds1307 = {
317        .name           = "ds1307",
318        .id             = I2C_DRIVERID_DS1307,
319        .flags          = I2C_DF_NOTIFY,
320        .attach_adapter = ds1307_attach_adapter,
321        .detach_client  = ds1307_detach_client,
322        .command        = ds1307_command,
323};
324
325static int __init sm_ds1307_init(void)
326{
327        printk("ds1307.o version %s (%s)\n", LM_VERSION, LM_DATE);
328        return i2c_add_driver(&ds1307);
329}
330
331static void __exit sm_ds1307_exit(void)
332{
333        i2c_del_driver(&ds1307);
334}
335
336
337
338MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
339MODULE_DESCRIPTION ("Linux support for DS1307 Real-Time Clock");
340
341MODULE_LICENSE ("GPL");
342
343module_init(sm_ds1307_init);
344module_exit(sm_ds1307_exit);
345
Note: See TracBrowser for help on using the browser.