linux/drivers/rtc/rtc-s35390a.c
<<
>>
Prefs
   1/*
   2 * Seiko Instruments S-35390A RTC Driver
   3 *
   4 * Copyright (c) 2007 Byron Bradley
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/rtc.h>
  14#include <linux/i2c.h>
  15#include <linux/bitrev.h>
  16#include <linux/bcd.h>
  17#include <linux/slab.h>
  18
  19#define S35390A_CMD_STATUS1     0
  20#define S35390A_CMD_STATUS2     1
  21#define S35390A_CMD_TIME1       2
  22
  23#define S35390A_BYTE_YEAR       0
  24#define S35390A_BYTE_MONTH      1
  25#define S35390A_BYTE_DAY        2
  26#define S35390A_BYTE_WDAY       3
  27#define S35390A_BYTE_HOURS      4
  28#define S35390A_BYTE_MINS       5
  29#define S35390A_BYTE_SECS       6
  30
  31#define S35390A_FLAG_POC        0x01
  32#define S35390A_FLAG_BLD        0x02
  33#define S35390A_FLAG_24H        0x40
  34#define S35390A_FLAG_RESET      0x80
  35#define S35390A_FLAG_TEST       0x01
  36
  37struct s35390a {
  38        struct i2c_client *client[8];
  39        struct rtc_device *rtc;
  40        int twentyfourhour;
  41};
  42
  43static int s35390a_set_reg(struct s35390a *s35390a, int reg, char *buf, int len)
  44{
  45        struct i2c_client *client = s35390a->client[reg];
  46        struct i2c_msg msg[] = {
  47                { client->addr, 0, len, buf },
  48        };
  49
  50        if ((i2c_transfer(client->adapter, msg, 1)) != 1)
  51                return -EIO;
  52
  53        return 0;
  54}
  55
  56static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
  57{
  58        struct i2c_client *client = s35390a->client[reg];
  59        struct i2c_msg msg[] = {
  60                { client->addr, I2C_M_RD, len, buf },
  61        };
  62
  63        if ((i2c_transfer(client->adapter, msg, 1)) != 1)
  64                return -EIO;
  65
  66        return 0;
  67}
  68
  69static int s35390a_reset(struct s35390a *s35390a)
  70{
  71        char buf[1];
  72
  73        if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0)
  74                return -EIO;
  75
  76        if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD)))
  77                return 0;
  78
  79        buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H);
  80        buf[0] &= 0xf0;
  81        return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
  82}
  83
  84static int s35390a_disable_test_mode(struct s35390a *s35390a)
  85{
  86        char buf[1];
  87
  88        if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)) < 0)
  89                return -EIO;
  90
  91        if (!(buf[0] & S35390A_FLAG_TEST))
  92                return 0;
  93
  94        buf[0] &= ~S35390A_FLAG_TEST;
  95        return s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf));
  96}
  97
  98static char s35390a_hr2reg(struct s35390a *s35390a, int hour)
  99{
 100        if (s35390a->twentyfourhour)
 101                return BIN2BCD(hour);
 102
 103        if (hour < 12)
 104                return BIN2BCD(hour);
 105
 106        return 0x40 | BIN2BCD(hour - 12);
 107}
 108
 109static int s35390a_reg2hr(struct s35390a *s35390a, char reg)
 110{
 111        unsigned hour;
 112
 113        if (s35390a->twentyfourhour)
 114                return BCD2BIN(reg & 0x3f);
 115
 116        hour = BCD2BIN(reg & 0x3f);
 117        if (reg & 0x40)
 118                hour += 12;
 119
 120        return hour;
 121}
 122
 123static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 124{
 125        struct s35390a  *s35390a = i2c_get_clientdata(client);
 126        int i, err;
 127        char buf[7];
 128
 129        dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, "
 130                "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,
 131                tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
 132                tm->tm_wday);
 133
 134        buf[S35390A_BYTE_YEAR] = BIN2BCD(tm->tm_year - 100);
 135        buf[S35390A_BYTE_MONTH] = BIN2BCD(tm->tm_mon + 1);
 136        buf[S35390A_BYTE_DAY] = BIN2BCD(tm->tm_mday);
 137        buf[S35390A_BYTE_WDAY] = BIN2BCD(tm->tm_wday);
 138        buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour);
 139        buf[S35390A_BYTE_MINS] = BIN2BCD(tm->tm_min);
 140        buf[S35390A_BYTE_SECS] = BIN2BCD(tm->tm_sec);
 141
 142        /* This chip expects the bits of each byte to be in reverse order */
 143        for (i = 0; i < 7; ++i)
 144                buf[i] = bitrev8(buf[i]);
 145
 146        err = s35390a_set_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
 147
 148        return err;
 149}
 150
 151static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 152{
 153        struct s35390a *s35390a = i2c_get_clientdata(client);
 154        char buf[7];
 155        int i, err;
 156
 157        err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
 158        if (err < 0)
 159                return err;
 160
 161        /* This chip returns the bits of each byte in reverse order */
 162        for (i = 0; i < 7; ++i)
 163                buf[i] = bitrev8(buf[i]);
 164
 165        tm->tm_sec = BCD2BIN(buf[S35390A_BYTE_SECS]);
 166        tm->tm_min = BCD2BIN(buf[S35390A_BYTE_MINS]);
 167        tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]);
 168        tm->tm_wday = BCD2BIN(buf[S35390A_BYTE_WDAY]);
 169        tm->tm_mday = BCD2BIN(buf[S35390A_BYTE_DAY]);
 170        tm->tm_mon = BCD2BIN(buf[S35390A_BYTE_MONTH]) - 1;
 171        tm->tm_year = BCD2BIN(buf[S35390A_BYTE_YEAR]) + 100;
 172
 173        dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, "
 174                "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,
 175                tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
 176                tm->tm_wday);
 177
 178        return rtc_valid_tm(tm);
 179}
 180
 181static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
 182{
 183        return s35390a_get_datetime(to_i2c_client(dev), tm);
 184}
 185
 186static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
 187{
 188        return s35390a_set_datetime(to_i2c_client(dev), tm);
 189}
 190
 191static const struct rtc_class_ops s35390a_rtc_ops = {
 192        .read_time      = s35390a_rtc_read_time,
 193        .set_time       = s35390a_rtc_set_time,
 194};
 195
 196static struct i2c_driver s35390a_driver;
 197
 198static int s35390a_probe(struct i2c_client *client)
 199{
 200        int err;
 201        unsigned int i;
 202        struct s35390a *s35390a;
 203        struct rtc_time tm;
 204        char buf[1];
 205
 206        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 207                err = -ENODEV;
 208                goto exit;
 209        }
 210
 211        s35390a = kzalloc(sizeof(struct s35390a), GFP_KERNEL);
 212        if (!s35390a) {
 213                err = -ENOMEM;
 214                goto exit;
 215        }
 216
 217        s35390a->client[0] = client;
 218        i2c_set_clientdata(client, s35390a);
 219
 220        /* This chip uses multiple addresses, use dummy devices for them */
 221        for (i = 1; i < 8; ++i) {
 222                s35390a->client[i] = i2c_new_dummy(client->adapter,
 223                                        client->addr + i, "rtc-s35390a");
 224                if (!s35390a->client[i]) {
 225                        dev_err(&client->dev, "Address %02x unavailable\n",
 226                                                client->addr + i);
 227                        err = -EBUSY;
 228                        goto exit_dummy;
 229                }
 230        }
 231
 232        err = s35390a_reset(s35390a);
 233        if (err < 0) {
 234                dev_err(&client->dev, "error resetting chip\n");
 235                goto exit_dummy;
 236        }
 237
 238        err = s35390a_disable_test_mode(s35390a);
 239        if (err < 0) {
 240                dev_err(&client->dev, "error disabling test mode\n");
 241                goto exit_dummy;
 242        }
 243
 244        err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
 245        if (err < 0) {
 246                dev_err(&client->dev, "error checking 12/24 hour mode\n");
 247                goto exit_dummy;
 248        }
 249        if (buf[0] & S35390A_FLAG_24H)
 250                s35390a->twentyfourhour = 1;
 251        else
 252                s35390a->twentyfourhour = 0;
 253
 254        if (s35390a_get_datetime(client, &tm) < 0)
 255                dev_warn(&client->dev, "clock needs to be set\n");
 256
 257        s35390a->rtc = rtc_device_register(s35390a_driver.driver.name,
 258                                &client->dev, &s35390a_rtc_ops, THIS_MODULE);
 259
 260        if (IS_ERR(s35390a->rtc)) {
 261                err = PTR_ERR(s35390a->rtc);
 262                goto exit_dummy;
 263        }
 264        return 0;
 265
 266exit_dummy:
 267        for (i = 1; i < 8; ++i)
 268                if (s35390a->client[i])
 269                        i2c_unregister_device(s35390a->client[i]);
 270        kfree(s35390a);
 271        i2c_set_clientdata(client, NULL);
 272
 273exit:
 274        return err;
 275}
 276
 277static int s35390a_remove(struct i2c_client *client)
 278{
 279        unsigned int i;
 280
 281        struct s35390a *s35390a = i2c_get_clientdata(client);
 282        for (i = 1; i < 8; ++i)
 283                if (s35390a->client[i])
 284                        i2c_unregister_device(s35390a->client[i]);
 285
 286        rtc_device_unregister(s35390a->rtc);
 287        kfree(s35390a);
 288        i2c_set_clientdata(client, NULL);
 289
 290        return 0;
 291}
 292
 293static struct i2c_driver s35390a_driver = {
 294        .driver         = {
 295                .name   = "rtc-s35390a",
 296        },
 297        .probe          = s35390a_probe,
 298        .remove         = s35390a_remove,
 299};
 300
 301static int __init s35390a_rtc_init(void)
 302{
 303        return i2c_add_driver(&s35390a_driver);
 304}
 305
 306static void __exit s35390a_rtc_exit(void)
 307{
 308        i2c_del_driver(&s35390a_driver);
 309}
 310
 311MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>");
 312MODULE_DESCRIPTION("S35390A RTC driver");
 313MODULE_LICENSE("GPL");
 314
 315module_init(s35390a_rtc_init);
 316module_exit(s35390a_rtc_exit);
 317
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.