linux/drivers/rtc/rtc-m41t80.c
<<
>>
Prefs
   1/*
   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/bcd.h>
  17#include <linux/i2c.h>
  18#include <linux/init.h>
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/rtc.h>
  22#include <linux/slab.h>
  23#include <linux/mutex.h>
  24#include <linux/string.h>
  25#ifdef CONFIG_RTC_DRV_M41T80_WDT
  26#include <linux/fs.h>
  27#include <linux/ioctl.h>
  28#include <linux/miscdevice.h>
  29#include <linux/reboot.h>
  30#include <linux/watchdog.h>
  31#endif
  32
  33#define M41T80_REG_SSEC 0
  34#define M41T80_REG_SEC  1
  35#define M41T80_REG_MIN  2
  36#define M41T80_REG_HOUR 3
  37#define M41T80_REG_WDAY 4
  38#define M41T80_REG_DAY  5
  39#define M41T80_REG_MON  6
  40#define M41T80_REG_YEAR 7
  41#define M41T80_REG_ALARM_MON    0xa
  42#define M41T80_REG_ALARM_DAY    0xb
  43#define M41T80_REG_ALARM_HOUR   0xc
  44#define M41T80_REG_ALARM_MIN    0xd
  45#define M41T80_REG_ALARM_SEC    0xe
  46#define M41T80_REG_FLAGS        0xf
  47#define M41T80_REG_SQW  0x13
  48
  49#define M41T80_DATETIME_REG_SIZE        (M41T80_REG_YEAR + 1)
  50#define M41T80_ALARM_REG_SIZE   \
  51        (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
  52
  53#define M41T80_SEC_ST           (1 << 7)        /* ST: Stop Bit */
  54#define M41T80_ALMON_AFE        (1 << 7)        /* AFE: AF Enable Bit */
  55#define M41T80_ALMON_SQWE       (1 << 6)        /* SQWE: SQW Enable Bit */
  56#define M41T80_ALHOUR_HT        (1 << 6)        /* HT: Halt Update Bit */
  57#define M41T80_FLAGS_AF         (1 << 6)        /* AF: Alarm Flag Bit */
  58#define M41T80_FLAGS_BATT_LOW   (1 << 4)        /* BL: Battery Low Bit */
  59#define M41T80_WATCHDOG_RB2     (1 << 7)        /* RB: Watchdog resolution */
  60#define M41T80_WATCHDOG_RB1     (1 << 1)        /* RB: Watchdog resolution */
  61#define M41T80_WATCHDOG_RB0     (1 << 0)        /* RB: Watchdog resolution */
  62
  63#define M41T80_FEATURE_HT       (1 << 0)        /* Halt feature */
  64#define M41T80_FEATURE_BL       (1 << 1)        /* Battery low indicator */
  65#define M41T80_FEATURE_SQ       (1 << 2)        /* Squarewave feature */
  66#define M41T80_FEATURE_WD       (1 << 3)        /* Extra watchdog resolution */
  67#define M41T80_FEATURE_SQ_ALT   (1 << 4)        /* RSx bits are in reg 4 */
  68
  69#define DRV_VERSION "0.05"
  70
  71static DEFINE_MUTEX(m41t80_rtc_mutex);
  72static const struct i2c_device_id m41t80_id[] = {
  73        { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
  74        { "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
  75        { "m41t80", M41T80_FEATURE_SQ },
  76        { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ},
  77        { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
  78        { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
  79        { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
  80        { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
  81        { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
  82        { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
  83        { }
  84};
  85MODULE_DEVICE_TABLE(i2c, m41t80_id);
  86
  87struct m41t80_data {
  88        u8 features;
  89        struct rtc_device *rtc;
  90};
  91
  92static int m41t80_get_datetime(struct i2c_client *client,
  93                               struct rtc_time *tm)
  94{
  95        u8 buf[M41T80_DATETIME_REG_SIZE], dt_addr[1] = { M41T80_REG_SEC };
  96        struct i2c_msg msgs[] = {
  97                {
  98                        .addr   = client->addr,
  99                        .flags  = 0,
 100                        .len    = 1,
 101                        .buf    = dt_addr,
 102                },
 103                {
 104                        .addr   = client->addr,
 105                        .flags  = I2C_M_RD,
 106                        .len    = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
 107                        .buf    = buf + M41T80_REG_SEC,
 108                },
 109        };
 110
 111        if (i2c_transfer(client->adapter, msgs, 2) < 0) {
 112                dev_err(&client->dev, "read error\n");
 113                return -EIO;
 114        }
 115
 116        tm->tm_sec = bcd2bin(buf[M41T80_REG_SEC] & 0x7f);
 117        tm->tm_min = bcd2bin(buf[M41T80_REG_MIN] & 0x7f);
 118        tm->tm_hour = bcd2bin(buf[M41T80_REG_HOUR] & 0x3f);
 119        tm->tm_mday = bcd2bin(buf[M41T80_REG_DAY] & 0x3f);
 120        tm->tm_wday = buf[M41T80_REG_WDAY] & 0x07;
 121        tm->tm_mon = bcd2bin(buf[M41T80_REG_MON] & 0x1f) - 1;
 122
 123        /* assume 20YY not 19YY, and ignore the Century Bit */
 124        tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
 125        return rtc_valid_tm(tm);
 126}
 127
 128/* Sets the given date and time to the real time clock. */
 129static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 130{
 131        u8 wbuf[1 + M41T80_DATETIME_REG_SIZE];
 132        u8 *buf = &wbuf[1];
 133        u8 dt_addr[1] = { M41T80_REG_SEC };
 134        struct i2c_msg msgs_in[] = {
 135                {
 136                        .addr   = client->addr,
 137                        .flags  = 0,
 138                        .len    = 1,
 139                        .buf    = dt_addr,
 140                },
 141                {
 142                        .addr   = client->addr,
 143                        .flags  = I2C_M_RD,
 144                        .len    = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
 145                        .buf    = buf + M41T80_REG_SEC,
 146                },
 147        };
 148        struct i2c_msg msgs[] = {
 149                {
 150                        .addr   = client->addr,
 151                        .flags  = 0,
 152                        .len    = 1 + M41T80_DATETIME_REG_SIZE,
 153                        .buf    = wbuf,
 154                 },
 155        };
 156
 157        /* Read current reg values into buf[1..7] */
 158        if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
 159                dev_err(&client->dev, "read error\n");
 160                return -EIO;
 161        }
 162
 163        wbuf[0] = 0; /* offset into rtc's regs */
 164        /* Merge time-data and register flags into buf[0..7] */
 165        buf[M41T80_REG_SSEC] = 0;
 166        buf[M41T80_REG_SEC] =
 167                bin2bcd(tm->tm_sec) | (buf[M41T80_REG_SEC] & ~0x7f);
 168        buf[M41T80_REG_MIN] =
 169                bin2bcd(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f);
 170        buf[M41T80_REG_HOUR] =
 171                bin2bcd(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f) ;
 172        buf[M41T80_REG_WDAY] =
 173                (tm->tm_wday & 0x07) | (buf[M41T80_REG_WDAY] & ~0x07);
 174        buf[M41T80_REG_DAY] =
 175                bin2bcd(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f);
 176        buf[M41T80_REG_MON] =
 177                bin2bcd(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f);
 178        /* assume 20YY not 19YY */
 179        buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year % 100);
 180
 181        if (i2c_transfer(client->adapter, msgs, 1) != 1) {
 182                dev_err(&client->dev, "write error\n");
 183                return -EIO;
 184        }
 185        return 0;
 186}
 187
 188#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
 189static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq)
 190{
 191        struct i2c_client *client = to_i2c_client(dev);
 192        struct m41t80_data *clientdata = i2c_get_clientdata(client);
 193        u8 reg;
 194
 195        if (clientdata->features & M41T80_FEATURE_BL) {
 196                reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
 197                seq_printf(seq, "battery\t\t: %s\n",
 198                           (reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok");
 199        }
 200        return 0;
 201}
 202#else
 203#define m41t80_rtc_proc NULL
 204#endif
 205
 206static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
 207{
 208        return m41t80_get_datetime(to_i2c_client(dev), tm);
 209}
 210
 211static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
 212{
 213        return m41t80_set_datetime(to_i2c_client(dev), tm);
 214}
 215
 216static int m41t80_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
 217{
 218        struct i2c_client *client = to_i2c_client(dev);
 219        int rc;
 220
 221        rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
 222        if (rc < 0)
 223                goto err;
 224
 225        if (enabled)
 226                rc |= M41T80_ALMON_AFE;
 227        else
 228                rc &= ~M41T80_ALMON_AFE;
 229
 230        if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, rc) < 0)
 231                goto err;
 232
 233        return 0;
 234err:
 235        return -EIO;
 236}
 237
 238static int m41t80_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 239{
 240        struct i2c_client *client = to_i2c_client(dev);
 241        u8 wbuf[1 + M41T80_ALARM_REG_SIZE];
 242        u8 *buf = &wbuf[1];
 243        u8 *reg = buf - M41T80_REG_ALARM_MON;
 244        u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
 245        struct i2c_msg msgs_in[] = {
 246                {
 247                        .addr   = client->addr,
 248                        .flags  = 0,
 249                        .len    = 1,
 250                        .buf    = dt_addr,
 251                },
 252                {
 253                        .addr   = client->addr,
 254                        .flags  = I2C_M_RD,
 255                        .len    = M41T80_ALARM_REG_SIZE,
 256                        .buf    = buf,
 257                },
 258        };
 259        struct i2c_msg msgs[] = {
 260                {
 261                        .addr   = client->addr,
 262                        .flags  = 0,
 263                        .len    = 1 + M41T80_ALARM_REG_SIZE,
 264                        .buf    = wbuf,
 265                 },
 266        };
 267
 268        if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
 269                dev_err(&client->dev, "read error\n");
 270                return -EIO;
 271        }
 272        reg[M41T80_REG_ALARM_MON] &= ~(0x1f | M41T80_ALMON_AFE);
 273        reg[M41T80_REG_ALARM_DAY] = 0;
 274        reg[M41T80_REG_ALARM_HOUR] &= ~(0x3f | 0x80);
 275        reg[M41T80_REG_ALARM_MIN] = 0;
 276        reg[M41T80_REG_ALARM_SEC] = 0;
 277
 278        wbuf[0] = M41T80_REG_ALARM_MON; /* offset into rtc's regs */
 279        reg[M41T80_REG_ALARM_SEC] |= t->time.tm_sec >= 0 ?
 280                bin2bcd(t->time.tm_sec) : 0x80;
 281        reg[M41T80_REG_ALARM_MIN] |= t->time.tm_min >= 0 ?
 282                bin2bcd(t->time.tm_min) : 0x80;
 283        reg[M41T80_REG_ALARM_HOUR] |= t->time.tm_hour >= 0 ?
 284                bin2bcd(t->time.tm_hour) : 0x80;
 285        reg[M41T80_REG_ALARM_DAY] |= t->time.tm_mday >= 0 ?
 286                bin2bcd(t->time.tm_mday) : 0x80;
 287        if (t->time.tm_mon >= 0)
 288                reg[M41T80_REG_ALARM_MON] |= bin2bcd(t->time.tm_mon + 1);
 289        else
 290                reg[M41T80_REG_ALARM_DAY] |= 0x40;
 291
 292        if (i2c_transfer(client->adapter, msgs, 1) != 1) {
 293                dev_err(&client->dev, "write error\n");
 294                return -EIO;
 295        }
 296
 297        if (t->enabled) {
 298                reg[M41T80_REG_ALARM_MON] |= M41T80_ALMON_AFE;
 299                if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
 300                                              reg[M41T80_REG_ALARM_MON]) < 0) {
 301                        dev_err(&client->dev, "write error\n");
 302                        return -EIO;
 303                }
 304        }
 305        return 0;
 306}
 307
 308static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t)
 309{
 310        struct i2c_client *client = to_i2c_client(dev);
 311        u8 buf[M41T80_ALARM_REG_SIZE + 1]; /* all alarm regs and flags */
 312        u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
 313        u8 *reg = buf - M41T80_REG_ALARM_MON;
 314        struct i2c_msg msgs[] = {
 315                {
 316                        .addr   = client->addr,
 317                        .flags  = 0,
 318                        .len    = 1,
 319                        .buf    = dt_addr,
 320                },
 321                {
 322                        .addr   = client->addr,
 323                        .flags  = I2C_M_RD,
 324                        .len    = M41T80_ALARM_REG_SIZE + 1,
 325                        .buf    = buf,
 326                },
 327        };
 328
 329        if (i2c_transfer(client->adapter, msgs, 2) < 0) {
 330                dev_err(&client->dev, "read error\n");
 331                return -EIO;
 332        }
 333        t->time.tm_sec = -1;
 334        t->time.tm_min = -1;
 335        t->time.tm_hour = -1;
 336        t->time.tm_mday = -1;
 337        t->time.tm_mon = -1;
 338        if (!(reg[M41T80_REG_ALARM_SEC] & 0x80))
 339                t->time.tm_sec = bcd2bin(reg[M41T80_REG_ALARM_SEC] & 0x7f);
 340        if (!(reg[M41T80_REG_ALARM_MIN] & 0x80))
 341                t->time.tm_min = bcd2bin(reg[M41T80_REG_ALARM_MIN] & 0x7f);
 342        if (!(reg[M41T80_REG_ALARM_HOUR] & 0x80))
 343                t->time.tm_hour = bcd2bin(reg[M41T80_REG_ALARM_HOUR] & 0x3f);
 344        if (!(reg[M41T80_REG_ALARM_DAY] & 0x80))
 345                t->time.tm_mday = bcd2bin(reg[M41T80_REG_ALARM_DAY] & 0x3f);
 346        if (!(reg[M41T80_REG_ALARM_DAY] & 0x40))
 347                t->time.tm_mon = bcd2bin(reg[M41T80_REG_ALARM_MON] & 0x1f) - 1;
 348        t->time.tm_year = -1;
 349        t->time.tm_wday = -1;
 350        t->time.tm_yday = -1;
 351        t->time.tm_isdst = -1;
 352        t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
 353        t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
 354        return 0;
 355}
 356
 357static struct rtc_class_ops m41t80_rtc_ops = {
 358        .read_time = m41t80_rtc_read_time,
 359        .set_time = m41t80_rtc_set_time,
 360        /*
 361         * XXX - m41t80 alarm functionality is reported broken.
 362         * until it is fixed, don't register alarm functions.
 363         *
 364        .read_alarm = m41t80_rtc_read_alarm,
 365        .set_alarm = m41t80_rtc_set_alarm,
 366        */
 367        .proc = m41t80_rtc_proc,
 368        /*
 369         * See above comment on broken alarm
 370         *
 371        .alarm_irq_enable = m41t80_rtc_alarm_irq_enable,
 372        */
 373};
 374
 375#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
 376static ssize_t m41t80_sysfs_show_flags(struct device *dev,
 377                                struct device_attribute *attr, char *buf)
 378{
 379        struct i2c_client *client = to_i2c_client(dev);
 380        int val;
 381
 382        val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
 383        if (val < 0)
 384                return -EIO;
 385        return sprintf(buf, "%#x\n", val);
 386}
 387static DEVICE_ATTR(flags, S_IRUGO, m41t80_sysfs_show_flags, NULL);
 388
 389static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev,
 390                                struct device_attribute *attr, char *buf)
 391{
 392        struct i2c_client *client = to_i2c_client(dev);
 393        struct m41t80_data *clientdata = i2c_get_clientdata(client);
 394        int val, reg_sqw;
 395
 396        if (!(clientdata->features & M41T80_FEATURE_SQ))
 397                return -EINVAL;
 398
 399        reg_sqw = M41T80_REG_SQW;
 400        if (clientdata->features & M41T80_FEATURE_SQ_ALT)
 401                reg_sqw = M41T80_REG_WDAY;
 402        val = i2c_smbus_read_byte_data(client, reg_sqw);
 403        if (val < 0)
 404                return -EIO;
 405        val = (val >> 4) & 0xf;
 406        switch (val) {
 407        case 0:
 408                break;
 409        case 1:
 410                val = 32768;
 411                break;
 412        default:
 413                val = 32768 >> val;
 414        }
 415        return sprintf(buf, "%d\n", val);
 416}
 417static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
 418                                struct device_attribute *attr,
 419                                const char *buf, size_t count)
 420{
 421        struct i2c_client *client = to_i2c_client(dev);
 422        struct m41t80_data *clientdata = i2c_get_clientdata(client);
 423        int almon, sqw, reg_sqw;
 424        int val = simple_strtoul(buf, NULL, 0);
 425
 426        if (!(clientdata->features & M41T80_FEATURE_SQ))
 427                return -EINVAL;
 428
 429        if (val) {
 430                if (!is_power_of_2(val))
 431                        return -EINVAL;
 432                val = ilog2(val);
 433                if (val == 15)
 434                        val = 1;
 435                else if (val < 14)
 436                        val = 15 - val;
 437                else
 438                        return -EINVAL;
 439        }
 440        /* disable SQW, set SQW frequency & re-enable */
 441        almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
 442        if (almon < 0)
 443                return -EIO;
 444        reg_sqw = M41T80_REG_SQW;
 445        if (clientdata->features & M41T80_FEATURE_SQ_ALT)
 446                reg_sqw = M41T80_REG_WDAY;
 447        sqw = i2c_smbus_read_byte_data(client, reg_sqw);
 448        if (sqw < 0)
 449                return -EIO;
 450        sqw = (sqw & 0x0f) | (val << 4);
 451        if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
 452                                      almon & ~M41T80_ALMON_SQWE) < 0 ||
 453            i2c_smbus_write_byte_data(client, reg_sqw, sqw) < 0)
 454                return -EIO;
 455        if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
 456                                             almon | M41T80_ALMON_SQWE) < 0)
 457                return -EIO;
 458        return count;
 459}
 460static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR,
 461                   m41t80_sysfs_show_sqwfreq, m41t80_sysfs_set_sqwfreq);
 462
 463static struct attribute *attrs[] = {
 464        &dev_attr_flags.attr,
 465        &dev_attr_sqwfreq.attr,
 466        NULL,
 467};
 468static struct attribute_group attr_group = {
 469        .attrs = attrs,
 470};
 471
 472static int m41t80_sysfs_register(struct device *dev)
 473{
 474        return sysfs_create_group(&dev->kobj, &attr_group);
 475}
 476#else
 477static int m41t80_sysfs_register(struct device *dev)
 478{
 479        return 0;
 480}
 481#endif
 482
 483#ifdef CONFIG_RTC_DRV_M41T80_WDT
 484/*
 485 *****************************************************************************
 486 *
 487 * Watchdog Driver
 488 *
 489 *****************************************************************************
 490 */
 491static struct i2c_client *save_client;
 492
 493/* Default margin */
 494#define WD_TIMO 60              /* 1..31 seconds */
 495
 496static int wdt_margin = WD_TIMO;
 497module_param(wdt_margin, int, 0);
 498MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)");
 499
 500static unsigned long wdt_is_open;
 501static int boot_flag;
 502
 503/**
 504 *      wdt_ping:
 505 *
 506 *      Reload counter one with the watchdog timeout. We don't bother reloading
 507 *      the cascade counter.
 508 */
 509static void wdt_ping(void)
 510{
 511        unsigned char i2c_data[2];
 512        struct i2c_msg msgs1[1] = {
 513                {
 514                        .addr   = save_client->addr,
 515                        .flags  = 0,
 516                        .len    = 2,
 517                        .buf    = i2c_data,
 518                },
 519        };
 520        struct m41t80_data *clientdata = i2c_get_clientdata(save_client);
 521
 522        i2c_data[0] = 0x09;             /* watchdog register */
 523
 524        if (wdt_margin > 31)
 525                i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */
 526        else
 527                /*
 528                 * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02)
 529                 */
 530                i2c_data[1] = wdt_margin<<2 | 0x82;
 531
 532        /*
 533         * M41T65 has three bits for watchdog resolution.  Don't set bit 7, as
 534         * that would be an invalid resolution.
 535         */
 536        if (clientdata->features & M41T80_FEATURE_WD)
 537                i2c_data[1] &= ~M41T80_WATCHDOG_RB2;
 538
 539        i2c_transfer(save_client->adapter, msgs1, 1);
 540}
 541
 542/**
 543 *      wdt_disable:
 544 *
 545 *      disables watchdog.
 546 */
 547static void wdt_disable(void)
 548{
 549        unsigned char i2c_data[2], i2c_buf[0x10];
 550        struct i2c_msg msgs0[2] = {
 551                {
 552                        .addr   = save_client->addr,
 553                        .flags  = 0,
 554                        .len    = 1,
 555                        .buf    = i2c_data,
 556                },
 557                {
 558                        .addr   = save_client->addr,
 559                        .flags  = I2C_M_RD,
 560                        .len    = 1,
 561                        .buf    = i2c_buf,
 562                },
 563        };
 564        struct i2c_msg msgs1[1] = {
 565                {
 566                        .addr   = save_client->addr,
 567                        .flags  = 0,
 568                        .len    = 2,
 569                        .buf    = i2c_data,
 570                },
 571        };
 572
 573        i2c_data[0] = 0x09;
 574        i2c_transfer(save_client->adapter, msgs0, 2);
 575
 576        i2c_data[0] = 0x09;
 577        i2c_data[1] = 0x00;
 578        i2c_transfer(save_client->adapter, msgs1, 1);
 579}
 580
 581/**
 582 *      wdt_write:
 583 *      @file: file handle to the watchdog
 584 *      @buf: buffer to write (unused as data does not matter here
 585 *      @count: count of bytes
 586 *      @ppos: pointer to the position to write. No seeks allowed
 587 *
 588 *      A write to a watchdog device is defined as a keepalive signal. Any
 589 *      write of data will do, as we we don't define content meaning.
 590 */
 591static ssize_t wdt_write(struct file *file, const char __user *buf,
 592                         size_t count, loff_t *ppos)
 593{
 594        if (count) {
 595                wdt_ping();
 596                return 1;
 597        }
 598        return 0;
 599}
 600
 601static ssize_t wdt_read(struct file *file, char __user *buf,
 602                        size_t count, loff_t *ppos)
 603{
 604        return 0;
 605}
 606
 607/**
 608 *      wdt_ioctl:
 609 *      @inode: inode of the device
 610 *      @file: file handle to the device
 611 *      @cmd: watchdog command
 612 *      @arg: argument pointer
 613 *
 614 *      The watchdog API defines a common set of functions for all watchdogs
 615 *      according to their available features. We only actually usefully support
 616 *      querying capabilities and current status.
 617 */
 618static int wdt_ioctl(struct file *file, unsigned int cmd,
 619                     unsigned long arg)
 620{
 621        int new_margin, rv;
 622        static struct watchdog_info ident = {
 623                .options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING |
 624                        WDIOF_SETTIMEOUT,
 625                .firmware_version = 1,
 626                .identity = "M41T80 WTD"
 627        };
 628
 629        switch (cmd) {
 630        case WDIOC_GETSUPPORT:
 631                return copy_to_user((struct watchdog_info __user *)arg, &ident,
 632                                    sizeof(ident)) ? -EFAULT : 0;
 633
 634        case WDIOC_GETSTATUS:
 635        case WDIOC_GETBOOTSTATUS:
 636                return put_user(boot_flag, (int __user *)arg);
 637        case WDIOC_KEEPALIVE:
 638                wdt_ping();
 639                return 0;
 640        case WDIOC_SETTIMEOUT:
 641                if (get_user(new_margin, (int __user *)arg))
 642                        return -EFAULT;
 643                /* Arbitrary, can't find the card's limits */
 644                if (new_margin < 1 || new_margin > 124)
 645                        return -EINVAL;
 546    h"616" class="line" name="L6"> 546    h"616" cnt">/* resoluti7me="L644"> 644                if ( */
 638                6dt_disable(void)
class="comment">/* Arbitrary, can't find th class="same="L548"> 548{
 640        case i2c_buf[0x10];
 636                returnMODULE_PARM_DESC" class="sref">MODULE_PARput_user(boot_flag, (int __user *)msgs0[2] = {
 5516/a>                {
 635 claOPTION case addr,
 631   from" nam     lass="sref">__user(boot_flag, (int __user *)flags  = 0,
 642                        return -len    = 1,
i2c_data,
-> 629        switch ( 556<6a>                },
cpruser"                  . *      \nspan class="string">"Watchdog timeout in secon6557"> 5576/a>                {
 547sttring">"Watchdog timeout in secon65 class="sass="sref">addr,
I2C_M_RD,
len    = 1,
-> 629        switch (i2c_buf,
cpruser"                  ."Watchdog timeout in secon662"> 562<6a>                },
 638                 563        };
msgs1[1] = {
 5656/a>                {
 645                        return -addr,
flags  = 0,
 645  NOTTY                 return -len    = 2,
i2c_data,
 570<6a>                },
static int wdt_ioctl(struct file *file, unsigned int  571        };
 619                     unsigned long  572
i26_data[0] = 0x09;
file *msgs0, 2);
 575
__user__user *)i26_data[0] = 0x09;
 618<">wdt_ioctl(struct  * 619         class="sref">__user *)__user__user *)msgs1, 1);
 579}
 * 580
/**
 *  6   wdt_write:
 607
 608 610 *      @file: fi @count: 6ount of bytes
 610 *      @file: fi sref">i26seeks allowed
 5876span clas6="comment"> *
/* Arbitrary, can't find ta keepali6e signal. Any
 500static int             ="li="sref">wdt_ioctls="li            ="li="sr,0.c#L622" id="L622" cla"+code=wdt_ioctl" class="sref">wdt_ioctl(struct 
 */
wdt_ioctls="li            ="li="sransfer(            _r0">"L629"=>                .] &=MINO=options" class=.] &=MINO="L629"> 629        switch (buf,
__user__user *)ppos)
__user 500static uns)9"> 629        switch ( 593{
__user__user *)count) {
 645  BUSY2c_client *wdt_ping();
    6           return 1;
 532        6 name="L567"> 597        }
 *      @file: fi598"> 5986/a>        return 0;
 529 599}
 500static uns. * 600
__user__user *)buf,
 50nonlass claine"  618<">wdt_ioctls="li            ="li="sreclass="line" nam(struct __user *)ppos)
__user *) 593{
 645  NODEV2c_client * 6047/a>        return 0;
 605}
 606
 607<7pan class7"comment">/**
 582 *  7   wdt_ioctl:
 611 *    7de: inode7of the device
 611 *    7ds="line"7to the device
 611 *    7dde=buf" 7chdog command
 529            ="li="sref">wdt_ioctls="li            ="li="sr,0.c#L622" id="L622" cla"+code=wdt_ioctl" class="sref">wdt_ioctl(struct  *

wdt_ioctls="li            ="li="sransfer(            _r0">"L629"=>                .] &=MINO=options" class=.] &=MINO="L629 629        switch (
__user 500static uns)lient * 67pan class7"comment"> */
cmd,
arg)
 607<7s="line" 7ame="L620"> 620{
 582rv;
 582ident = {
 *      @ppos: pointer to the position to7ref">WDIO7_KEEPALIVEPING |
: L584">"> *      @ppos: pointer to the position to7rions for7DIOF_SETTIMEOUT,
 *      @ppos: pointer to the position to7r="line" 7are_version = 1,
 *      @ppos: pointer to the position to7res and c7T80 WTD"
 627        };
 orse0yet dua hr     foomme hr fsck. Thisss="linsuck,tc- facrding to their available features. We only a7ss="line"7name="L628"> 628
 -c-m4itlinptics4itlspan suckan class="comment"> *      querying capabili7e=cmd" cl7ss="sref">cmd) {
 590<7s="sref">7DIOC_GETSUPPORT:
 500static int _blockf="+code=new_manotif/a>_block="sref">wdt_ioctlthisf="+code=new_mathisfile" class="sr19" class="line" namc"li           c"li="sr," class="line" name="L590"> 590<7scode=rv"7ss="sref">ident,
wdt_ioctlL584">           L584">">629 629        switch (EFAULT : 0;
 633
                SYS_DOWN           SYS_DOWN="sr" if (           c"li="sr"=>                SYS_HA="L642"> 642 SYS_HA="">629 629        switch (:
Wlass="li id="L590" class="line" name="L590"> 590<7s="line" 7C_GETBOOTSTATUS:
 547sttring">"Watchdog timeout in secon7de=arg" c7ass="sref">arg);
:
wdt_ping();
    7           return 0;
 5007DIOC_SETTIMEOUT:
                THIS_MODULEs="line" name="THIS_MODULE="sr," class="line" name="L590"> 590<7de=arg" c7ass="sref">arg))
                ic  590<7d class="7s="sref">EFAULT;
                ic  590<7ds="line"79;s limits */
                ic  590<7dions for7margin > 124)
 50atic uns./r>                ic  500 590<7d="line" 7s="sref">EINVAL;
 590<7de=arg" c7"comment"> */
                no_"llasss="line" name="no_"llass uns," class="line" name="L590"> 590<7ds="sref"7dt_disable(void)
 548{
i2c_buf[0x10];
 61f="+code=new_mamisc0"> 61vers/1t80.c#L618" id="L60">           ="L60"> uns.msgs0[2] = {
                .] &=MINO=options" class=.] &=MINO="L62," class="line" name="L590"> 590<7551"> 5517/a>                {
 590<75 class="7ass="sref">addr,
 50fops uns.__user 500 590<75s="line"7ref">flags  = 0,
len    = 1,
i2c_data,
 532        756"> 556<7a>                },
tc- ordine" " class="line" name="L532"> 532        75s="sref"7/a>                {
 *      querying capabili75 class="7ass="sref">addr,
 590<7RD" class7"sref">I2C_M_RD,
_blockf="+code=new_manotif/a>_block="sre1t80.c#L618" id="L6notifiode=boot_flag" cl="L6notifiod uns.len    = 1,
_call unsr>                ic  500 590<7buf" clas7="sref">i2c_buf,
 562<7a>                },
 590<7Rs="line"7"> 563        };
 590<7R class="7ef">msgs1[1] = {
 532        7565"> 5657/a>                {
 532        756"> 556<7ass="sref">addr,
 5877" class="7ref">flags  = 0,
 *      @file: f7e class="7ref">len    = 2,
 *      @file: f7ta" class7"sref">i2c_data,
 532        770"> 570<7a>                },
 590<7name="L577"> 571        };
static int wdt_ioctlde=i2c_transfer" clasde=i2c="sr," class="line" name="L590"> 590<7ss="line"7name="L572"> 572
 61_iref="+code=ssize name"> 61_ir="sref">wdt_ioctlsref="+code=ssize >">629 629        switch (i27_data[0] = 0x09;
msgs0, 2);
file * 67ss="line"7name="L575"> 575
 61f="+code=new_manam_0"> 61="sref">wdt_ioctlnamf="+code=new_manam unsr>                NUL"L645"> 645 NUL"2c_client *i27_data[0] = 0x09;
wdt_ioctlde=i2c="lif="+code=new_made=i2c="li unsr>                NUL"L645"> 645 NUL"2c_client *msgs1, 1);
 579}
wdt_ioctlde=i2c_transfer" clasde=i2c="sransfer(( 570<7name="L580"> 580
6299"> 629        switch (/**
 645  NODEV2c_client * *  7   wdt_write:
i27 the watchdog


user" class="sref">c0">user" 618__user(           0"> uns," class="line" name="L590"> 590<7 sref">i27seeks allowed
           DRV_VERSION="sre1            ."Watchdog timeout in secon7span clas7="comment"> *

                kzallocs="line" name="kzalloc unswdt_ioctlde=i2c="lif="+code=new_made=i2c="li uns)" r( 645 GFP_KERNE" uns)ring">"Watchdog timeout in secon7define co7tent meaning.
 629        switch ( */
 645  NOMEM2c_client *buf,
ppos)
__user *) 593{
 590<7unt" clas7="sref">count) {
( 50f615 unsr>                sref="+code=ssize >">62ansfer(wdt_ping();
wdt_ioctlde=i2c_transfer" clasde=i2c="sr" file * *i27           return 1;
 597        }
 61_-mgistL5f="+code=new_manam_0"> 61_-mgistL5 618<">wdt_ioctlde=i2c_transfer" clasde=i2c="sransfer(__user(           0"> uns," class="line" name="L590"> 590<7598"> 5987/a>        return 0;
__user 50="L575_" i_ops="sr" file * * 599}
wdt_ioctlnamf="+code=new_manam uns99"> 629        switch ( 600
wdt_ioctlnamf="+code=new_manam uns9lient *buf,
wdt_ioctlnamf="+code=new_manam unsr>                NUL"L645"> 645 NUL"2c_client *ppos)
 593{
 6048/a>        return 0;
 605}
(                namf="+code=new_manam unslient */**
 590<8ent"> *  8   wdt_ioctl:
wdt_ioctlde=i2c_transfer" clasde=i2c="sr" file * *


 642 M=iden_ALHOU=_H" uns)"> 629        switch ( *
( 50f615 unsr ntdata-> 642 M=iden_FEATURE_H" uns)"> 629        switch ( 6048all watchdogs
 64="letimef="+code=new_ma="L575_> 64="letime 618<">wdt_ioctlde=i2c_transfer" clasde=i2c="sr" lass="sref">__user *
user" class="sref">c0">user" 618__user(           0"> uns,e1            ."Watchdog timeout in secon8ies and c8rrent status.
user" class="sref">c0">user" 618__user(           0"> uns,ing">"Watchdog timeout in secon8ian class8"comment"> */
"Watchdog timeout in secon8int"> *  8lass="sref">cmd,
"Watchdog timeout in secon8ode=arg" 8lass="sref">arg)
"Watchdog timeout in secon8os="line"8ame="L620"> 620{
 50tm_mo vers/+ 1" file * *"Watchdog timeout in secon8+code=rv"8class="sref">rv;
 *ident = {
WDIO8_KEEPALIVEPING |
wdt_ioctlde=i2c_transfer" clasde=i2c="sr"rtc/rtc-m41t80.c#L605" id="L605" cla8r04"> 6048DIOF_SETTIMEOUT,
 = 1,
 642 M=iden_ALHOU=_H" uns)"     09 629        switch ( 627        };
 *  8name="L628"> 628
cmd) {
 590<8s="sref">8DIOC_GETSUPPORT:
wdt_ioctlde=i2c_transfer" clasde=i2c="sr" file * *ident,
EFAULT : 0;
 633
 590<8"ions for8WDIOC_GETSTATUS:
 642 M=iden_SEC_S" uns)"> 629        switch (:
wdt_ioctlde=i2c_transfer" clasde=i2c="sr" file *arg);
 642 M=iden_SEC_S" uns)"     09 629        switch ( *  8ref">wdt_ping();
    8           return 0;
8DIOC_SETTIMEOUT:
__user(           0"> uns9lient *arg))
EFAULT;

 590<8dions for8margin > 124)
 590<8d="line" 8s="sref">EINVAL;
( 50f615 unsr ntdata-> 642 M=iden_FEATURE_H" uns)"> 629        switch ( */
 id="L624" classde=i2c_transfer" clasde=i2c="srlient *(void)
__user           ="L60"> uns9lient * *  8ame="L548"> 548{
i2c_buf[0x10];
msgs0[2] = {
__user * 5518/a>                {
 629        switch (addr,
__user           ="L60"> uns9lient *flags  = 0,
len    = 1,
i2c_data,
                },
 685 class="8ass="sref">addr,
I2C_M_RD,
len    = 1,
i2c_buf,
uer5f="+code=new_ma0">uer5 618__user(           0"> uns,e1            ."Watchdog timeout in secon862"> 562<8a>                },
 563        };
msgs1[1] = {
                {
uer5f="+code=new_ma0">uer5 618__user(           0"> uns,e1            ."Watchdog timeout in secon86e=arg" c8ass="sref">addr,
flags  = 0,
len    = 2,
i2c_data,
 61_un-mgistL5f="+code=new_manam_0"> 61_un-mgistL5 618<">wdt_ioctl-amf="+code=new_manam uns9lient * 571        };
wdt_ioctlde=i2c="lif="+code=new_made=i2c="li uns)lient * 572
[0] = 0x09;
msgs0, 2);
 575
static int wdt_ioctlde=i2c_transfer" clasde=i2c="sr9/rtc/rtc-m41t80.c#L499" id="L499" cl8te=arg" c8_data[0] = 0x09;
wdt_ioctlde=i2c="lif="+code=new_made=i2c="li unsr>                s2coget_de=i2c="lif="+code=new_ma22coget_de=i2c="li 618<">wdt_ioctlde=i2c_transfer" clasde=i2c="sr)lient *msgs1, 1);
 61f="+code=new_manam_0"> 61="sref">wdt_ioctlnamf="+code=new_manam unsr>                de=i2c="lif="+code=new_made=i2c="li unsansfer( * 579}
 570<8name="L580"> 580
 590<8pan class8"comment">/**
( 50f615 unsr ntdata-> 642 M=iden_FEATURE_H" uns)"> 629        switch ( *  8   wdt_write:
__user           ="L60"> uns9lient *i28 the watchdog
__user__user *
i28seeks allowed
 *
 61_un-mgistL5f="+code=new_manam_0"> 61_un-mgistL5 618<">wdt_ioctl-amf="+code=new_manam uns9lient *wdt_ioctlde=i2c="lif="+code=new_made=i2c="li uns)lient * */
 68ode=buf" 8lass="sref">buf,
ppos)
i28ame="L593"> 593{
 > 629        switch (count) {
 > 629        switch (wdt_ping();
"Watchdog timeout in secon8esref">i28           return 1;
"Watchdog timeout in secon8epan clas87"> 597        }
 id="L624" class="L575_prob1f="+code=new_mam"L575_prob1 uns,ing">"Watchdog timeout in secon8e keepali8/a>        return 0;
 id="L624" class="L575_removef="+code=new_ma="L575_remove 618,ing">"Watchdog timeout in secon9s="line" 9ame="L599"> 599}
"Watchdog timeout in secon9s1"line" 9acomment"> */
buf,
ppos)
wdt_ioctlm"L575_=t80.cf="+code=new_mam"L575_=t80.c uns)lient * 593{
 590<9604"> 6049/a>        return 0;
"Watchdog timeout in secon9s="line" 9ame="L605"> 605}
"Watchdog timeout in secon9s7"line" 9a          return 1;
"Watchdog timeout in secon9s8"line" 9a"> 597        }
wdt_ioctlDRV_VERSION           DRV_VERSION="srtring">"Watchdog timeout in secon9s9"line" 9aa>        return 0;
"http://sourceforge.net/projects/lxcf>LXR=643"unss= 618, this experi" clal v nass= by0ng">"mailto:lxc@retux.no">lxc@retux.no 618.
lxc.retux.no kindly hostLd by0ng">"http://www.redpill-retpro.no">Redpill Letpro AS 618, providineof Letux clasult hr and atiraclass ser> 61s since 1995.