0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/module.h>
0009 #include <linux/kernel.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/rtc.h>
0012 #include <linux/spi/spi.h>
0013 #include <linux/bcd.h>
0014
0015 #define M41T94_REG_SECONDS 0x01
0016 #define M41T94_REG_MINUTES 0x02
0017 #define M41T94_REG_HOURS 0x03
0018 #define M41T94_REG_WDAY 0x04
0019 #define M41T94_REG_DAY 0x05
0020 #define M41T94_REG_MONTH 0x06
0021 #define M41T94_REG_YEAR 0x07
0022 #define M41T94_REG_HT 0x0c
0023
0024 #define M41T94_BIT_HALT 0x40
0025 #define M41T94_BIT_STOP 0x80
0026 #define M41T94_BIT_CB 0x40
0027 #define M41T94_BIT_CEB 0x80
0028
0029 static int m41t94_set_time(struct device *dev, struct rtc_time *tm)
0030 {
0031 struct spi_device *spi = to_spi_device(dev);
0032 u8 buf[8];
0033
0034 dev_dbg(dev, "%s secs=%d, mins=%d, "
0035 "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
0036 "write", tm->tm_sec, tm->tm_min,
0037 tm->tm_hour, tm->tm_mday,
0038 tm->tm_mon, tm->tm_year, tm->tm_wday);
0039
0040 buf[0] = 0x80 | M41T94_REG_SECONDS;
0041 buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec);
0042 buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min);
0043 buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour);
0044 buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
0045 buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday);
0046 buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1);
0047
0048 buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB;
0049 if (tm->tm_year >= 100)
0050 buf[M41T94_REG_HOURS] |= M41T94_BIT_CB;
0051 buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100);
0052
0053 return spi_write(spi, buf, 8);
0054 }
0055
0056 static int m41t94_read_time(struct device *dev, struct rtc_time *tm)
0057 {
0058 struct spi_device *spi = to_spi_device(dev);
0059 u8 buf[2];
0060 int ret, hour;
0061
0062
0063 ret = spi_w8r8(spi, M41T94_REG_HT);
0064 if (ret < 0)
0065 return ret;
0066 if (ret & M41T94_BIT_HALT) {
0067 buf[0] = 0x80 | M41T94_REG_HT;
0068 buf[1] = ret & ~M41T94_BIT_HALT;
0069 spi_write(spi, buf, 2);
0070 }
0071
0072
0073 ret = spi_w8r8(spi, M41T94_REG_SECONDS);
0074 if (ret < 0)
0075 return ret;
0076 if (ret & M41T94_BIT_STOP) {
0077 buf[0] = 0x80 | M41T94_REG_SECONDS;
0078 buf[1] = ret & ~M41T94_BIT_STOP;
0079 spi_write(spi, buf, 2);
0080 }
0081
0082 tm->tm_sec = bcd2bin(spi_w8r8(spi, M41T94_REG_SECONDS));
0083 tm->tm_min = bcd2bin(spi_w8r8(spi, M41T94_REG_MINUTES));
0084 hour = spi_w8r8(spi, M41T94_REG_HOURS);
0085 tm->tm_hour = bcd2bin(hour & 0x3f);
0086 tm->tm_wday = bcd2bin(spi_w8r8(spi, M41T94_REG_WDAY)) - 1;
0087 tm->tm_mday = bcd2bin(spi_w8r8(spi, M41T94_REG_DAY));
0088 tm->tm_mon = bcd2bin(spi_w8r8(spi, M41T94_REG_MONTH)) - 1;
0089 tm->tm_year = bcd2bin(spi_w8r8(spi, M41T94_REG_YEAR));
0090 if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB))
0091 tm->tm_year += 100;
0092
0093 dev_dbg(dev, "%s secs=%d, mins=%d, "
0094 "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
0095 "read", tm->tm_sec, tm->tm_min,
0096 tm->tm_hour, tm->tm_mday,
0097 tm->tm_mon, tm->tm_year, tm->tm_wday);
0098
0099 return 0;
0100 }
0101
0102 static const struct rtc_class_ops m41t94_rtc_ops = {
0103 .read_time = m41t94_read_time,
0104 .set_time = m41t94_set_time,
0105 };
0106
0107 static struct spi_driver m41t94_driver;
0108
0109 static int m41t94_probe(struct spi_device *spi)
0110 {
0111 struct rtc_device *rtc;
0112 int res;
0113
0114 spi->bits_per_word = 8;
0115 spi_setup(spi);
0116
0117 res = spi_w8r8(spi, M41T94_REG_SECONDS);
0118 if (res < 0) {
0119 dev_err(&spi->dev, "not found.\n");
0120 return res;
0121 }
0122
0123 rtc = devm_rtc_device_register(&spi->dev, m41t94_driver.driver.name,
0124 &m41t94_rtc_ops, THIS_MODULE);
0125 if (IS_ERR(rtc))
0126 return PTR_ERR(rtc);
0127
0128 spi_set_drvdata(spi, rtc);
0129
0130 return 0;
0131 }
0132
0133 static struct spi_driver m41t94_driver = {
0134 .driver = {
0135 .name = "rtc-m41t94",
0136 },
0137 .probe = m41t94_probe,
0138 };
0139
0140 module_spi_driver(m41t94_driver);
0141
0142 MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>");
0143 MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC");
0144 MODULE_LICENSE("GPL");
0145 MODULE_ALIAS("spi:rtc-m41t94");