xref: /linux/drivers/rtc/rtc-m41t93.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
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 
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 
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 
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 
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