linux/drivers/rtc/rtc-m41t93.c
<<
>>
Prefs
   1/*
   2 *
   3 * Driver for ST M41T93 SPI RTC
   4 *
   5 * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/bcd.h>
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/platform_device.h>
  16#include <linux/rtc.h>
  17#include <linux/spi/spi.h>
  18
  19#define M41T93_REG_SSEC                 0
  20#define M41T93_REG_ST_SEC               1
  21#define M41T93_REG_MIN                  2
  22#define M41T93_REG_CENT_HOUR            3
  23#define M41T93_REG_WDAY                 4
  24#define M41T93_REG_DAY                  5
  25#define M41T93_REG_MON                  6
  26#define M41T93_REG_YEAR                 7
  27
  28
  29#define M41T93_REG_ALM_HOUR_HT          0xc
  30#define M41T93_REG_FLAGS                0xf
  31
  32#define M41T93_FLAG_ST                  (1 << 7)
  33#define M41T93_FLAG_OF                  (1 << 2)
  34#define M41T93_FLAG_BL                  (1 << 4)
  35#define M41T93_FLAG_HT                  (1 << 6)
  36
  37static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
  38{
  39        u8 buf[2];
  40
  41        /* MSB must be '1' to write */
  42        buf[0] = addr | 0x80;
  43        buf[1] = data;
  44
  45        return spi_write(spi, buf, sizeof(buf));
  46}
  47
  48static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
  49{
  50        struct spi_device *spi = to_spi_device(dev);
  51        u8 buf[9] = {0x80};        /* write cmd + 8 data bytes */
  52        u8 * const data = &buf[1]; /* ptr to first data byte */
  53
  54        dev_dbg(dev, "%s secs=%d, mins=%d, "
  55                "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
  56                "write", tm->tm_sec, tm->tm_min,
  57                tm->tm_hour, tm->tm_mday,
  58                tm->tm_mon, tm->tm_year, tm->tm_wday);
  59
  60        if (tm->tm_year < 100) {
  61                dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n");
  62                return -EINVAL;
  63        }
  64
  65        data[M41T93_REG_SSEC]           = 0;
  66        data[M41T93_REG_ST_SEC]         = bin2bcd(tm->tm_sec);
  67        data[M41T93_REG_MIN]            = bin2bcd(tm->tm_min);
  68        data[M41T93_REG_CENT_HOUR]      = bin2bcd(tm->tm_hour) |
  69                                                ((tm->tm_year/100-1) << 6);
  70        data[M41T93_REG_DAY]            = bin2bcd(tm->tm_mday);
  71        data[M41T93_REG_WDAY]           = bin2bcd(tm->tm_wday + 1);
  72        data[M41T93_REG_MON]            = bin2bcd(tm->tm_mon + 1);
  73        data[M41T93_REG_YEAR]           = bin2bcd(tm->tm_year % 100);
  74
  75        return spi_write(spi, buf, sizeof(buf));
  76}
  77
  78
  79static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
  80{
  81        struct spi_device *spi = to_spi_device(dev);
  82        const u8 start_addr = 0;
  83        u8 buf[8];
  84        int century_after_1900;
  85        int tmp;
  86        int ret = 0;
  87
  88        /* Check status of clock. Two states must be considered:
  89           1. halt bit (HT) is set: the clock is running but update of readout
  90              registers has been disabled due to power failure. This is normal
  91              case after poweron. Time is valid after resetting HT bit.
  92           2. oscillator fail bit (OF) is set. Oscillator has be stopped and
  93              time is invalid:
  94              a) OF can be immeditely reset.
  95              b) OF cannot be immediately reset: oscillator has to be restarted.
  96        */
  97        tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
  98        if (tmp < 0)
  99                return tmp;
 100
 101        if (tmp & M41T93_FLAG_HT) {
 102                dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n");
 103                m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT,
 104                               tmp & ~M41T93_FLAG_HT);
 105        }
 106
 107        tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
 108        if (tmp < 0)
 109                return tmp;
 110
 111        if (tmp & M41T93_FLAG_OF) {
 112                ret = -EINVAL;
 113                dev_warn(&spi->dev, "OF bit is set, resetting.\n");
 114                m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
 115
 116                tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
 117                if (tmp < 0)
 118                        return tmp;
 119                else if (tmp & M41T93_FLAG_OF) {
 120                        u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
 121
 122                        dev_warn(&spi->dev,
 123                                 "OF bit is still set, kickstarting clock.\n");
 124                        m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
 125                        reset_osc &= ~M41T93_FLAG_ST;
 126                        m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
 127                }
 128        }
 129
 130        if (tmp & M41T93_FLAG_BL)
 131                dev_warn(&spi->dev, "BL bit is set, replace battery.\n");
 132
 133        /* read actual time/date */
 134        tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf));
 135        if (tmp < 0)
 136                return tmp;
 137
 138        tm->tm_sec      = bcd2bin(buf[M41T93_REG_ST_SEC]);
 139        tm->tm_min      = bcd2bin(buf[M41T93_REG_MIN]);
 140        tm->tm_hour     = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f);
 141        tm->tm_mday     = bcd2bin(buf[M41T93_REG_DAY]);
 142        tm->tm_mon      = bcd2bin(buf[M41T93_REG_MON]) - 1;
 143        tm->tm_wday     = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1;
 144
 145        century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1;
 146        tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100;
 147
 148        dev_dbg(dev, "%s secs=%d, mins=%d, "
 149                "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
 150                "read", tm->tm_sec, tm->tm_min,
 151                tm->tm_hour, tm->tm_mday,
 152                tm->tm_mon, tm->tm_year, tm->tm_wday);
 153
 154        return ret < 0 ? ret : rtc_valid_tm(tm);
 155}
 156
 157
 158static const struct rtc_class_ops m41t93_rtc_ops = {
 159        .read_time      = m41t93_get_time,
 160        .set_time       = m41t93_set_time,
 161};
 162
 163static struct spi_driver m41t93_driver;
 164
 165static int __devinit m41t93_probe(struct spi_device *spi)
 166{
 167        struct rtc_device *rtc;
 168        int res;
 169
 170        spi->bits_per_word = 8;
 171        spi_setup(spi);
 172
 173        res = spi_w8r8(spi, M41T93_REG_WDAY);
 174        if (res < 0 || (res & 0xf8) != 0) {
 175                dev_err(&spi->dev, "not found 0x%x.\n", res);
 176                return -ENODEV;
 177        }
 178
 179        rtc = rtc_device_register(m41t93_driver.driver.name,
 180                &spi->dev, &m41t93_rtc_ops, THIS_MODULE);
 181        if (IS_ERR(rtc))
 182                return PTR_ERR(rtc);
 183
 184        dev_set_drvdata(&spi->dev, rtc);
 185
 186        return 0;
 187}
 188
 189
 190static int __devexit m41t93_remove(struct spi_device *spi)
 191{
 192        struct rtc_device *rtc = spi_get_drvdata(spi);
 193
 194        if (rtc)
 195                rtc_device_unregister(rtc);
 196
 197        return 0;
 198}
 199
 200static struct spi_driver m41t93_driver = {
 201        .driver = {
 202                .name   = "rtc-m41t93",
 203                .owner  = THIS_MODULE,
 204        },
 205        .probe  = m41t93_probe,
 206        .remove = __devexit_p(m41t93_remove),
 207};
 208
 209static __init int m41t93_init(void)
 210{
 211        return spi_register_driver(&m41t93_driver);
 212}
 213module_init(m41t93_init);
 214
 215static __exit void m41t93_exit(void)
 216{
 217        spi_unregister_driver(&m41t93_driver);
 218}
 219module_exit(m41t93_exit);
 220
 221MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
 222MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
 223MODULE_LICENSE("GPL");
 224MODULE_ALIAS("spi:rtc-m41t93");
 225