OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [rtc/] [rtc-m41t80.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * I2C client/driver for the ST M41T80 family of i2c rtc chips.
3
 *
4
 * Author: Alexander Bigga <ab@mycable.de>
5
 *
6
 * Based on m41t00.c by Mark A. Greer <mgreer@mvista.com>
7
 *
8
 * 2006 (c) mycable GmbH
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License version 2 as
12
 * published by the Free Software Foundation.
13
 *
14
 */
15
 
16
#include <linux/module.h>
17
#include <linux/init.h>
18
#include <linux/slab.h>
19
#include <linux/string.h>
20
#include <linux/i2c.h>
21
#include <linux/rtc.h>
22
#include <linux/bcd.h>
23
#ifdef CONFIG_RTC_DRV_M41T80_WDT
24
#include <linux/miscdevice.h>
25
#include <linux/watchdog.h>
26
#include <linux/reboot.h>
27
#include <linux/fs.h>
28
#include <linux/ioctl.h>
29
#endif
30
 
31
#define M41T80_REG_SSEC 0
32
#define M41T80_REG_SEC  1
33
#define M41T80_REG_MIN  2
34
#define M41T80_REG_HOUR 3
35
#define M41T80_REG_WDAY 4
36
#define M41T80_REG_DAY  5
37
#define M41T80_REG_MON  6
38
#define M41T80_REG_YEAR 7
39
#define M41T80_REG_ALARM_MON    0xa
40
#define M41T80_REG_ALARM_DAY    0xb
41
#define M41T80_REG_ALARM_HOUR   0xc
42
#define M41T80_REG_ALARM_MIN    0xd
43
#define M41T80_REG_ALARM_SEC    0xe
44
#define M41T80_REG_FLAGS        0xf
45
#define M41T80_REG_SQW  0x13
46
 
47
#define M41T80_DATETIME_REG_SIZE        (M41T80_REG_YEAR + 1)
48
#define M41T80_ALARM_REG_SIZE   \
49
        (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
50
 
51
#define M41T80_SEC_ST           (1 << 7)        /* ST: Stop Bit */
52
#define M41T80_ALMON_AFE        (1 << 7)        /* AFE: AF Enable Bit */
53
#define M41T80_ALMON_SQWE       (1 << 6)        /* SQWE: SQW Enable Bit */
54
#define M41T80_ALHOUR_HT        (1 << 6)        /* HT: Halt Update Bit */
55
#define M41T80_FLAGS_AF         (1 << 6)        /* AF: Alarm Flag Bit */
56
#define M41T80_FLAGS_BATT_LOW   (1 << 4)        /* BL: Battery Low Bit */
57
 
58
#define M41T80_FEATURE_HT       (1 << 0)
59
#define M41T80_FEATURE_BL       (1 << 1)
60
 
61
#define DRV_VERSION "0.05"
62
 
63
struct m41t80_chip_info {
64
        const char *name;
65
        u8 features;
66
};
67
 
68
static const struct m41t80_chip_info m41t80_chip_info_tbl[] = {
69
        {
70
                .name           = "m41t80",
71
                .features       = 0,
72
        },
73
        {
74
                .name           = "m41t81",
75
                .features       = M41T80_FEATURE_HT,
76
        },
77
        {
78
                .name           = "m41t81s",
79
                .features       = M41T80_FEATURE_HT | M41T80_FEATURE_BL,
80
        },
81
        {
82
                .name           = "m41t82",
83
                .features       = M41T80_FEATURE_HT | M41T80_FEATURE_BL,
84
        },
85
        {
86
                .name           = "m41t83",
87
                .features       = M41T80_FEATURE_HT | M41T80_FEATURE_BL,
88
        },
89
        {
90
                .name           = "m41st84",
91
                .features       = M41T80_FEATURE_HT | M41T80_FEATURE_BL,
92
        },
93
        {
94
                .name           = "m41st85",
95
                .features       = M41T80_FEATURE_HT | M41T80_FEATURE_BL,
96
        },
97
        {
98
                .name           = "m41st87",
99
                .features       = M41T80_FEATURE_HT | M41T80_FEATURE_BL,
100
        },
101
};
102
 
103
struct m41t80_data {
104
        const struct m41t80_chip_info *chip;
105
        struct rtc_device *rtc;
106
};
107
 
108
static int m41t80_get_datetime(struct i2c_client *client,
109
                               struct rtc_time *tm)
110
{
111
        u8 buf[M41T80_DATETIME_REG_SIZE], dt_addr[1] = { M41T80_REG_SEC };
112
        struct i2c_msg msgs[] = {
113
                {
114
                        .addr   = client->addr,
115
                        .flags  = 0,
116
                        .len    = 1,
117
                        .buf    = dt_addr,
118
                },
119
                {
120
                        .addr   = client->addr,
121
                        .flags  = I2C_M_RD,
122
                        .len    = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
123
                        .buf    = buf + M41T80_REG_SEC,
124
                },
125
        };
126
 
127
        if (i2c_transfer(client->adapter, msgs, 2) < 0) {
128
                dev_err(&client->dev, "read error\n");
129
                return -EIO;
130
        }
131
 
132
        tm->tm_sec = BCD2BIN(buf[M41T80_REG_SEC] & 0x7f);
133
        tm->tm_min = BCD2BIN(buf[M41T80_REG_MIN] & 0x7f);
134
        tm->tm_hour = BCD2BIN(buf[M41T80_REG_HOUR] & 0x3f);
135
        tm->tm_mday = BCD2BIN(buf[M41T80_REG_DAY] & 0x3f);
136
        tm->tm_wday = buf[M41T80_REG_WDAY] & 0x07;
137
        tm->tm_mon = BCD2BIN(buf[M41T80_REG_MON] & 0x1f) - 1;
138
 
139
        /* assume 20YY not 19YY, and ignore the Century Bit */
140
        tm->tm_year = BCD2BIN(buf[M41T80_REG_YEAR]) + 100;
141
        return 0;
142
}
143
 
144
/* Sets the given date and time to the real time clock. */
145
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
146
{
147
        u8 wbuf[1 + M41T80_DATETIME_REG_SIZE];
148
        u8 *buf = &wbuf[1];
149
        u8 dt_addr[1] = { M41T80_REG_SEC };
150
        struct i2c_msg msgs_in[] = {
151
                {
152
                        .addr   = client->addr,
153
                        .flags  = 0,
154
                        .len    = 1,
155
                        .buf    = dt_addr,
156
                },
157
                {
158
                        .addr   = client->addr,
159
                        .flags  = I2C_M_RD,
160
                        .len    = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
161
                        .buf    = buf + M41T80_REG_SEC,
162
                },
163
        };
164
        struct i2c_msg msgs[] = {
165
                {
166
                        .addr   = client->addr,
167
                        .flags  = 0,
168
                        .len    = 1 + M41T80_DATETIME_REG_SIZE,
169
                        .buf    = wbuf,
170
                 },
171
        };
172
 
173
        /* Read current reg values into buf[1..7] */
174
        if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
175
                dev_err(&client->dev, "read error\n");
176
                return -EIO;
177
        }
178
 
179
        wbuf[0] = 0; /* offset into rtc's regs */
180
        /* Merge time-data and register flags into buf[0..7] */
181
        buf[M41T80_REG_SSEC] = 0;
182
        buf[M41T80_REG_SEC] =
183
                BIN2BCD(tm->tm_sec) | (buf[M41T80_REG_SEC] & ~0x7f);
184
        buf[M41T80_REG_MIN] =
185
                BIN2BCD(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f);
186
        buf[M41T80_REG_HOUR] =
187
                BIN2BCD(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f) ;
188
        buf[M41T80_REG_WDAY] =
189
                (tm->tm_wday & 0x07) | (buf[M41T80_REG_WDAY] & ~0x07);
190
        buf[M41T80_REG_DAY] =
191
                BIN2BCD(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f);
192
        buf[M41T80_REG_MON] =
193
                BIN2BCD(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f);
194
        /* assume 20YY not 19YY */
195
        buf[M41T80_REG_YEAR] = BIN2BCD(tm->tm_year % 100);
196
 
197
        if (i2c_transfer(client->adapter, msgs, 1) != 1) {
198
                dev_err(&client->dev, "write error\n");
199
                return -EIO;
200
        }
201
        return 0;
202
}
203
 
204
#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
205
static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq)
206
{
207
        struct i2c_client *client = to_i2c_client(dev);
208
        struct m41t80_data *clientdata = i2c_get_clientdata(client);
209
        u8 reg;
210
 
211
        if (clientdata->chip->features & M41T80_FEATURE_BL) {
212
                reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
213
                seq_printf(seq, "battery\t\t: %s\n",
214
                           (reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok");
215
        }
216
        return 0;
217
}
218
#else
219
#define m41t80_rtc_proc NULL
220
#endif
221
 
222
static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
223
{
224
        return m41t80_get_datetime(to_i2c_client(dev), tm);
225
}
226
 
227
static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
228
{
229
        return m41t80_set_datetime(to_i2c_client(dev), tm);
230
}
231
 
232
#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
233
static int
234
m41t80_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
235
{
236
        struct i2c_client *client = to_i2c_client(dev);
237
        int rc;
238
 
239
        switch (cmd) {
240
        case RTC_AIE_OFF:
241
        case RTC_AIE_ON:
242
                break;
243
        default:
244
                return -ENOIOCTLCMD;
245
        }
246
 
247
        rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
248
        if (rc < 0)
249
                goto err;
250
        switch (cmd) {
251
        case RTC_AIE_OFF:
252
                rc &= ~M41T80_ALMON_AFE;
253
                break;
254
        case RTC_AIE_ON:
255
                rc |= M41T80_ALMON_AFE;
256
                break;
257
        }
258
        if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, rc) < 0)
259
                goto err;
260
        return 0;
261
err:
262
        return -EIO;
263
}
264
#else
265
#define m41t80_rtc_ioctl NULL
266
#endif
267
 
268
static int m41t80_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
269
{
270
        struct i2c_client *client = to_i2c_client(dev);
271
        u8 wbuf[1 + M41T80_ALARM_REG_SIZE];
272
        u8 *buf = &wbuf[1];
273
        u8 *reg = buf - M41T80_REG_ALARM_MON;
274
        u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
275
        struct i2c_msg msgs_in[] = {
276
                {
277
                        .addr   = client->addr,
278
                        .flags  = 0,
279
                        .len    = 1,
280
                        .buf    = dt_addr,
281
                },
282
                {
283
                        .addr   = client->addr,
284
                        .flags  = I2C_M_RD,
285
                        .len    = M41T80_ALARM_REG_SIZE,
286
                        .buf    = buf,
287
                },
288
        };
289
        struct i2c_msg msgs[] = {
290
                {
291
                        .addr   = client->addr,
292
                        .flags  = 0,
293
                        .len    = 1 + M41T80_ALARM_REG_SIZE,
294
                        .buf    = wbuf,
295
                 },
296
        };
297
 
298
        if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
299
                dev_err(&client->dev, "read error\n");
300
                return -EIO;
301
        }
302
        reg[M41T80_REG_ALARM_MON] &= ~(0x1f | M41T80_ALMON_AFE);
303
        reg[M41T80_REG_ALARM_DAY] = 0;
304
        reg[M41T80_REG_ALARM_HOUR] &= ~(0x3f | 0x80);
305
        reg[M41T80_REG_ALARM_MIN] = 0;
306
        reg[M41T80_REG_ALARM_SEC] = 0;
307
 
308
        wbuf[0] = M41T80_REG_ALARM_MON; /* offset into rtc's regs */
309
        reg[M41T80_REG_ALARM_SEC] |= t->time.tm_sec >= 0 ?
310
                BIN2BCD(t->time.tm_sec) : 0x80;
311
        reg[M41T80_REG_ALARM_MIN] |= t->time.tm_min >= 0 ?
312
                BIN2BCD(t->time.tm_min) : 0x80;
313
        reg[M41T80_REG_ALARM_HOUR] |= t->time.tm_hour >= 0 ?
314
                BIN2BCD(t->time.tm_hour) : 0x80;
315
        reg[M41T80_REG_ALARM_DAY] |= t->time.tm_mday >= 0 ?
316
                BIN2BCD(t->time.tm_mday) : 0x80;
317
        if (t->time.tm_mon >= 0)
318
                reg[M41T80_REG_ALARM_MON] |= BIN2BCD(t->time.tm_mon + 1);
319
        else
320
                reg[M41T80_REG_ALARM_DAY] |= 0x40;
321
 
322
        if (i2c_transfer(client->adapter, msgs, 1) != 1) {
323
                dev_err(&client->dev, "write error\n");
324
                return -EIO;
325
        }
326
 
327
        if (t->enabled) {
328
                reg[M41T80_REG_ALARM_MON] |= M41T80_ALMON_AFE;
329
                if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
330
                                              reg[M41T80_REG_ALARM_MON]) < 0) {
331
                        dev_err(&client->dev, "write error\n");
332
                        return -EIO;
333
                }
334
        }
335
        return 0;
336
}
337
 
338
static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t)
339
{
340
        struct i2c_client *client = to_i2c_client(dev);
341
        u8 buf[M41T80_ALARM_REG_SIZE + 1]; /* all alarm regs and flags */
342
        u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
343
        u8 *reg = buf - M41T80_REG_ALARM_MON;
344
        struct i2c_msg msgs[] = {
345
                {
346
                        .addr   = client->addr,
347
                        .flags  = 0,
348
                        .len    = 1,
349
                        .buf    = dt_addr,
350
                },
351
                {
352
                        .addr   = client->addr,
353
                        .flags  = I2C_M_RD,
354
                        .len    = M41T80_ALARM_REG_SIZE + 1,
355
                        .buf    = buf,
356
                },
357
        };
358
 
359
        if (i2c_transfer(client->adapter, msgs, 2) < 0) {
360
                dev_err(&client->dev, "read error\n");
361
                return -EIO;
362
        }
363
        t->time.tm_sec = -1;
364
        t->time.tm_min = -1;
365
        t->time.tm_hour = -1;
366
        t->time.tm_mday = -1;
367
        t->time.tm_mon = -1;
368
        if (!(reg[M41T80_REG_ALARM_SEC] & 0x80))
369
                t->time.tm_sec = BCD2BIN(reg[M41T80_REG_ALARM_SEC] & 0x7f);
370
        if (!(reg[M41T80_REG_ALARM_MIN] & 0x80))
371
                t->time.tm_min = BCD2BIN(reg[M41T80_REG_ALARM_MIN] & 0x7f);
372
        if (!(reg[M41T80_REG_ALARM_HOUR] & 0x80))
373
                t->time.tm_hour = BCD2BIN(reg[M41T80_REG_ALARM_HOUR] & 0x3f);
374
        if (!(reg[M41T80_REG_ALARM_DAY] & 0x80))
375
                t->time.tm_mday = BCD2BIN(reg[M41T80_REG_ALARM_DAY] & 0x3f);
376
        if (!(reg[M41T80_REG_ALARM_DAY] & 0x40))
377
                t->time.tm_mon = BCD2BIN(reg[M41T80_REG_ALARM_MON] & 0x1f) - 1;
378
        t->time.tm_year = -1;
379
        t->time.tm_wday = -1;
380
        t->time.tm_yday = -1;
381
        t->time.tm_isdst = -1;
382
        t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
383
        t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
384
        return 0;
385
}
386
 
387
static struct rtc_class_ops m41t80_rtc_ops = {
388
        .read_time = m41t80_rtc_read_time,
389
        .set_time = m41t80_rtc_set_time,
390
        .read_alarm = m41t80_rtc_read_alarm,
391
        .set_alarm = m41t80_rtc_set_alarm,
392
        .proc = m41t80_rtc_proc,
393
        .ioctl = m41t80_rtc_ioctl,
394
};
395
 
396
#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
397
static ssize_t m41t80_sysfs_show_flags(struct device *dev,
398
                                struct device_attribute *attr, char *buf)
399
{
400
        struct i2c_client *client = to_i2c_client(dev);
401
        int val;
402
 
403
        val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
404
        if (val < 0)
405
                return -EIO;
406
        return sprintf(buf, "%#x\n", val);
407
}
408
static DEVICE_ATTR(flags, S_IRUGO, m41t80_sysfs_show_flags, NULL);
409
 
410
static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev,
411
                                struct device_attribute *attr, char *buf)
412
{
413
        struct i2c_client *client = to_i2c_client(dev);
414
        int val;
415
 
416
        val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW);
417
        if (val < 0)
418
                return -EIO;
419
        val = (val >> 4) & 0xf;
420
        switch (val) {
421
        case 0:
422
                break;
423
        case 1:
424
                val = 32768;
425
                break;
426
        default:
427
                val = 32768 >> val;
428
        }
429
        return sprintf(buf, "%d\n", val);
430
}
431
static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
432
                                struct device_attribute *attr,
433
                                const char *buf, size_t count)
434
{
435
        struct i2c_client *client = to_i2c_client(dev);
436
        int almon, sqw;
437
        int val = simple_strtoul(buf, NULL, 0);
438
 
439
        if (val) {
440
                if (!is_power_of_2(val))
441
                        return -EINVAL;
442
                val = ilog2(val);
443
                if (val == 15)
444
                        val = 1;
445
                else if (val < 14)
446
                        val = 15 - val;
447
                else
448
                        return -EINVAL;
449
        }
450
        /* disable SQW, set SQW frequency & re-enable */
451
        almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
452
        if (almon < 0)
453
                return -EIO;
454
        sqw = i2c_smbus_read_byte_data(client, M41T80_REG_SQW);
455
        if (sqw < 0)
456
                return -EIO;
457
        sqw = (sqw & 0x0f) | (val << 4);
458
        if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
459
                                      almon & ~M41T80_ALMON_SQWE) < 0 ||
460
            i2c_smbus_write_byte_data(client, M41T80_REG_SQW, sqw) < 0)
461
                return -EIO;
462
        if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
463
                                             almon | M41T80_ALMON_SQWE) < 0)
464
                return -EIO;
465
        return count;
466
}
467
static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR,
468
                   m41t80_sysfs_show_sqwfreq, m41t80_sysfs_set_sqwfreq);
469
 
470
static struct attribute *attrs[] = {
471
        &dev_attr_flags.attr,
472
        &dev_attr_sqwfreq.attr,
473
        NULL,
474
};
475
static struct attribute_group attr_group = {
476
        .attrs = attrs,
477
};
478
 
479
static int m41t80_sysfs_register(struct device *dev)
480
{
481
        return sysfs_create_group(&dev->kobj, &attr_group);
482
}
483
#else
484
static int m41t80_sysfs_register(struct device *dev)
485
{
486
        return 0;
487
}
488
#endif
489
 
490
#ifdef CONFIG_RTC_DRV_M41T80_WDT
491
/*
492
 *****************************************************************************
493
 *
494
 * Watchdog Driver
495
 *
496
 *****************************************************************************
497
 */
498
static struct i2c_client *save_client;
499
 
500
/* Default margin */
501
#define WD_TIMO 60              /* 1..31 seconds */
502
 
503
static int wdt_margin = WD_TIMO;
504
module_param(wdt_margin, int, 0);
505
MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)");
506
 
507
static unsigned long wdt_is_open;
508
static int boot_flag;
509
 
510
/**
511
 *      wdt_ping:
512
 *
513
 *      Reload counter one with the watchdog timeout. We don't bother reloading
514
 *      the cascade counter.
515
 */
516
static void wdt_ping(void)
517
{
518
        unsigned char i2c_data[2];
519
        struct i2c_msg msgs1[1] = {
520
                {
521
                        .addr   = save_client->addr,
522
                        .flags  = 0,
523
                        .len    = 2,
524
                        .buf    = i2c_data,
525
                },
526
        };
527
        i2c_data[0] = 0x09;              /* watchdog register */
528
 
529
        if (wdt_margin > 31)
530
                i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */
531
        else
532
                /*
533
                 * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02)
534
                 */
535
                i2c_data[1] = wdt_margin<<2 | 0x82;
536
 
537
        i2c_transfer(save_client->adapter, msgs1, 1);
538
}
539
 
540
/**
541
 *      wdt_disable:
542
 *
543
 *      disables watchdog.
544
 */
545
static void wdt_disable(void)
546
{
547
        unsigned char i2c_data[2], i2c_buf[0x10];
548
        struct i2c_msg msgs0[2] = {
549
                {
550
                        .addr   = save_client->addr,
551
                        .flags  = 0,
552
                        .len    = 1,
553
                        .buf    = i2c_data,
554
                },
555
                {
556
                        .addr   = save_client->addr,
557
                        .flags  = I2C_M_RD,
558
                        .len    = 1,
559
                        .buf    = i2c_buf,
560
                },
561
        };
562
        struct i2c_msg msgs1[1] = {
563
                {
564
                        .addr   = save_client->addr,
565
                        .flags  = 0,
566
                        .len    = 2,
567
                        .buf    = i2c_data,
568
                },
569
        };
570
 
571
        i2c_data[0] = 0x09;
572
        i2c_transfer(save_client->adapter, msgs0, 2);
573
 
574
        i2c_data[0] = 0x09;
575
        i2c_data[1] = 0x00;
576
        i2c_transfer(save_client->adapter, msgs1, 1);
577
}
578
 
579
/**
580
 *      wdt_write:
581
 *      @file: file handle to the watchdog
582
 *      @buf: buffer to write (unused as data does not matter here
583
 *      @count: count of bytes
584
 *      @ppos: pointer to the position to write. No seeks allowed
585
 *
586
 *      A write to a watchdog device is defined as a keepalive signal. Any
587
 *      write of data will do, as we we don't define content meaning.
588
 */
589
static ssize_t wdt_write(struct file *file, const char __user *buf,
590
                         size_t count, loff_t *ppos)
591
{
592
        /*  Can't seek (pwrite) on this device
593
        if (ppos != &file->f_pos)
594
        return -ESPIPE;
595
        */
596
        if (count) {
597
                wdt_ping();
598
                return 1;
599
        }
600
        return 0;
601
}
602
 
603
static ssize_t wdt_read(struct file *file, char __user *buf,
604
                        size_t count, loff_t *ppos)
605
{
606
        return 0;
607
}
608
 
609
/**
610
 *      wdt_ioctl:
611
 *      @inode: inode of the device
612
 *      @file: file handle to the device
613
 *      @cmd: watchdog command
614
 *      @arg: argument pointer
615
 *
616
 *      The watchdog API defines a common set of functions for all watchdogs
617
 *      according to their available features. We only actually usefully support
618
 *      querying capabilities and current status.
619
 */
620
static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
621
                     unsigned long arg)
622
{
623
        int new_margin, rv;
624
        static struct watchdog_info ident = {
625
                .options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING |
626
                        WDIOF_SETTIMEOUT,
627
                .firmware_version = 1,
628
                .identity = "M41T80 WTD"
629
        };
630
 
631
        switch (cmd) {
632
        case WDIOC_GETSUPPORT:
633
                return copy_to_user((struct watchdog_info __user *)arg, &ident,
634
                                    sizeof(ident)) ? -EFAULT : 0;
635
 
636
        case WDIOC_GETSTATUS:
637
        case WDIOC_GETBOOTSTATUS:
638
                return put_user(boot_flag, (int __user *)arg);
639
        case WDIOC_KEEPALIVE:
640
                wdt_ping();
641
                return 0;
642
        case WDIOC_SETTIMEOUT:
643
                if (get_user(new_margin, (int __user *)arg))
644
                        return -EFAULT;
645
                /* Arbitrary, can't find the card's limits */
646
                if (new_margin < 1 || new_margin > 124)
647
                        return -EINVAL;
648
                wdt_margin = new_margin;
649
                wdt_ping();
650
                /* Fall */
651
        case WDIOC_GETTIMEOUT:
652
                return put_user(wdt_margin, (int __user *)arg);
653
 
654
        case WDIOC_SETOPTIONS:
655
                if (copy_from_user(&rv, (int __user *)arg, sizeof(int)))
656
                        return -EFAULT;
657
 
658
                if (rv & WDIOS_DISABLECARD) {
659
                        printk(KERN_INFO
660
                               "rtc-m41t80: disable watchdog\n");
661
                        wdt_disable();
662
                }
663
 
664
                if (rv & WDIOS_ENABLECARD) {
665
                        printk(KERN_INFO
666
                               "rtc-m41t80: enable watchdog\n");
667
                        wdt_ping();
668
                }
669
 
670
                return -EINVAL;
671
        }
672
        return -ENOTTY;
673
}
674
 
675
/**
676
 *      wdt_open:
677
 *      @inode: inode of device
678
 *      @file: file handle to device
679
 *
680
 */
681
static int wdt_open(struct inode *inode, struct file *file)
682
{
683
        if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
684
                if (test_and_set_bit(0, &wdt_is_open))
685
                        return -EBUSY;
686
                /*
687
                 *      Activate
688
                 */
689
                wdt_is_open = 1;
690
                return 0;
691
        }
692
        return -ENODEV;
693
}
694
 
695
/**
696
 *      wdt_close:
697
 *      @inode: inode to board
698
 *      @file: file handle to board
699
 *
700
 */
701
static int wdt_release(struct inode *inode, struct file *file)
702
{
703
        if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
704
                clear_bit(0, &wdt_is_open);
705
        return 0;
706
}
707
 
708
/**
709
 *      notify_sys:
710
 *      @this: our notifier block
711
 *      @code: the event being reported
712
 *      @unused: unused
713
 *
714
 *      Our notifier is called on system shutdowns. We want to turn the card
715
 *      off at reboot otherwise the machine will reboot again during memory
716
 *      test or worse yet during the following fsck. This would suck, in fact
717
 *      trust me - if it happens it does suck.
718
 */
719
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
720
                          void *unused)
721
{
722
        if (code == SYS_DOWN || code == SYS_HALT)
723
                /* Disable Watchdog */
724
                wdt_disable();
725
        return NOTIFY_DONE;
726
}
727
 
728
static const struct file_operations wdt_fops = {
729
        .owner  = THIS_MODULE,
730
        .read   = wdt_read,
731
        .ioctl  = wdt_ioctl,
732
        .write  = wdt_write,
733
        .open   = wdt_open,
734
        .release = wdt_release,
735
};
736
 
737
static struct miscdevice wdt_dev = {
738
        .minor = WATCHDOG_MINOR,
739
        .name = "watchdog",
740
        .fops = &wdt_fops,
741
};
742
 
743
/*
744
 *      The WDT card needs to learn about soft shutdowns in order to
745
 *      turn the timebomb registers off.
746
 */
747
static struct notifier_block wdt_notifier = {
748
        .notifier_call = wdt_notify_sys,
749
};
750
#endif /* CONFIG_RTC_DRV_M41T80_WDT */
751
 
752
/*
753
 *****************************************************************************
754
 *
755
 *      Driver Interface
756
 *
757
 *****************************************************************************
758
 */
759
static int m41t80_probe(struct i2c_client *client)
760
{
761
        int i, rc = 0;
762
        struct rtc_device *rtc = NULL;
763
        struct rtc_time tm;
764
        const struct m41t80_chip_info *chip;
765
        struct m41t80_data *clientdata = NULL;
766
 
767
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C
768
                                     | I2C_FUNC_SMBUS_BYTE_DATA)) {
769
                rc = -ENODEV;
770
                goto exit;
771
        }
772
 
773
        dev_info(&client->dev,
774
                 "chip found, driver version " DRV_VERSION "\n");
775
 
776
        chip = NULL;
777
        for (i = 0; i < ARRAY_SIZE(m41t80_chip_info_tbl); i++) {
778
                if (!strcmp(m41t80_chip_info_tbl[i].name, client->name)) {
779
                        chip = &m41t80_chip_info_tbl[i];
780
                        break;
781
                }
782
        }
783
        if (!chip) {
784
                dev_err(&client->dev, "%s is not supported\n", client->name);
785
                rc = -ENODEV;
786
                goto exit;
787
        }
788
 
789
        clientdata = kzalloc(sizeof(*clientdata), GFP_KERNEL);
790
        if (!clientdata) {
791
                rc = -ENOMEM;
792
                goto exit;
793
        }
794
 
795
        rtc = rtc_device_register(client->name, &client->dev,
796
                                  &m41t80_rtc_ops, THIS_MODULE);
797
        if (IS_ERR(rtc)) {
798
                rc = PTR_ERR(rtc);
799
                rtc = NULL;
800
                goto exit;
801
        }
802
 
803
        clientdata->rtc = rtc;
804
        clientdata->chip = chip;
805
        i2c_set_clientdata(client, clientdata);
806
 
807
        /* Make sure HT (Halt Update) bit is cleared */
808
        rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
809
        if (rc < 0)
810
                goto ht_err;
811
 
812
        if (rc & M41T80_ALHOUR_HT) {
813
                if (chip->features & M41T80_FEATURE_HT) {
814
                        m41t80_get_datetime(client, &tm);
815
                        dev_info(&client->dev, "HT bit was set!\n");
816
                        dev_info(&client->dev,
817
                                 "Power Down at "
818
                                 "%04i-%02i-%02i %02i:%02i:%02i\n",
819
                                 tm.tm_year + 1900,
820
                                 tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
821
                                 tm.tm_min, tm.tm_sec);
822
                }
823
                if (i2c_smbus_write_byte_data(client,
824
                                              M41T80_REG_ALARM_HOUR,
825
                                              rc & ~M41T80_ALHOUR_HT) < 0)
826
                        goto ht_err;
827
        }
828
 
829
        /* Make sure ST (stop) bit is cleared */
830
        rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC);
831
        if (rc < 0)
832
                goto st_err;
833
 
834
        if (rc & M41T80_SEC_ST) {
835
                if (i2c_smbus_write_byte_data(client, M41T80_REG_SEC,
836
                                              rc & ~M41T80_SEC_ST) < 0)
837
                        goto st_err;
838
        }
839
 
840
        rc = m41t80_sysfs_register(&client->dev);
841
        if (rc)
842
                goto exit;
843
 
844
#ifdef CONFIG_RTC_DRV_M41T80_WDT
845
        if (chip->features & M41T80_FEATURE_HT) {
846
                rc = misc_register(&wdt_dev);
847
                if (rc)
848
                        goto exit;
849
                rc = register_reboot_notifier(&wdt_notifier);
850
                if (rc) {
851
                        misc_deregister(&wdt_dev);
852
                        goto exit;
853
                }
854
                save_client = client;
855
        }
856
#endif
857
        return 0;
858
 
859
st_err:
860
        rc = -EIO;
861
        dev_err(&client->dev, "Can't clear ST bit\n");
862
        goto exit;
863
ht_err:
864
        rc = -EIO;
865
        dev_err(&client->dev, "Can't clear HT bit\n");
866
        goto exit;
867
 
868
exit:
869
        if (rtc)
870
                rtc_device_unregister(rtc);
871
        kfree(clientdata);
872
        return rc;
873
}
874
 
875
static int m41t80_remove(struct i2c_client *client)
876
{
877
        struct m41t80_data *clientdata = i2c_get_clientdata(client);
878
        struct rtc_device *rtc = clientdata->rtc;
879
 
880
#ifdef CONFIG_RTC_DRV_M41T80_WDT
881
        if (clientdata->chip->features & M41T80_FEATURE_HT) {
882
                misc_deregister(&wdt_dev);
883
                unregister_reboot_notifier(&wdt_notifier);
884
        }
885
#endif
886
        if (rtc)
887
                rtc_device_unregister(rtc);
888
        kfree(clientdata);
889
 
890
        return 0;
891
}
892
 
893
static struct i2c_driver m41t80_driver = {
894
        .driver = {
895
                .name = "rtc-m41t80",
896
        },
897
        .probe = m41t80_probe,
898
        .remove = m41t80_remove,
899
};
900
 
901
static int __init m41t80_rtc_init(void)
902
{
903
        return i2c_add_driver(&m41t80_driver);
904
}
905
 
906
static void __exit m41t80_rtc_exit(void)
907
{
908
        i2c_del_driver(&m41t80_driver);
909
}
910
 
911
MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>");
912
MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver");
913
MODULE_LICENSE("GPL");
914
MODULE_VERSION(DRV_VERSION);
915
 
916
module_init(m41t80_rtc_init);
917
module_exit(m41t80_rtc_exit);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.