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 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 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 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