linux/drivers/rtc/rtc-isl12022.c
<<
>>
Prefs
   1/*
   2 * An I2C driver for the Intersil ISL 12022
   3 *
   4 * Author: Roman Fietze <roman.fietze@telemotive.de>
   5 *
   6 * Based on the Philips PCF8563 RTC
   7 * by Alessandro Zummo <a.zummo@towertech.it>.
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License version
  11 * 2 as published by the Free Software Foundation.
  12 */
  13
  14#include <linux/i2c.h>
  15#include <linux/bcd.h>
  16#include <linux/rtc.h>
  17#include <linux/slab.h>
  18#include <linux/module.h>
  19
  20#define DRV_VERSION "0.1"
  21
  22/* ISL register offsets */
  23#define ISL12022_REG_SC         0x00
  24#define ISL12022_REG_MN         0x01
  25#define ISL12022_REG_HR         0x02
  26#define ISL12022_REG_DT         0x03
  27#define ISL12022_REG_MO         0x04
  28#define ISL12022_REG_YR         0x05
  29#define ISL12022_REG_DW         0x06
  30
  31#define ISL12022_REG_SR         0x07
  32#define ISL12022_REG_INT        0x08
  33
  34/* ISL register bits */
  35#define ISL12022_HR_MIL         (1 << 7)        /* military or 24 hour time */
  36
  37#define ISL12022_SR_LBAT85      (1 << 2)
  38#define ISL12022_SR_LBAT75      (1 << 1)
  39
  40#define ISL12022_INT_WRTC       (1 << 6)
  41
  42
  43static struct i2c_driver isl12022_driver;
  44
  45struct isl12022 {
  46        struct rtc_device *rtc;
  47
  48        bool write_enabled;     /* true if write enable is set */
  49};
  50
  51
  52static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
  53                              uint8_t *data, size_t n)
  54{
  55        struct i2c_msg msgs[] = {
  56                {
  57                        .addr   = client->addr,
  58                        .flags  = 0,
  59                        .len    = 1,
  60                        .buf    = data
  61                },              /* setup read ptr */
  62                {
  63                        .addr   = client->addr,
  64                        .flags  = I2C_M_RD,
  65                        .len    = n,
  66                        .buf    = data
  67                }
  68        };
  69
  70        int ret;
  71
  72        data[0] = reg;
  73        ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
  74        if (ret != ARRAY_SIZE(msgs)) {
  75                dev_err(&client->dev, "%s: read error, ret=%d\n",
  76                        __func__, ret);
  77                return -EIO;
  78        }
  79
  80        return 0;
  81}
  82
  83
  84static int isl12022_write_reg(struct i2c_client *client,
  85                              uint8_t reg, uint8_t val)
  86{
  87        uint8_t data[2] = { reg, val };
  88        int err;
  89
  90        err = i2c_master_send(client, data, sizeof(data));
  91        if (err != sizeof(data)) {
  92                dev_err(&client->dev,
  93                        "%s: err=%d addr=%02x, data=%02x\n",
  94                        __func__, err, data[0], data[1]);
  95                return -EIO;
  96        }
  97
  98        return 0;
  99}
 100
 101
 102/*
 103 * In the routines that deal directly with the isl12022 hardware, we use
 104 * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
 105 */
 106static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 107{
 108        uint8_t buf[ISL12022_REG_INT + 1];
 109        int ret;
 110
 111        ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
 112        if (ret)
 113                return ret;
 114
 115        if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
 116                dev_warn(&client->dev,
 117                         "voltage dropped below %u%%, "
 118                         "date and time is not reliable.\n",
 119                         buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
 120        }
 121
 122        dev_dbg(&client->dev,
 123                "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
 124                "mday=%02x, mon=%02x, year=%02x, wday=%02x, "
 125                "sr=%02x, int=%02x",
 126                __func__,
 127                buf[ISL12022_REG_SC],
 128                buf[ISL12022_REG_MN],
 129                buf[ISL12022_REG_HR],
 130                buf[ISL12022_REG_DT],
 131                buf[ISL12022_REG_MO],
 132                buf[ISL12022_REG_YR],
 133                buf[ISL12022_REG_DW],
 134                buf[ISL12022_REG_SR],
 135                buf[ISL12022_REG_INT]);
 136
 137        tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
 138        tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
 139        tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
 140        tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
 141        tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
 142        tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
 143        tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
 144
 145        dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
 146                "mday=%d, mon=%d, year=%d, wday=%d\n",
 147                __func__,
 148                tm->tm_sec, tm->tm_min, tm->tm_hour,
 149                tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
 150
 151        /* The clock can give out invalid datetime, but we cannot return
 152         * -EINVAL otherwise hwclock will refuse to set the time on bootup. */
 153        if (rtc_valid_tm(tm) < 0)
 154                dev_err(&client->dev, "retrieved date and time is invalid.\n");
 155
 156        return 0;
 157}
 158
 159static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 160{
 161        struct isl12022 *isl12022 = i2c_get_clientdata(client);
 162        size_t i;
 163        int ret;
 164        uint8_t buf[ISL12022_REG_DW + 1];
 165
 166        dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
 167                "mday=%d, mon=%d, year=%d, wday=%d\n",
 168                __func__,
 169                tm->tm_sec, tm->tm_min, tm->tm_hour,
 170                tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
 171
 172        if (!isl12022->write_enabled) {
 173
 174                ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
 175                if (ret)
 176                        return ret;
 177
 178                /* Check if WRTC (write rtc enable) is set factory default is
 179                 * 0 (not set) */
 180                if (!(buf[0] & ISL12022_INT_WRTC)) {
 181                        dev_info(&client->dev,
 182                                 "init write enable and 24 hour format\n");
 183
 184                        /* Set the write enable bit. */
 185                        ret = isl12022_write_reg(client,
 186                                                 ISL12022_REG_INT,
 187                                                 buf[0] | ISL12022_INT_WRTC);
 188                        if (ret)
 189                                return ret;
 190
 191                        /* Write to any RTC register to start RTC, we use the
 192                         * HR register, setting the MIL bit to use the 24 hour
 193                         * format. */
 194                        ret = isl12022_read_regs(client, ISL12022_REG_HR,
 195                                                 buf, 1);
 196                        if (ret)
 197                                return ret;
 198
 199                        ret = isl12022_write_reg(client,
 200                                                 ISL12022_REG_HR,
 201                                                 buf[0] | ISL12022_HR_MIL);
 202                        if (ret)
 203                                return ret;
 204                }
 205
 206                isl12022->write_enabled = 1;
 207        }
 208
 209        /* hours, minutes and seconds */
 210        buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
 211        buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
 212        buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
 213
 214        buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
 215
 216        /* month, 1 - 12 */
 217        buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
 218
 219        /* year and century */
 220        buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
 221
 222        buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
 223
 224        /* write register's data */
 225        for (i = 0; i < ARRAY_SIZE(buf); i++) {
 226                ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
 227                                         buf[ISL12022_REG_SC + i]);
 228                if (ret)
 229                        return -EIO;
 230        }
 231
 232        return 0;
 233}
 234
 235static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
 236{
 237        return isl12022_get_datetime(to_i2c_client(dev), tm);
 238}
 239
 240static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
 241{
 242        return isl12022_set_datetime(to_i2c_client(dev), tm);
 243}
 244
 245static const struct rtc_class_ops isl12022_rtc_ops = {
 246        .read_time      = isl12022_rtc_read_time,
 247        .set_time       = isl12022_rtc_set_time,
 248};
 249
 250static int isl12022_probe(struct i2c_client *client,
 251                          const struct i2c_device_id *id)
 252{
 253        struct isl12022 *isl12022;
 254
 255        int ret = 0;
 256
 257        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 258                return -ENODEV;
 259
 260        isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL);
 261        if (!isl12022)
 262                return -ENOMEM;
 263
 264        dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n");
 265
 266        i2c_set_clientdata(client, isl12022);
 267
 268        isl12022->rtc = rtc_device_register(isl12022_driver.driver.name,
 269                                            &client->dev,
 270                                            &isl12022_rtc_ops,
 271                                            THIS_MODULE);
 272
 273        if (IS_ERR(isl12022->rtc)) {
 274                ret = PTR_ERR(isl12022->rtc);
 275                goto exit_kfree;
 276        }
 277
 278        return 0;
 279
 280exit_kfree:
 281        kfree(isl12022);
 282
 283        return ret;
 284}
 285
 286static int isl12022_remove(struct i2c_client *client)
 287{
 288        struct isl12022 *isl12022 = i2c_get_clientdata(client);
 289
 290        rtc_device_unregister(isl12022->rtc);
 291        kfree(isl12022);
 292
 293        return 0;
 294}
 295
 296static const struct i2c_device_id isl12022_id[] = {
 297        { "isl12022", 0 },
 298        { "rtc8564", 0 },
 299        { }
 300};
 301MODULE_DEVICE_TABLE(i2c, isl12022_id);
 302
 303static struct i2c_driver isl12022_driver = {
 304        .driver         = {
 305                .name   = "rtc-isl12022",
 306        },
 307        .probe          = isl12022_probe,
 308        .remove         = isl12022_remove,
 309        .id_table       = isl12022_id,
 310};
 311
 312module_i2c_driver(isl12022_driver);
 313
 314MODULE_AUTHOR("roman.fietze@telemotive.de");
 315MODULE_DESCRIPTION("ISL 12022 RTC driver");
 316MODULE_LICENSE("GPL");
 317MODULE_VERSION(DRV_VERSION);
 318
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.