11c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König/*
21c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König * Real Time Clock driver for Freescale MC13XXX PMIC
31c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König *
41c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König * (C) 2009 Sascha Hauer, Pengutronix
51c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König * (C) 2009 Uwe Kleine-Koenig, Pengutronix
61c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König *
71c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König * This program is free software; you can redistribute it and/or modify
81c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König * it under the terms of the GNU General Public License version 2 as
91c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König * published by the Free Software Foundation.
101c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König */
111c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
121c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#include <linux/mfd/mc13xxx.h>
131c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#include <linux/platform_device.h>
141c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#include <linux/kernel.h>
151c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#include <linux/module.h>
161c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#include <linux/slab.h>
171c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#include <linux/rtc.h>
181c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
191c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#define DRIVER_NAME "mc13xxx-rtc"
201c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
211c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#define MC13XXX_RTCTOD	20
221c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#define MC13XXX_RTCTODA	21
231c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#define MC13XXX_RTCDAY	22
241c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König#define MC13XXX_RTCDAYA	23
251c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
266a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan#define SEC_PER_DAY	(24 * 60 * 60)
276a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan
281c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstruct mc13xxx_rtc {
291c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct rtc_device *rtc;
301c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx *mc13xxx;
311c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int valid;
321c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König};
331c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
341c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic int mc13xxx_rtc_irq_enable_unlocked(struct device *dev,
351c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		unsigned int enabled, int irq)
361c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
371c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
381c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int (*func)(struct mc13xxx *mc13xxx, int irq);
391c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
401c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (!priv->valid)
411c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		return -ENODATA;
421c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
431c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask;
441c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return func(priv->mc13xxx, irq);
451c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
461c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
472fb004a9b00e34908e59a1c1c5770910690a7588Alexander Shiyanstatic int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
482fb004a9b00e34908e59a1c1c5770910690a7588Alexander Shiyan					unsigned int enabled)
491c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
501c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
511c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int ret;
521c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
531c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_lock(priv->mc13xxx);
541c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
552fb004a9b00e34908e59a1c1c5770910690a7588Alexander Shiyan	ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, MC13XXX_IRQ_TODA);
561c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
571c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_unlock(priv->mc13xxx);
581c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
591c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return ret;
601c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
611c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
621c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
631c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
641c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
651c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned int seconds, days1, days2;
661c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
6712de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan	if (!priv->valid)
6812de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan		return -ENODATA;
691c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
706a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	do {
716a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		int ret;
721c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
736a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
746a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		if (ret)
756a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan			return ret;
761c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
776a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
786a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		if (ret)
796a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan			return ret;
801c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
816a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
826a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan		if (ret)
836a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan			return ret;
846a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	} while (days1 != days2);
851c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
866a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	rtc_time_to_tm(days1 * SEC_PER_DAY + seconds, tm);
871c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
881c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return rtc_valid_tm(tm);
891c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
901c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
911c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs)
921c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
931c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
941c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned int seconds, days;
951c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned int alarmseconds;
961c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int ret;
971c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
986a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	seconds = secs % SEC_PER_DAY;
996a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	days = secs / SEC_PER_DAY;
1001c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1011c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_lock(priv->mc13xxx);
1021c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1031c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	/*
1041c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	 * temporarily invalidate alarm to prevent triggering it when the day is
1051c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	 * already updated while the time isn't yet.
1061c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	 */
1071c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &alarmseconds);
1081c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
1091c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1101c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1116a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	if (alarmseconds < SEC_PER_DAY) {
1121c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		ret = mc13xxx_reg_write(priv->mc13xxx,
1131c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König				MC13XXX_RTCTODA, 0x1ffff);
1141c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		if (unlikely(ret))
1151c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			goto out;
1161c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	}
1171c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1181c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	/*
1191c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	 * write seconds=0 to prevent a day switch between writing days
1201c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	 * and seconds below
1211c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	 */
1221c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, 0);
1231c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
1241c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1251c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1261c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAY, days);
1271c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
1281c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1291c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1301c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, seconds);
1311c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
1321c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1331c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1341c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	/* restore alarm */
1356a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	if (alarmseconds < SEC_PER_DAY) {
1361c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		ret = mc13xxx_reg_write(priv->mc13xxx,
1371c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König				MC13XXX_RTCTODA, alarmseconds);
1381c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		if (unlikely(ret))
1391c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			goto out;
1401c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	}
1411c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
14212de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan	if (!priv->valid) {
14312de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan		ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
14412de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan		if (unlikely(ret))
14512de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan			goto out;
14612de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan
14712de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan		ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
14812de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan	}
1491c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1501c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königout:
1511c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	priv->valid = !ret;
1521c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1531c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_unlock(priv->mc13xxx);
1541c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1551c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return ret;
1561c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
1571c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1581c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
1591c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
1601c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
1611c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned seconds, days;
1621c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned long s1970;
1631c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int enabled, pending;
1641c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int ret;
1651c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1661c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_lock(priv->mc13xxx);
1671c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1681c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds);
1691c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
1701c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1716a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	if (seconds >= SEC_PER_DAY) {
1721c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		ret = -ENODATA;
1731c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1741c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	}
1751c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1761c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days);
1771c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
1781c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
1791c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1801c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_irq_status(priv->mc13xxx, MC13XXX_IRQ_TODA,
1811c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			&enabled, &pending);
1821c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1831c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königout:
1841c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_unlock(priv->mc13xxx);
1851c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1861c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (ret)
1871c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		return ret;
1881c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1891c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	alarm->enabled = enabled;
1901c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	alarm->pending = pending;
1911c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1926a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	s1970 = days * SEC_PER_DAY + seconds;
1931c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1941c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	rtc_time_to_tm(s1970, &alarm->time);
1951c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	dev_dbg(dev, "%s: %lu\n", __func__, s1970);
1961c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
1971c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return 0;
1981c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
1991c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2001c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
2011c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
2021c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
2031c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned long s1970;
2041c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	unsigned seconds, days;
2051c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int ret;
2061c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2071c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_lock(priv->mc13xxx);
2081c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2091c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	/* disable alarm to prevent false triggering */
2101c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff);
2111c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
2121c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
2131c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2141c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TODA);
2151c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
2161c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
2171c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2181c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = rtc_tm_to_time(&alarm->time, &s1970);
2191c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
2201c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
2211c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2221c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff",
2231c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			s1970);
2241c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2251c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled,
2261c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			MC13XXX_IRQ_TODA);
2271c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
2281c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
2291c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2306a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	seconds = s1970 % SEC_PER_DAY;
2316a2b342228b98432a585c83d59ed6aa0620be7c4Alexander Shiyan	days = s1970 / SEC_PER_DAY;
2321c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2331c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days);
2341c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (unlikely(ret))
2351c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		goto out;
2361c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2371c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, seconds);
2381c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2391c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königout:
2401c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_unlock(priv->mc13xxx);
2411c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2421c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return ret;
2431c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
2441c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2451c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev)
2461c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
2471c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev;
2481c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx *mc13xxx = priv->mc13xxx;
2491c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2501c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
2511c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2521c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_irq_ack(mc13xxx, irq);
2531c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2541c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return IRQ_HANDLED;
2551c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
2561c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2571c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev)
2581c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
2591c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev;
2601c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx *mc13xxx = priv->mc13xxx;
2611c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2621c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
2631c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2641c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_irq_ack(mc13xxx, irq);
2651c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2661c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return IRQ_HANDLED;
2671c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
2681c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2691c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic const struct rtc_class_ops mc13xxx_rtc_ops = {
2701c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.read_time = mc13xxx_rtc_read_time,
2711c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.set_mmss = mc13xxx_rtc_set_mmss,
2721c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.read_alarm = mc13xxx_rtc_read_alarm,
2731c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.set_alarm = mc13xxx_rtc_set_alarm,
2741c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable,
2751c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König};
2761c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2771c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
2781c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
2791c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = dev;
2801c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx *mc13xxx = priv->mc13xxx;
2811c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2821c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	priv->valid = 0;
2831c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2841c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_irq_mask(mc13xxx, irq);
2851c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2861c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return IRQ_HANDLED;
2871c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
2881c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
289db5cf8d1ac4ac3fa06d89345154ce20068aeb097Uwe Kleine-Königstatic int __init mc13xxx_rtc_probe(struct platform_device *pdev)
2901c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
2911c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	int ret;
2921c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv;
2931c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx *mc13xxx;
2941c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
295a379fd2458d78d3286c8103aa479839b073c89feJingoo Han	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
2961c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (!priv)
2971c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		return -ENOMEM;
2981c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
2991c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx = dev_get_drvdata(pdev->dev.parent);
3001c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	priv->mc13xxx = mc13xxx;
30112de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan	priv->valid = 1;
3021c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3031c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	platform_set_drvdata(pdev, priv);
3041c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3051c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_lock(mc13xxx);
3061c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
30712de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan	mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST);
30812de362108d5ec24cce1bbe520570dc8fdccca9cAlexander Shiyan
3091c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST,
3101c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
3111c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (ret)
3125ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan		goto err_irq_request;
3131c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
314fb1bd9a22da3d0f2436ffa95fdc0382b89659288Alexander Shiyan	ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_1HZ,
3151c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			mc13xxx_rtc_update_handler, DRIVER_NAME, priv);
3161c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	if (ret)
3175ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan		goto err_irq_request;
3181c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3191c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA,
3201c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König			mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv);
3215ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan	if (ret)
3225ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan		goto err_irq_request;
3231c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3245ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan	mc13xxx_unlock(mc13xxx);
3255ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan
3265ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan	priv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
3275ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan					     &mc13xxx_rtc_ops, THIS_MODULE);
3281c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3295ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan	return 0;
3305ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan
3315ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyanerr_irq_request:
3325ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan	mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
3335ab9a52a3af613b74a7cda033c1623c7913c50d9Alexander Shiyan	mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv);
334589e501439f9866b4bc415743b0ae0bb9a461c69Alexander Shiyan	mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
3351c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
336589e501439f9866b4bc415743b0ae0bb9a461c69Alexander Shiyan	mc13xxx_unlock(mc13xxx);
3371c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3381c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return ret;
3391c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
3401c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
34177bf2ea8de78ce7dd842409a847b888e720f62d8Alexander Shiyanstatic int mc13xxx_rtc_remove(struct platform_device *pdev)
3421c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König{
3431c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
3441c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3451c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_lock(priv->mc13xxx);
3461c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3471c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TODA, priv);
3481c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_1HZ, priv);
3491c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_RTCRST, priv);
3501c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3511c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	mc13xxx_unlock(priv->mc13xxx);
3521c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3531c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	return 0;
3541c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König}
3551c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
356a46481d7af1e6c59c03f3ddac400d9054f804952Axel Linstatic const struct platform_device_id mc13xxx_rtc_idtable[] = {
3571c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	{
3581c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		.name = "mc13783-rtc",
3591c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	}, {
3601c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		.name = "mc13892-rtc",
36119a1ac505d5f3545ebc5b0a195d65cef4da94bb5Uwe Kleine-König	}, {
36219a1ac505d5f3545ebc5b0a195d65cef4da94bb5Uwe Kleine-König		.name = "mc34708-rtc",
3631c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	},
3640f636fc16f80fe64261ce39440e49a259ff2b0daUwe Kleine-König	{ /* sentinel */ }
3651c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König};
3660f636fc16f80fe64261ce39440e49a259ff2b0daUwe Kleine-KönigMODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
3671c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3681c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-Königstatic struct platform_driver mc13xxx_rtc_driver = {
3691c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.id_table = mc13xxx_rtc_idtable,
37077bf2ea8de78ce7dd842409a847b888e720f62d8Alexander Shiyan	.remove = mc13xxx_rtc_remove,
3711c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	.driver = {
3721c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		.name = DRIVER_NAME,
3731c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König		.owner = THIS_MODULE,
3741c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König	},
3751c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König};
3761c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
37709fb7ad15aa62116f96e0f7b62c7723e636786f1Jingoo Hanmodule_platform_driver_probe(mc13xxx_rtc_driver, &mc13xxx_rtc_probe);
3781c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-König
3791c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-KönigMODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
3801c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-KönigMODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
3811c97872b80691f6bd3e46ec431a0d59dc75cb8daUwe Kleine-KönigMODULE_LICENSE("GPL v2");
382