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 102*22652ba7SAlexandre Belloni return 0; 1038fc2c767SKim B. Heino } 1048fc2c767SKim B. Heino 1058fc2c767SKim B. Heino static const struct rtc_class_ops m41t94_rtc_ops = { 1068fc2c767SKim B. Heino .read_time = m41t94_read_time, 1078fc2c767SKim B. Heino .set_time = m41t94_set_time, 1088fc2c767SKim B. Heino }; 1098fc2c767SKim B. Heino 1108fc2c767SKim B. Heino static struct spi_driver m41t94_driver; 1118fc2c767SKim B. Heino 1125a167f45SGreg Kroah-Hartman static int m41t94_probe(struct spi_device *spi) 1138fc2c767SKim B. Heino { 1148fc2c767SKim B. Heino struct rtc_device *rtc; 1158fc2c767SKim B. Heino int res; 1168fc2c767SKim B. Heino 1178fc2c767SKim B. Heino spi->bits_per_word = 8; 1188fc2c767SKim B. Heino spi_setup(spi); 1198fc2c767SKim B. Heino 1208fc2c767SKim B. Heino res = spi_w8r8(spi, M41T94_REG_SECONDS); 1218fc2c767SKim B. Heino if (res < 0) { 1228fc2c767SKim B. Heino dev_err(&spi->dev, "not found.\n"); 1238fc2c767SKim B. Heino return res; 1248fc2c767SKim B. Heino } 1258fc2c767SKim B. Heino 126fb320d0aSJingoo Han rtc = devm_rtc_device_register(&spi->dev, m41t94_driver.driver.name, 127fb320d0aSJingoo Han &m41t94_rtc_ops, THIS_MODULE); 1288fc2c767SKim B. Heino if (IS_ERR(rtc)) 1298fc2c767SKim B. Heino return PTR_ERR(rtc); 1308fc2c767SKim B. Heino 131ee62474dSJingoo Han spi_set_drvdata(spi, rtc); 1328fc2c767SKim B. Heino 1338fc2c767SKim B. Heino return 0; 1348fc2c767SKim B. Heino } 1358fc2c767SKim B. Heino 1368fc2c767SKim B. Heino static struct spi_driver m41t94_driver = { 1378fc2c767SKim B. Heino .driver = { 1388fc2c767SKim B. Heino .name = "rtc-m41t94", 1398fc2c767SKim B. Heino }, 1408fc2c767SKim B. Heino .probe = m41t94_probe, 1418fc2c767SKim B. Heino }; 1428fc2c767SKim B. Heino 143109e9418SAxel Lin module_spi_driver(m41t94_driver); 1448fc2c767SKim B. Heino 1458fc2c767SKim B. Heino MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); 1468fc2c767SKim B. Heino MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); 1478fc2c767SKim B. Heino MODULE_LICENSE("GPL"); 148e0626e38SAnton Vorontsov MODULE_ALIAS("spi:rtc-m41t94"); 149