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