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