Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for ST M41T94 SPI RTC
0004  *
0005  * Copyright (C) 2008 Kim B. Heino
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]; /* write cmd + 7 registers */
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; /* write time + date */
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     /* clear halt update bit */
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     /* clear stop bit */
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");