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