18fc2c767SKim B. Heino /* 28fc2c767SKim B. Heino * Driver for ST M41T94 SPI RTC 38fc2c767SKim B. Heino * 48fc2c767SKim B. Heino * Copyright (C) 2008 Kim B. Heino 58fc2c767SKim B. Heino * 68fc2c767SKim B. Heino * This program is free software; you can redistribute it and/or modify 78fc2c767SKim B. Heino * it under the terms of the GNU General Public License version 2 as 88fc2c767SKim B. Heino * published by the Free Software Foundation. 98fc2c767SKim B. Heino */ 108fc2c767SKim B. Heino 118fc2c767SKim B. Heino #include <linux/module.h> 128fc2c767SKim B. Heino #include <linux/kernel.h> 138fc2c767SKim B. Heino #include <linux/platform_device.h> 148fc2c767SKim B. Heino #include <linux/rtc.h> 158fc2c767SKim B. Heino #include <linux/spi/spi.h> 168fc2c767SKim B. Heino #include <linux/bcd.h> 178fc2c767SKim B. Heino 188fc2c767SKim B. Heino #define M41T94_REG_SECONDS 0x01 198fc2c767SKim B. Heino #define M41T94_REG_MINUTES 0x02 208fc2c767SKim B. Heino #define M41T94_REG_HOURS 0x03 218fc2c767SKim B. Heino #define M41T94_REG_WDAY 0x04 228fc2c767SKim B. Heino #define M41T94_REG_DAY 0x05 238fc2c767SKim B. Heino #define M41T94_REG_MONTH 0x06 248fc2c767SKim B. Heino #define M41T94_REG_YEAR 0x07 258fc2c767SKim B. Heino #define M41T94_REG_HT 0x0c 268fc2c767SKim B. Heino 278fc2c767SKim B. Heino #define M41T94_BIT_HALT 0x40 288fc2c767SKim B. Heino #define M41T94_BIT_STOP 0x80 298fc2c767SKim B. Heino #define M41T94_BIT_CB 0x40 308fc2c767SKim B. Heino #define M41T94_BIT_CEB 0x80 318fc2c767SKim B. Heino 328fc2c767SKim B. Heino static int m41t94_set_time(struct device *dev, struct rtc_time *tm) 338fc2c767SKim B. Heino { 348fc2c767SKim B. Heino struct spi_device *spi = to_spi_device(dev); 358fc2c767SKim B. Heino u8 buf[8]; /* write cmd + 7 registers */ 368fc2c767SKim B. Heino 378fc2c767SKim B. Heino dev_dbg(dev, "%s secs=%d, mins=%d, " 388fc2c767SKim B. Heino "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", 398fc2c767SKim B. Heino "write", tm->tm_sec, tm->tm_min, 408fc2c767SKim B. Heino tm->tm_hour, tm->tm_mday, 418fc2c767SKim B. Heino tm->tm_mon, tm->tm_year, tm->tm_wday); 428fc2c767SKim B. Heino 438fc2c767SKim B. Heino buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */ 44fe20ba70SAdrian Bunk buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec); 45fe20ba70SAdrian Bunk buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min); 46fe20ba70SAdrian Bunk buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour); 47fe20ba70SAdrian Bunk buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1); 48fe20ba70SAdrian Bunk buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday); 49fe20ba70SAdrian Bunk buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1); 508fc2c767SKim B. Heino 518fc2c767SKim B. Heino buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB; 528fc2c767SKim B. Heino if (tm->tm_year >= 100) 538fc2c767SKim B. Heino buf[M41T94_REG_HOURS] |= M41T94_BIT_CB; 54fe20ba70SAdrian Bunk buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100); 558fc2c767SKim B. Heino 568fc2c767SKim B. Heino return spi_write(spi, buf, 8); 578fc2c767SKim B. Heino } 588fc2c767SKim B. Heino 598fc2c767SKim B. Heino static int m41t94_read_time(struct device *dev, struct rtc_time *tm) 608fc2c767SKim B. Heino { 618fc2c767SKim B. Heino struct spi_device *spi = to_spi_device(dev); 628fc2c767SKim B. Heino u8 buf[2]; 638fc2c767SKim B. Heino int ret, hour; 648fc2c767SKim B. Heino 658fc2c767SKim B. Heino /* clear halt update bit */ 668fc2c767SKim B. Heino ret = spi_w8r8(spi, M41T94_REG_HT); 678fc2c767SKim B. Heino if (ret < 0) 688fc2c767SKim B. Heino return ret; 698fc2c767SKim B. Heino if (ret & M41T94_BIT_HALT) { 708fc2c767SKim B. Heino buf[0] = 0x80 | M41T94_REG_HT; 718fc2c767SKim B. Heino buf[1] = ret & ~M41T94_BIT_HALT; 728fc2c767SKim B. Heino spi_write(spi, buf, 2); 738fc2c767SKim B. Heino } 748fc2c767SKim B. Heino 758fc2c767SKim B. Heino /* clear stop bit */ 768fc2c767SKim B. Heino ret = spi_w8r8(spi, M41T94_REG_SECONDS); 778fc2c767SKim B. Heino if (ret < 0) 788fc2c767SKim B. Heino return ret; 798fc2c767SKim B. Heino if (ret & M41T94_BIT_STOP) { 808fc2c767SKim B. Heino buf[0] = 0x80 | M41T94_REG_SECONDS; 818fc2c767SKim B. Heino buf[1] = ret & ~M41T94_BIT_STOP; 828fc2c767SKim B. Heino spi_write(spi, buf, 2); 838fc2c767SKim B. Heino } 848fc2c767SKim B. Heino 85fe20ba70SAdrian Bunk tm->tm_sec = bcd2bin(spi_w8r8(spi, M41T94_REG_SECONDS)); 86fe20ba70SAdrian Bunk tm->tm_min = bcd2bin(spi_w8r8(spi, M41T94_REG_MINUTES)); 878fc2c767SKim B. Heino hour = spi_w8r8(spi, M41T94_REG_HOURS); 88fe20ba70SAdrian Bunk tm->tm_hour = bcd2bin(hour & 0x3f); 89fe20ba70SAdrian Bunk tm->tm_wday = bcd2bin(spi_w8r8(spi, M41T94_REG_WDAY)) - 1; 90fe20ba70SAdrian Bunk tm->tm_mday = bcd2bin(spi_w8r8(spi, M41T94_REG_DAY)); 91fe20ba70SAdrian Bunk tm->tm_mon = bcd2bin(spi_w8r8(spi, M41T94_REG_MONTH)) - 1; 92fe20ba70SAdrian Bunk tm->tm_year = bcd2bin(spi_w8r8(spi, M41T94_REG_YEAR)); 938fc2c767SKim B. Heino if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB)) 948fc2c767SKim B. Heino tm->tm_year += 100; 958fc2c767SKim B. Heino 968fc2c767SKim B. Heino dev_dbg(dev, "%s secs=%d, mins=%d, " 978fc2c767SKim B. Heino "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", 988fc2c767SKim B. Heino "read", tm->tm_sec, tm->tm_min, 998fc2c767SKim B. Heino tm->tm_hour, tm->tm_mday, 1008fc2c767SKim B. Heino tm->tm_mon, tm->tm_year, tm->tm_wday); 1018fc2c767SKim B. Heino 1028fc2c767SKim B. Heino /* initial clock setting can be undefined */ 1038fc2c767SKim B. Heino return rtc_valid_tm(tm); 1048fc2c767SKim B. Heino } 1058fc2c767SKim B. Heino 1068fc2c767SKim B. Heino static const struct rtc_class_ops m41t94_rtc_ops = { 1078fc2c767SKim B. Heino .read_time = m41t94_read_time, 1088fc2c767SKim B. Heino .set_time = m41t94_set_time, 1098fc2c767SKim B. Heino }; 1108fc2c767SKim B. Heino 1118fc2c767SKim B. Heino static struct spi_driver m41t94_driver; 1128fc2c767SKim B. Heino 1138fc2c767SKim B. Heino static int __devinit m41t94_probe(struct spi_device *spi) 1148fc2c767SKim B. Heino { 1158fc2c767SKim B. Heino struct rtc_device *rtc; 1168fc2c767SKim B. Heino int res; 1178fc2c767SKim B. Heino 1188fc2c767SKim B. Heino spi->bits_per_word = 8; 1198fc2c767SKim B. Heino spi_setup(spi); 1208fc2c767SKim B. Heino 1218fc2c767SKim B. Heino res = spi_w8r8(spi, M41T94_REG_SECONDS); 1228fc2c767SKim B. Heino if (res < 0) { 1238fc2c767SKim B. Heino dev_err(&spi->dev, "not found.\n"); 1248fc2c767SKim B. Heino return res; 1258fc2c767SKim B. Heino } 1268fc2c767SKim B. Heino 1278fc2c767SKim B. Heino rtc = rtc_device_register(m41t94_driver.driver.name, 1288fc2c767SKim B. Heino &spi->dev, &m41t94_rtc_ops, THIS_MODULE); 1298fc2c767SKim B. Heino if (IS_ERR(rtc)) 1308fc2c767SKim B. Heino return PTR_ERR(rtc); 1318fc2c767SKim B. Heino 1328fc2c767SKim B. Heino dev_set_drvdata(&spi->dev, rtc); 1338fc2c767SKim B. Heino 1348fc2c767SKim B. Heino return 0; 1358fc2c767SKim B. Heino } 1368fc2c767SKim B. Heino 1378fc2c767SKim B. Heino static int __devexit m41t94_remove(struct spi_device *spi) 1388fc2c767SKim B. Heino { 1398fc2c767SKim B. Heino struct rtc_device *rtc = platform_get_drvdata(spi); 1408fc2c767SKim B. Heino 1418fc2c767SKim B. Heino if (rtc) 1428fc2c767SKim B. Heino rtc_device_unregister(rtc); 1438fc2c767SKim B. Heino 1448fc2c767SKim B. Heino return 0; 1458fc2c767SKim B. Heino } 1468fc2c767SKim B. Heino 1478fc2c767SKim B. Heino static struct spi_driver m41t94_driver = { 1488fc2c767SKim B. Heino .driver = { 1498fc2c767SKim B. Heino .name = "rtc-m41t94", 1508fc2c767SKim B. Heino .bus = &spi_bus_type, 1518fc2c767SKim B. Heino .owner = THIS_MODULE, 1528fc2c767SKim B. Heino }, 1538fc2c767SKim B. Heino .probe = m41t94_probe, 1548fc2c767SKim B. Heino .remove = __devexit_p(m41t94_remove), 1558fc2c767SKim B. Heino }; 1568fc2c767SKim B. Heino 1578fc2c767SKim B. Heino static __init int m41t94_init(void) 1588fc2c767SKim B. Heino { 1598fc2c767SKim B. Heino return spi_register_driver(&m41t94_driver); 1608fc2c767SKim B. Heino } 1618fc2c767SKim B. Heino 1628fc2c767SKim B. Heino module_init(m41t94_init); 1638fc2c767SKim B. Heino 1648fc2c767SKim B. Heino static __exit void m41t94_exit(void) 1658fc2c767SKim B. Heino { 1668fc2c767SKim B. Heino spi_unregister_driver(&m41t94_driver); 1678fc2c767SKim B. Heino } 1688fc2c767SKim B. Heino 1698fc2c767SKim B. Heino module_exit(m41t94_exit); 1708fc2c767SKim B. Heino 1718fc2c767SKim B. Heino MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); 1728fc2c767SKim B. Heino MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); 1738fc2c767SKim B. Heino MODULE_LICENSE("GPL"); 174*e0626e38SAnton Vorontsov MODULE_ALIAS("spi:rtc-m41t94"); 175