1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28fc2c767SKim B. Heino /*
38fc2c767SKim B. Heino * Driver for ST M41T94 SPI RTC
48fc2c767SKim B. Heino *
58fc2c767SKim B. Heino * Copyright (C) 2008 Kim B. Heino
68fc2c767SKim B. Heino */
78fc2c767SKim B. Heino
88fc2c767SKim B. Heino #include <linux/module.h>
98fc2c767SKim B. Heino #include <linux/kernel.h>
108fc2c767SKim B. Heino #include <linux/platform_device.h>
118fc2c767SKim B. Heino #include <linux/rtc.h>
128fc2c767SKim B. Heino #include <linux/spi/spi.h>
138fc2c767SKim B. Heino #include <linux/bcd.h>
148fc2c767SKim B. Heino
158fc2c767SKim B. Heino #define M41T94_REG_SECONDS 0x01
168fc2c767SKim B. Heino #define M41T94_REG_MINUTES 0x02
178fc2c767SKim B. Heino #define M41T94_REG_HOURS 0x03
188fc2c767SKim B. Heino #define M41T94_REG_WDAY 0x04
198fc2c767SKim B. Heino #define M41T94_REG_DAY 0x05
208fc2c767SKim B. Heino #define M41T94_REG_MONTH 0x06
218fc2c767SKim B. Heino #define M41T94_REG_YEAR 0x07
228fc2c767SKim B. Heino #define M41T94_REG_HT 0x0c
238fc2c767SKim B. Heino
248fc2c767SKim B. Heino #define M41T94_BIT_HALT 0x40
258fc2c767SKim B. Heino #define M41T94_BIT_STOP 0x80
268fc2c767SKim B. Heino #define M41T94_BIT_CB 0x40
278fc2c767SKim B. Heino #define M41T94_BIT_CEB 0x80
288fc2c767SKim B. Heino
m41t94_set_time(struct device * dev,struct rtc_time * tm)298fc2c767SKim B. Heino static int m41t94_set_time(struct device *dev, struct rtc_time *tm)
308fc2c767SKim B. Heino {
318fc2c767SKim B. Heino struct spi_device *spi = to_spi_device(dev);
328fc2c767SKim B. Heino u8 buf[8]; /* write cmd + 7 registers */
338fc2c767SKim B. Heino
348fc2c767SKim B. Heino dev_dbg(dev, "%s secs=%d, mins=%d, "
358fc2c767SKim B. Heino "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
368fc2c767SKim B. Heino "write", tm->tm_sec, tm->tm_min,
378fc2c767SKim B. Heino tm->tm_hour, tm->tm_mday,
388fc2c767SKim B. Heino tm->tm_mon, tm->tm_year, tm->tm_wday);
398fc2c767SKim B. Heino
408fc2c767SKim B. Heino buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */
41fe20ba70SAdrian Bunk buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec);
42fe20ba70SAdrian Bunk buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min);
43fe20ba70SAdrian Bunk buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour);
44fe20ba70SAdrian Bunk buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
45fe20ba70SAdrian Bunk buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday);
46fe20ba70SAdrian Bunk buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1);
478fc2c767SKim B. Heino
488fc2c767SKim B. Heino buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB;
498fc2c767SKim B. Heino if (tm->tm_year >= 100)
508fc2c767SKim B. Heino buf[M41T94_REG_HOURS] |= M41T94_BIT_CB;
51fe20ba70SAdrian Bunk buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100);
528fc2c767SKim B. Heino
538fc2c767SKim B. Heino return spi_write(spi, buf, 8);
548fc2c767SKim B. Heino }
558fc2c767SKim B. Heino
m41t94_read_time(struct device * dev,struct rtc_time * tm)568fc2c767SKim B. Heino static int m41t94_read_time(struct device *dev, struct rtc_time *tm)
578fc2c767SKim B. Heino {
588fc2c767SKim B. Heino struct spi_device *spi = to_spi_device(dev);
598fc2c767SKim B. Heino u8 buf[2];
608fc2c767SKim B. Heino int ret, hour;
618fc2c767SKim B. Heino
628fc2c767SKim B. Heino /* clear halt update bit */
638fc2c767SKim B. Heino ret = spi_w8r8(spi, M41T94_REG_HT);
648fc2c767SKim B. Heino if (ret < 0)
658fc2c767SKim B. Heino return ret;
668fc2c767SKim B. Heino if (ret & M41T94_BIT_HALT) {
678fc2c767SKim B. Heino buf[0] = 0x80 | M41T94_REG_HT;
688fc2c767SKim B. Heino buf[1] = ret & ~M41T94_BIT_HALT;
698fc2c767SKim B. Heino spi_write(spi, buf, 2);
708fc2c767SKim B. Heino }
718fc2c767SKim B. Heino
728fc2c767SKim B. Heino /* clear stop bit */
738fc2c767SKim B. Heino ret = spi_w8r8(spi, M41T94_REG_SECONDS);
748fc2c767SKim B. Heino if (ret < 0)
758fc2c767SKim B. Heino return ret;
768fc2c767SKim B. Heino if (ret & M41T94_BIT_STOP) {
778fc2c767SKim B. Heino buf[0] = 0x80 | M41T94_REG_SECONDS;
788fc2c767SKim B. Heino buf[1] = ret & ~M41T94_BIT_STOP;
798fc2c767SKim B. Heino spi_write(spi, buf, 2);
808fc2c767SKim B. Heino }
818fc2c767SKim B. Heino
82fe20ba70SAdrian Bunk tm->tm_sec = bcd2bin(spi_w8r8(spi, M41T94_REG_SECONDS));
83fe20ba70SAdrian Bunk tm->tm_min = bcd2bin(spi_w8r8(spi, M41T94_REG_MINUTES));
848fc2c767SKim B. Heino hour = spi_w8r8(spi, M41T94_REG_HOURS);
85fe20ba70SAdrian Bunk tm->tm_hour = bcd2bin(hour & 0x3f);
86fe20ba70SAdrian Bunk tm->tm_wday = bcd2bin(spi_w8r8(spi, M41T94_REG_WDAY)) - 1;
87fe20ba70SAdrian Bunk tm->tm_mday = bcd2bin(spi_w8r8(spi, M41T94_REG_DAY));
88fe20ba70SAdrian Bunk tm->tm_mon = bcd2bin(spi_w8r8(spi, M41T94_REG_MONTH)) - 1;
89fe20ba70SAdrian Bunk tm->tm_year = bcd2bin(spi_w8r8(spi, M41T94_REG_YEAR));
908fc2c767SKim B. Heino if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB))
918fc2c767SKim B. Heino tm->tm_year += 100;
928fc2c767SKim B. Heino
938fc2c767SKim B. Heino dev_dbg(dev, "%s secs=%d, mins=%d, "
948fc2c767SKim B. Heino "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
958fc2c767SKim B. Heino "read", tm->tm_sec, tm->tm_min,
968fc2c767SKim B. Heino tm->tm_hour, tm->tm_mday,
978fc2c767SKim B. Heino tm->tm_mon, tm->tm_year, tm->tm_wday);
988fc2c767SKim B. Heino
9922652ba7SAlexandre Belloni return 0;
1008fc2c767SKim B. Heino }
1018fc2c767SKim B. Heino
1028fc2c767SKim B. Heino static const struct rtc_class_ops m41t94_rtc_ops = {
1038fc2c767SKim B. Heino .read_time = m41t94_read_time,
1048fc2c767SKim B. Heino .set_time = m41t94_set_time,
1058fc2c767SKim B. Heino };
1068fc2c767SKim B. Heino
1078fc2c767SKim B. Heino static struct spi_driver m41t94_driver;
1088fc2c767SKim B. Heino
m41t94_probe(struct spi_device * spi)1095a167f45SGreg Kroah-Hartman static int m41t94_probe(struct spi_device *spi)
1108fc2c767SKim B. Heino {
1118fc2c767SKim B. Heino struct rtc_device *rtc;
1128fc2c767SKim B. Heino int res;
1138fc2c767SKim B. Heino
1148fc2c767SKim B. Heino spi->bits_per_word = 8;
1158fc2c767SKim B. Heino spi_setup(spi);
1168fc2c767SKim B. Heino
1178fc2c767SKim B. Heino res = spi_w8r8(spi, M41T94_REG_SECONDS);
1188fc2c767SKim B. Heino if (res < 0) {
1198fc2c767SKim B. Heino dev_err(&spi->dev, "not found.\n");
1208fc2c767SKim B. Heino return res;
1218fc2c767SKim B. Heino }
1228fc2c767SKim B. Heino
123fb320d0aSJingoo Han rtc = devm_rtc_device_register(&spi->dev, m41t94_driver.driver.name,
124fb320d0aSJingoo Han &m41t94_rtc_ops, THIS_MODULE);
1258fc2c767SKim B. Heino if (IS_ERR(rtc))
1268fc2c767SKim B. Heino return PTR_ERR(rtc);
1278fc2c767SKim B. Heino
128ee62474dSJingoo Han spi_set_drvdata(spi, rtc);
1298fc2c767SKim B. Heino
1308fc2c767SKim B. Heino return 0;
1318fc2c767SKim B. Heino }
1328fc2c767SKim B. Heino
1338fc2c767SKim B. Heino static struct spi_driver m41t94_driver = {
1348fc2c767SKim B. Heino .driver = {
1358fc2c767SKim B. Heino .name = "rtc-m41t94",
1368fc2c767SKim B. Heino },
1378fc2c767SKim B. Heino .probe = m41t94_probe,
1388fc2c767SKim B. Heino };
1398fc2c767SKim B. Heino
140109e9418SAxel Lin module_spi_driver(m41t94_driver);
1418fc2c767SKim B. Heino
1428fc2c767SKim B. Heino MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>");
1438fc2c767SKim B. Heino MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC");
1448fc2c767SKim B. Heino MODULE_LICENSE("GPL");
145e0626e38SAnton Vorontsov MODULE_ALIAS("spi:rtc-m41t94");
146