1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
274d34d4bSVoss, Nikolaus /*
374d34d4bSVoss, Nikolaus *
474d34d4bSVoss, Nikolaus * Driver for ST M41T93 SPI RTC
574d34d4bSVoss, Nikolaus *
674d34d4bSVoss, Nikolaus * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH
774d34d4bSVoss, Nikolaus */
874d34d4bSVoss, Nikolaus
974d34d4bSVoss, Nikolaus #include <linux/bcd.h>
1074d34d4bSVoss, Nikolaus #include <linux/kernel.h>
1174d34d4bSVoss, Nikolaus #include <linux/module.h>
1274d34d4bSVoss, Nikolaus #include <linux/platform_device.h>
1374d34d4bSVoss, Nikolaus #include <linux/rtc.h>
1474d34d4bSVoss, Nikolaus #include <linux/spi/spi.h>
1574d34d4bSVoss, Nikolaus
1674d34d4bSVoss, Nikolaus #define M41T93_REG_SSEC 0
1774d34d4bSVoss, Nikolaus #define M41T93_REG_ST_SEC 1
1874d34d4bSVoss, Nikolaus #define M41T93_REG_MIN 2
1974d34d4bSVoss, Nikolaus #define M41T93_REG_CENT_HOUR 3
2074d34d4bSVoss, Nikolaus #define M41T93_REG_WDAY 4
2174d34d4bSVoss, Nikolaus #define M41T93_REG_DAY 5
2274d34d4bSVoss, Nikolaus #define M41T93_REG_MON 6
2374d34d4bSVoss, Nikolaus #define M41T93_REG_YEAR 7
2474d34d4bSVoss, Nikolaus
2574d34d4bSVoss, Nikolaus
2674d34d4bSVoss, Nikolaus #define M41T93_REG_ALM_HOUR_HT 0xc
2774d34d4bSVoss, Nikolaus #define M41T93_REG_FLAGS 0xf
2874d34d4bSVoss, Nikolaus
2974d34d4bSVoss, Nikolaus #define M41T93_FLAG_ST (1 << 7)
3074d34d4bSVoss, Nikolaus #define M41T93_FLAG_OF (1 << 2)
3174d34d4bSVoss, Nikolaus #define M41T93_FLAG_BL (1 << 4)
3274d34d4bSVoss, Nikolaus #define M41T93_FLAG_HT (1 << 6)
3374d34d4bSVoss, Nikolaus
m41t93_set_reg(struct spi_device * spi,u8 addr,u8 data)3474d34d4bSVoss, Nikolaus static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
3574d34d4bSVoss, Nikolaus {
3674d34d4bSVoss, Nikolaus u8 buf[2];
3774d34d4bSVoss, Nikolaus
3874d34d4bSVoss, Nikolaus /* MSB must be '1' to write */
3974d34d4bSVoss, Nikolaus buf[0] = addr | 0x80;
4074d34d4bSVoss, Nikolaus buf[1] = data;
4174d34d4bSVoss, Nikolaus
4274d34d4bSVoss, Nikolaus return spi_write(spi, buf, sizeof(buf));
4374d34d4bSVoss, Nikolaus }
4474d34d4bSVoss, Nikolaus
m41t93_set_time(struct device * dev,struct rtc_time * tm)4574d34d4bSVoss, Nikolaus static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
4674d34d4bSVoss, Nikolaus {
4774d34d4bSVoss, Nikolaus struct spi_device *spi = to_spi_device(dev);
48bcffb10fSNikolaus Voss int tmp;
4974d34d4bSVoss, Nikolaus u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */
5074d34d4bSVoss, Nikolaus u8 * const data = &buf[1]; /* ptr to first data byte */
5174d34d4bSVoss, Nikolaus
5274d34d4bSVoss, Nikolaus dev_dbg(dev, "%s secs=%d, mins=%d, "
5374d34d4bSVoss, Nikolaus "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
5474d34d4bSVoss, Nikolaus "write", tm->tm_sec, tm->tm_min,
5574d34d4bSVoss, Nikolaus tm->tm_hour, tm->tm_mday,
5674d34d4bSVoss, Nikolaus tm->tm_mon, tm->tm_year, tm->tm_wday);
5774d34d4bSVoss, Nikolaus
5874d34d4bSVoss, Nikolaus if (tm->tm_year < 100) {
5974d34d4bSVoss, Nikolaus dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n");
6074d34d4bSVoss, Nikolaus return -EINVAL;
6174d34d4bSVoss, Nikolaus }
6274d34d4bSVoss, Nikolaus
63bcffb10fSNikolaus Voss tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
64bcffb10fSNikolaus Voss if (tmp < 0)
65bcffb10fSNikolaus Voss return tmp;
66bcffb10fSNikolaus Voss
67bcffb10fSNikolaus Voss if (tmp & M41T93_FLAG_OF) {
68bcffb10fSNikolaus Voss dev_warn(&spi->dev, "OF bit is set, resetting.\n");
69bcffb10fSNikolaus Voss m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
70bcffb10fSNikolaus Voss
71bcffb10fSNikolaus Voss tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
72bcffb10fSNikolaus Voss if (tmp < 0) {
73bcffb10fSNikolaus Voss return tmp;
74bcffb10fSNikolaus Voss } else if (tmp & M41T93_FLAG_OF) {
75bcffb10fSNikolaus Voss /* OF cannot be immediately reset: oscillator has to be
76bcffb10fSNikolaus Voss * restarted. */
77bcffb10fSNikolaus Voss u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
78bcffb10fSNikolaus Voss
79bcffb10fSNikolaus Voss dev_warn(&spi->dev,
80bcffb10fSNikolaus Voss "OF bit is still set, kickstarting clock.\n");
81bcffb10fSNikolaus Voss m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
82bcffb10fSNikolaus Voss reset_osc &= ~M41T93_FLAG_ST;
83bcffb10fSNikolaus Voss m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
84bcffb10fSNikolaus Voss }
85bcffb10fSNikolaus Voss }
86bcffb10fSNikolaus Voss
8774d34d4bSVoss, Nikolaus data[M41T93_REG_SSEC] = 0;
8874d34d4bSVoss, Nikolaus data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec);
8974d34d4bSVoss, Nikolaus data[M41T93_REG_MIN] = bin2bcd(tm->tm_min);
9074d34d4bSVoss, Nikolaus data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) |
9174d34d4bSVoss, Nikolaus ((tm->tm_year/100-1) << 6);
9274d34d4bSVoss, Nikolaus data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday);
9374d34d4bSVoss, Nikolaus data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
9474d34d4bSVoss, Nikolaus data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1);
9574d34d4bSVoss, Nikolaus data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100);
9674d34d4bSVoss, Nikolaus
9774d34d4bSVoss, Nikolaus return spi_write(spi, buf, sizeof(buf));
9874d34d4bSVoss, Nikolaus }
9974d34d4bSVoss, Nikolaus
10074d34d4bSVoss, Nikolaus
m41t93_get_time(struct device * dev,struct rtc_time * tm)10174d34d4bSVoss, Nikolaus static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
10274d34d4bSVoss, Nikolaus {
10374d34d4bSVoss, Nikolaus struct spi_device *spi = to_spi_device(dev);
10474d34d4bSVoss, Nikolaus const u8 start_addr = 0;
10574d34d4bSVoss, Nikolaus u8 buf[8];
10674d34d4bSVoss, Nikolaus int century_after_1900;
10774d34d4bSVoss, Nikolaus int tmp;
10874d34d4bSVoss, Nikolaus int ret = 0;
10974d34d4bSVoss, Nikolaus
11074d34d4bSVoss, Nikolaus /* Check status of clock. Two states must be considered:
11174d34d4bSVoss, Nikolaus 1. halt bit (HT) is set: the clock is running but update of readout
11274d34d4bSVoss, Nikolaus registers has been disabled due to power failure. This is normal
11374d34d4bSVoss, Nikolaus case after poweron. Time is valid after resetting HT bit.
114bcffb10fSNikolaus Voss 2. oscillator fail bit (OF) is set: time is invalid.
11574d34d4bSVoss, Nikolaus */
11674d34d4bSVoss, Nikolaus tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
11774d34d4bSVoss, Nikolaus if (tmp < 0)
11874d34d4bSVoss, Nikolaus return tmp;
11974d34d4bSVoss, Nikolaus
12074d34d4bSVoss, Nikolaus if (tmp & M41T93_FLAG_HT) {
12174d34d4bSVoss, Nikolaus dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n");
12274d34d4bSVoss, Nikolaus m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT,
12374d34d4bSVoss, Nikolaus tmp & ~M41T93_FLAG_HT);
12474d34d4bSVoss, Nikolaus }
12574d34d4bSVoss, Nikolaus
12674d34d4bSVoss, Nikolaus tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
12774d34d4bSVoss, Nikolaus if (tmp < 0)
12874d34d4bSVoss, Nikolaus return tmp;
12974d34d4bSVoss, Nikolaus
13074d34d4bSVoss, Nikolaus if (tmp & M41T93_FLAG_OF) {
13174d34d4bSVoss, Nikolaus ret = -EINVAL;
132bcffb10fSNikolaus Voss dev_warn(&spi->dev, "OF bit is set, write time to restart.\n");
13374d34d4bSVoss, Nikolaus }
13474d34d4bSVoss, Nikolaus
13574d34d4bSVoss, Nikolaus if (tmp & M41T93_FLAG_BL)
13674d34d4bSVoss, Nikolaus dev_warn(&spi->dev, "BL bit is set, replace battery.\n");
13774d34d4bSVoss, Nikolaus
13874d34d4bSVoss, Nikolaus /* read actual time/date */
13974d34d4bSVoss, Nikolaus tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf));
14074d34d4bSVoss, Nikolaus if (tmp < 0)
14174d34d4bSVoss, Nikolaus return tmp;
14274d34d4bSVoss, Nikolaus
14374d34d4bSVoss, Nikolaus tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]);
14474d34d4bSVoss, Nikolaus tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]);
14574d34d4bSVoss, Nikolaus tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f);
14674d34d4bSVoss, Nikolaus tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]);
14774d34d4bSVoss, Nikolaus tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1;
14874d34d4bSVoss, Nikolaus tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1;
14974d34d4bSVoss, Nikolaus
15074d34d4bSVoss, Nikolaus century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1;
15174d34d4bSVoss, Nikolaus tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100;
15274d34d4bSVoss, Nikolaus
15374d34d4bSVoss, Nikolaus dev_dbg(dev, "%s secs=%d, mins=%d, "
15474d34d4bSVoss, Nikolaus "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
15574d34d4bSVoss, Nikolaus "read", tm->tm_sec, tm->tm_min,
15674d34d4bSVoss, Nikolaus tm->tm_hour, tm->tm_mday,
15774d34d4bSVoss, Nikolaus tm->tm_mon, tm->tm_year, tm->tm_wday);
15874d34d4bSVoss, Nikolaus
1593d809cedSAlexandre Belloni return ret;
16074d34d4bSVoss, Nikolaus }
16174d34d4bSVoss, Nikolaus
16274d34d4bSVoss, Nikolaus
16374d34d4bSVoss, Nikolaus static const struct rtc_class_ops m41t93_rtc_ops = {
16474d34d4bSVoss, Nikolaus .read_time = m41t93_get_time,
16574d34d4bSVoss, Nikolaus .set_time = m41t93_set_time,
16674d34d4bSVoss, Nikolaus };
16774d34d4bSVoss, Nikolaus
16874d34d4bSVoss, Nikolaus static struct spi_driver m41t93_driver;
16974d34d4bSVoss, Nikolaus
m41t93_probe(struct spi_device * spi)1705a167f45SGreg Kroah-Hartman static int m41t93_probe(struct spi_device *spi)
17174d34d4bSVoss, Nikolaus {
17274d34d4bSVoss, Nikolaus struct rtc_device *rtc;
17374d34d4bSVoss, Nikolaus int res;
17474d34d4bSVoss, Nikolaus
17574d34d4bSVoss, Nikolaus spi->bits_per_word = 8;
17674d34d4bSVoss, Nikolaus spi_setup(spi);
17774d34d4bSVoss, Nikolaus
17874d34d4bSVoss, Nikolaus res = spi_w8r8(spi, M41T93_REG_WDAY);
17974d34d4bSVoss, Nikolaus if (res < 0 || (res & 0xf8) != 0) {
18074d34d4bSVoss, Nikolaus dev_err(&spi->dev, "not found 0x%x.\n", res);
18174d34d4bSVoss, Nikolaus return -ENODEV;
18274d34d4bSVoss, Nikolaus }
18374d34d4bSVoss, Nikolaus
1840166fcd0SJingoo Han rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name,
1850166fcd0SJingoo Han &m41t93_rtc_ops, THIS_MODULE);
18674d34d4bSVoss, Nikolaus if (IS_ERR(rtc))
18774d34d4bSVoss, Nikolaus return PTR_ERR(rtc);
18874d34d4bSVoss, Nikolaus
189b6c4e71aSJingoo Han spi_set_drvdata(spi, rtc);
19074d34d4bSVoss, Nikolaus
19174d34d4bSVoss, Nikolaus return 0;
19274d34d4bSVoss, Nikolaus }
19374d34d4bSVoss, Nikolaus
19474d34d4bSVoss, Nikolaus static struct spi_driver m41t93_driver = {
19574d34d4bSVoss, Nikolaus .driver = {
19674d34d4bSVoss, Nikolaus .name = "rtc-m41t93",
19774d34d4bSVoss, Nikolaus },
19874d34d4bSVoss, Nikolaus .probe = m41t93_probe,
19974d34d4bSVoss, Nikolaus };
20074d34d4bSVoss, Nikolaus
201109e9418SAxel Lin module_spi_driver(m41t93_driver);
20274d34d4bSVoss, Nikolaus
20374d34d4bSVoss, Nikolaus MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
20474d34d4bSVoss, Nikolaus MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
20574d34d4bSVoss, Nikolaus MODULE_LICENSE("GPL");
20674d34d4bSVoss, Nikolaus MODULE_ALIAS("spi:rtc-m41t93");
207