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