11d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo/* 21d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * ST M48T86 / Dallas DS12887 RTC driver 31d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * Copyright (c) 2006 Tower Technologies 41d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * 51d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * Author: Alessandro Zummo <a.zummo@towertech.it> 61d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * 71d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * This program is free software; you can redistribute it and/or modify 81d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * it under the terms of the GNU General Public License version 2 as 91d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * published by the Free Software Foundation. 101d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * 111d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * This drivers only supports the clock running in BCD and 24H mode. 121d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * If it will be ever adapted to binary and 12H mode, care must be taken 131d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo * to not introduce bugs. 141d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo */ 151d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 161d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#include <linux/module.h> 171d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#include <linux/rtc.h> 181d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#include <linux/platform_device.h> 191d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#include <linux/m48t86.h> 201d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#include <linux/bcd.h> 211d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 221d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_SEC 0x00 231d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_SECALRM 0x01 241d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_MIN 0x02 251d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_MINALRM 0x03 26f90a65060e6a71a818abc3584ac64f986b838fbaAlessandro Zummo#define M48T86_REG_HOUR 0x04 271d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_HOURALRM 0x05 281d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_DOW 0x06 /* 1 = sunday */ 291d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_DOM 0x07 301d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_MONTH 0x08 /* 1 - 12 */ 311d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_YEAR 0x09 /* 0 - 99 */ 321d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_A 0x0A 331d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_B 0x0B 341d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_C 0x0C 351d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_D 0x0D 361d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 371d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_B_H24 (1 << 1) 381d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_B_DM (1 << 2) 391d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_B_SET (1 << 7) 401d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define M48T86_REG_D_VRT (1 << 7) 411d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 421d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo#define DRV_VERSION "0.1" 431d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 441d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 451d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummostatic int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) 461d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo{ 471d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo unsigned char reg; 481d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct platform_device *pdev = to_platform_device(dev); 491d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct m48t86_ops *ops = pdev->dev.platform_data; 501d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 512d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton reg = ops->readbyte(M48T86_REG_B); 521d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 531d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo if (reg & M48T86_REG_B_DM) { 541d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* data (binary) mode */ 552d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_sec = ops->readbyte(M48T86_REG_SEC); 562d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_min = ops->readbyte(M48T86_REG_MIN); 572d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F; 582d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_mday = ops->readbyte(M48T86_REG_DOM); 591d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* tm_mon is 0-11 */ 602d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1; 612d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100; 622d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton tm->tm_wday = ops->readbyte(M48T86_REG_DOW); 631d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo } else { 641d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* bcd mode */ 65fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_sec = bcd2bin(ops->readbyte(M48T86_REG_SEC)); 66fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_min = bcd2bin(ops->readbyte(M48T86_REG_MIN)); 67fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_hour = bcd2bin(ops->readbyte(M48T86_REG_HOUR) & 0x3F); 68fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_mday = bcd2bin(ops->readbyte(M48T86_REG_DOM)); 691d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* tm_mon is 0-11 */ 70fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_mon = bcd2bin(ops->readbyte(M48T86_REG_MONTH)) - 1; 71fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_year = bcd2bin(ops->readbyte(M48T86_REG_YEAR)) + 100; 72fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk tm->tm_wday = bcd2bin(ops->readbyte(M48T86_REG_DOW)); 731d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo } 741d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 751d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* correct the hour if the clock is in 12h mode */ 761d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo if (!(reg & M48T86_REG_B_H24)) 772d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton if (ops->readbyte(M48T86_REG_HOUR) & 0x80) 781d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo tm->tm_hour += 12; 791d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 8052142ed416f85dcc3e2061e720511a1e69ac3d93Wan ZongShun return rtc_valid_tm(tm); 811d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo} 821d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 831d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummostatic int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) 841d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo{ 851d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo unsigned char reg; 861d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct platform_device *pdev = to_platform_device(dev); 871d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct m48t86_ops *ops = pdev->dev.platform_data; 881d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 892d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton reg = ops->readbyte(M48T86_REG_B); 901d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 911d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* update flag and 24h mode */ 921d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo reg |= M48T86_REG_B_SET | M48T86_REG_B_H24; 932d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(reg, M48T86_REG_B); 941d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 951d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo if (reg & M48T86_REG_B_DM) { 961d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* data (binary) mode */ 972d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_sec, M48T86_REG_SEC); 982d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_min, M48T86_REG_MIN); 992d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_hour, M48T86_REG_HOUR); 1002d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_mday, M48T86_REG_DOM); 1012d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH); 1022d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR); 1032d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(tm->tm_wday, M48T86_REG_DOW); 1041d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo } else { 1051d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* bcd mode */ 106fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_sec), M48T86_REG_SEC); 107fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_min), M48T86_REG_MIN); 108fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_hour), M48T86_REG_HOUR); 109fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_mday), M48T86_REG_DOM); 110fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_mon + 1), M48T86_REG_MONTH); 111fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_year % 100), M48T86_REG_YEAR); 112fe20ba70abf7d6e5855c3dacc729490b3d0d077fAdrian Bunk ops->writebyte(bin2bcd(tm->tm_wday), M48T86_REG_DOW); 1131d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo } 1141d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1151d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* update ended */ 1161d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo reg &= ~M48T86_REG_B_SET; 1172d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton ops->writebyte(reg, M48T86_REG_B); 1181d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1191d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo return 0; 1201d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo} 1211d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1221d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummostatic int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) 1231d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo{ 1241d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo unsigned char reg; 1251d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct platform_device *pdev = to_platform_device(dev); 1261d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct m48t86_ops *ops = pdev->dev.platform_data; 1271d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1282d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton reg = ops->readbyte(M48T86_REG_B); 1291d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1301d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo seq_printf(seq, "mode\t\t: %s\n", 1311d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); 1321d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1332d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton reg = ops->readbyte(M48T86_REG_D); 1341d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1351d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo seq_printf(seq, "battery\t\t: %s\n", 1361d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); 1371d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1381d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo return 0; 1391d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo} 1401d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 141ff8371ac9a5a55c956991fed8e5f58640c7a32f3David Brownellstatic const struct rtc_class_ops m48t86_rtc_ops = { 1421d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .read_time = m48t86_rtc_read_time, 1431d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .set_time = m48t86_rtc_set_time, 1441d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .proc = m48t86_rtc_proc, 1451d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo}; 1461d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1471d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummostatic int __devinit m48t86_rtc_probe(struct platform_device *dev) 1481d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo{ 1491d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo unsigned char reg; 1501d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct m48t86_ops *ops = dev->dev.platform_data; 1511d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct rtc_device *rtc = rtc_device_register("m48t86", 1521d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo &dev->dev, &m48t86_rtc_ops, THIS_MODULE); 1531d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 154d1d65b7712016ca5ff2e44470eb13e772999de94Alessandro Zummo if (IS_ERR(rtc)) 1551d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo return PTR_ERR(rtc); 1561d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1571d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo platform_set_drvdata(dev, rtc); 1581d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1591d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo /* read battery status */ 1602d7b20c1884777e66009be1a533641c19c4705f6Andrew Morton reg = ops->readbyte(M48T86_REG_D); 1611d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo dev_info(&dev->dev, "battery %s\n", 1621d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); 1631d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1641d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo return 0; 1651d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo} 1661d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1671d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummostatic int __devexit m48t86_rtc_remove(struct platform_device *dev) 1681d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo{ 1691d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo struct rtc_device *rtc = platform_get_drvdata(dev); 1701d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1711d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo if (rtc) 1721d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo rtc_device_unregister(rtc); 1731d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1741d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo platform_set_drvdata(dev, NULL); 1751d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1761d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo return 0; 1771d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo} 1781d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1791d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummostatic struct platform_driver m48t86_rtc_platform_driver = { 1801d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .driver = { 1811d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .name = "rtc-m48t86", 1821d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .owner = THIS_MODULE, 1831d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo }, 1841d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .probe = m48t86_rtc_probe, 1851d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo .remove = __devexit_p(m48t86_rtc_remove), 1861d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo}; 1871d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1880c4eae66591a292fee70051ea363a8d27aa54102Axel Linmodule_platform_driver(m48t86_rtc_platform_driver); 1891d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro Zummo 1901d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro ZummoMODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 1911d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro ZummoMODULE_DESCRIPTION("M48T86 RTC driver"); 1921d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro ZummoMODULE_LICENSE("GPL"); 1931d98af87270cc08bb8251e004b9dc63cc838f24bAlessandro ZummoMODULE_VERSION(DRV_VERSION); 194ad28a07bcadc5945f7a90d9de3a196825e69d9d3Kay SieversMODULE_ALIAS("platform:rtc-m48t86"); 195