| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 2000 Nils Faerber | 
 | 5 |  * | 
 | 6 |  * Based on rtc.c by Paul Gortmaker | 
 | 7 |  * | 
 | 8 |  * Original Driver by Nils Faerber <nils@kernelconcepts.de> | 
 | 9 |  * | 
 | 10 |  * Modifications from: | 
 | 11 |  *   CIH <cih@coventive.com> | 
| Nicolas Pitre | 2f82af0 | 2009-09-14 03:25:28 -0400 | [diff] [blame] | 12 |  *   Nicolas Pitre <nico@fluxnic.net> | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 13 |  *   Andrew Christian <andrew.christian@hp.com> | 
 | 14 |  * | 
 | 15 |  * Converted to the RTC subsystem and Driver Model | 
 | 16 |  *   by Richard Purdie <rpurdie@rpsys.net> | 
 | 17 |  * | 
 | 18 |  * This program is free software; you can redistribute it and/or | 
 | 19 |  * modify it under the terms of the GNU General Public License | 
 | 20 |  * as published by the Free Software Foundation; either version | 
 | 21 |  * 2 of the License, or (at your option) any later version. | 
 | 22 |  */ | 
 | 23 |  | 
 | 24 | #include <linux/platform_device.h> | 
 | 25 | #include <linux/module.h> | 
| Haojian Zhuang | 8e8bbcb | 2012-02-23 23:36:37 +0800 | [diff] [blame] | 26 | #include <linux/clk.h> | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 27 | #include <linux/rtc.h> | 
 | 28 | #include <linux/init.h> | 
 | 29 | #include <linux/fs.h> | 
 | 30 | #include <linux/interrupt.h> | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 31 | #include <linux/slab.h> | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 32 | #include <linux/string.h> | 
| Haojian Zhuang | 8bec2e9 | 2012-03-05 19:26:42 +0800 | [diff] [blame] | 33 | #include <linux/of.h> | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 34 | #include <linux/pm.h> | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 35 | #include <linux/bitops.h> | 
| Rob Herring | 23019a7 | 2012-03-20 14:33:19 -0500 | [diff] [blame] | 36 | #include <linux/io.h> | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 37 |  | 
| Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 38 | #include <mach/hardware.h> | 
| Rob Herring | 905cdc8 | 2012-02-23 14:27:58 +0100 | [diff] [blame] | 39 | #include <mach/irqs.h> | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 40 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 41 | #if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP) | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 42 | #include <mach/regs-rtc.h> | 
 | 43 | #endif | 
 | 44 |  | 
| Marcelo Roberto Jimenez | a404ad1 | 2010-10-18 22:33:53 +0100 | [diff] [blame] | 45 | #define RTC_DEF_DIVIDER		(32768 - 1) | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 46 | #define RTC_DEF_TRIM		0 | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 47 | #define RTC_FREQ		1024 | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 48 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 49 | struct sa1100_rtc { | 
 | 50 | 	spinlock_t		lock; | 
 | 51 | 	int			irq_1hz; | 
 | 52 | 	int			irq_alarm; | 
 | 53 | 	struct rtc_device	*rtc; | 
| Haojian Zhuang | 8e8bbcb | 2012-02-23 23:36:37 +0800 | [diff] [blame] | 54 | 	struct clk		*clk; | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 55 | }; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 56 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 57 | static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 58 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 59 | 	struct sa1100_rtc *info = dev_get_drvdata(dev_id); | 
 | 60 | 	struct rtc_device *rtc = info->rtc; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 61 | 	unsigned int rtsr; | 
 | 62 | 	unsigned long events = 0; | 
 | 63 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 64 | 	spin_lock(&info->lock); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 65 |  | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 66 | 	rtsr = RTSR; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 67 | 	/* clear interrupt sources */ | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 68 | 	RTSR = 0; | 
| Marcelo Roberto Jimenez | 7decaa5 | 2010-10-18 22:35:54 +0100 | [diff] [blame] | 69 | 	/* Fix for a nasty initialization problem the in SA11xx RTSR register. | 
 | 70 | 	 * See also the comments in sa1100_rtc_probe(). */ | 
 | 71 | 	if (rtsr & (RTSR_ALE | RTSR_HZE)) { | 
 | 72 | 		/* This is the original code, before there was the if test | 
 | 73 | 		 * above. This code does not clear interrupts that were not | 
 | 74 | 		 * enabled. */ | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 75 | 		RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); | 
| Marcelo Roberto Jimenez | 7decaa5 | 2010-10-18 22:35:54 +0100 | [diff] [blame] | 76 | 	} else { | 
 | 77 | 		/* For some reason, it is possible to enter this routine | 
 | 78 | 		 * without interruptions enabled, it has been tested with | 
 | 79 | 		 * several units (Bug in SA11xx chip?). | 
 | 80 | 		 * | 
 | 81 | 		 * This situation leads to an infinite "loop" of interrupt | 
 | 82 | 		 * routine calling and as a result the processor seems to | 
 | 83 | 		 * lock on its first call to open(). */ | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 84 | 		RTSR = RTSR_AL | RTSR_HZ; | 
| Marcelo Roberto Jimenez | 7decaa5 | 2010-10-18 22:35:54 +0100 | [diff] [blame] | 85 | 	} | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 86 |  | 
 | 87 | 	/* clear alarm interrupt if it has occurred */ | 
 | 88 | 	if (rtsr & RTSR_AL) | 
 | 89 | 		rtsr &= ~RTSR_ALE; | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 90 | 	RTSR = rtsr & (RTSR_ALE | RTSR_HZE); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 91 |  | 
 | 92 | 	/* update irq data & counter */ | 
 | 93 | 	if (rtsr & RTSR_AL) | 
 | 94 | 		events |= RTC_AF | RTC_IRQF; | 
 | 95 | 	if (rtsr & RTSR_HZ) | 
 | 96 | 		events |= RTC_UF | RTC_IRQF; | 
 | 97 |  | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 98 | 	rtc_update_irq(rtc, 1, events); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 99 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 100 | 	spin_unlock(&info->lock); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 101 |  | 
 | 102 | 	return IRQ_HANDLED; | 
 | 103 | } | 
 | 104 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 105 | static int sa1100_rtc_open(struct device *dev) | 
 | 106 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 107 | 	struct sa1100_rtc *info = dev_get_drvdata(dev); | 
 | 108 | 	struct rtc_device *rtc = info->rtc; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 109 | 	int ret; | 
 | 110 |  | 
| Linus Torvalds | 3480059 | 2012-03-27 16:41:24 -0700 | [diff] [blame] | 111 | 	ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 112 | 	if (ret) { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 113 | 		dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 114 | 		goto fail_ui; | 
 | 115 | 	} | 
| Linus Torvalds | 3480059 | 2012-03-27 16:41:24 -0700 | [diff] [blame] | 116 | 	ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 117 | 	if (ret) { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 118 | 		dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 119 | 		goto fail_ai; | 
 | 120 | 	} | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 121 | 	rtc->max_user_freq = RTC_FREQ; | 
 | 122 | 	rtc_irq_set_freq(rtc, NULL, RTC_FREQ); | 
| Marcelo Roberto Jimenez | d2ccb52 | 2010-12-16 21:31:32 +0100 | [diff] [blame] | 123 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 124 | 	return 0; | 
 | 125 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 126 |  fail_ai: | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 127 | 	free_irq(info->irq_1hz, dev); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 128 |  fail_ui: | 
| Haojian Zhuang | 8e8bbcb | 2012-02-23 23:36:37 +0800 | [diff] [blame] | 129 | 	clk_disable_unprepare(info->clk); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 130 | 	return ret; | 
 | 131 | } | 
 | 132 |  | 
 | 133 | static void sa1100_rtc_release(struct device *dev) | 
 | 134 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 135 | 	struct sa1100_rtc *info = dev_get_drvdata(dev); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 136 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 137 | 	spin_lock_irq(&info->lock); | 
 | 138 | 	RTSR = 0; | 
 | 139 | 	spin_unlock_irq(&info->lock); | 
 | 140 |  | 
 | 141 | 	free_irq(info->irq_alarm, dev); | 
 | 142 | 	free_irq(info->irq_1hz, dev); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 143 | } | 
 | 144 |  | 
| John Stultz | 16380c1 | 2011-02-02 17:02:41 -0800 | [diff] [blame] | 145 | static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | 
 | 146 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 147 | 	struct sa1100_rtc *info = dev_get_drvdata(dev); | 
 | 148 |  | 
 | 149 | 	spin_lock_irq(&info->lock); | 
| John Stultz | 16380c1 | 2011-02-02 17:02:41 -0800 | [diff] [blame] | 150 | 	if (enabled) | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 151 | 		RTSR |= RTSR_ALE; | 
| John Stultz | 16380c1 | 2011-02-02 17:02:41 -0800 | [diff] [blame] | 152 | 	else | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 153 | 		RTSR &= ~RTSR_ALE; | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 154 | 	spin_unlock_irq(&info->lock); | 
| John Stultz | 16380c1 | 2011-02-02 17:02:41 -0800 | [diff] [blame] | 155 | 	return 0; | 
 | 156 | } | 
 | 157 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 158 | static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) | 
 | 159 | { | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 160 | 	rtc_time_to_tm(RCNR, tm); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 161 | 	return 0; | 
 | 162 | } | 
 | 163 |  | 
 | 164 | static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) | 
 | 165 | { | 
 | 166 | 	unsigned long time; | 
 | 167 | 	int ret; | 
 | 168 |  | 
 | 169 | 	ret = rtc_tm_to_time(tm, &time); | 
 | 170 | 	if (ret == 0) | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 171 | 		RCNR = time; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 172 | 	return ret; | 
 | 173 | } | 
 | 174 |  | 
 | 175 | static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | 
 | 176 | { | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 177 | 	u32	rtsr; | 
| David Brownell | 32b49da | 2007-02-20 13:58:13 -0800 | [diff] [blame] | 178 |  | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 179 | 	rtsr = RTSR; | 
| David Brownell | 32b49da | 2007-02-20 13:58:13 -0800 | [diff] [blame] | 180 | 	alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; | 
 | 181 | 	alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 182 | 	return 0; | 
 | 183 | } | 
 | 184 |  | 
 | 185 | static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | 
 | 186 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 187 | 	struct sa1100_rtc *info = dev_get_drvdata(dev); | 
| Haojian Zhuang | 1d8c38c | 2012-02-20 19:49:30 +0800 | [diff] [blame] | 188 | 	unsigned long time; | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 189 | 	int ret; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 190 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 191 | 	spin_lock_irq(&info->lock); | 
| Haojian Zhuang | 1d8c38c | 2012-02-20 19:49:30 +0800 | [diff] [blame] | 192 | 	ret = rtc_tm_to_time(&alrm->time, &time); | 
 | 193 | 	if (ret != 0) | 
 | 194 | 		goto out; | 
 | 195 | 	RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); | 
 | 196 | 	RTAR = time; | 
 | 197 | 	if (alrm->enabled) | 
 | 198 | 		RTSR |= RTSR_ALE; | 
 | 199 | 	else | 
 | 200 | 		RTSR &= ~RTSR_ALE; | 
 | 201 | out: | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 202 | 	spin_unlock_irq(&info->lock); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 203 |  | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 204 | 	return ret; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 205 | } | 
 | 206 |  | 
 | 207 | static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) | 
 | 208 | { | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 209 | 	seq_printf(seq, "trim/divider\t\t: 0x%08x\n", (u32) RTTR); | 
 | 210 | 	seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", (u32)RTSR); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 211 |  | 
 | 212 | 	return 0; | 
 | 213 | } | 
 | 214 |  | 
| David Brownell | ff8371a | 2006-09-30 23:28:17 -0700 | [diff] [blame] | 215 | static const struct rtc_class_ops sa1100_rtc_ops = { | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 216 | 	.open = sa1100_rtc_open, | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 217 | 	.release = sa1100_rtc_release, | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 218 | 	.read_time = sa1100_rtc_read_time, | 
 | 219 | 	.set_time = sa1100_rtc_set_time, | 
 | 220 | 	.read_alarm = sa1100_rtc_read_alarm, | 
 | 221 | 	.set_alarm = sa1100_rtc_set_alarm, | 
 | 222 | 	.proc = sa1100_rtc_proc, | 
| John Stultz | 16380c1 | 2011-02-02 17:02:41 -0800 | [diff] [blame] | 223 | 	.alarm_irq_enable = sa1100_rtc_alarm_irq_enable, | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 224 | }; | 
 | 225 |  | 
 | 226 | static int sa1100_rtc_probe(struct platform_device *pdev) | 
 | 227 | { | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 228 | 	struct rtc_device *rtc; | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 229 | 	struct sa1100_rtc *info; | 
 | 230 | 	int irq_1hz, irq_alarm, ret = 0; | 
 | 231 |  | 
 | 232 | 	irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); | 
 | 233 | 	irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); | 
 | 234 | 	if (irq_1hz < 0 || irq_alarm < 0) | 
 | 235 | 		return -ENODEV; | 
 | 236 |  | 
 | 237 | 	info = kzalloc(sizeof(struct sa1100_rtc), GFP_KERNEL); | 
 | 238 | 	if (!info) | 
 | 239 | 		return -ENOMEM; | 
| Haojian Zhuang | 8e8bbcb | 2012-02-23 23:36:37 +0800 | [diff] [blame] | 240 | 	info->clk = clk_get(&pdev->dev, NULL); | 
 | 241 | 	if (IS_ERR(info->clk)) { | 
 | 242 | 		dev_err(&pdev->dev, "failed to find rtc clock source\n"); | 
 | 243 | 		ret = PTR_ERR(info->clk); | 
 | 244 | 		goto err_clk; | 
 | 245 | 	} | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 246 | 	info->irq_1hz = irq_1hz; | 
 | 247 | 	info->irq_alarm = irq_alarm; | 
 | 248 | 	spin_lock_init(&info->lock); | 
 | 249 | 	platform_set_drvdata(pdev, info); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 250 |  | 
| Chao Xie | 0cc0c38 | 2013-02-21 16:45:17 -0800 | [diff] [blame] | 251 | 	ret = clk_prepare_enable(info->clk); | 
 | 252 | 	if (ret) | 
 | 253 | 		goto err_enable_clk; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 254 | 	/* | 
 | 255 | 	 * According to the manual we should be able to let RTTR be zero | 
 | 256 | 	 * and then a default diviser for a 32.768KHz clock is used. | 
 | 257 | 	 * Apparently this doesn't work, at least for my SA1110 rev 5. | 
 | 258 | 	 * If the clock divider is uninitialized then reset it to the | 
 | 259 | 	 * default value to get the 1Hz clock. | 
 | 260 | 	 */ | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 261 | 	if (RTTR == 0) { | 
 | 262 | 		RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); | 
 | 263 | 		dev_warn(&pdev->dev, "warning: " | 
 | 264 | 			"initializing default clock divider/trim value\n"); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 265 | 		/* The current RTC value probably doesn't make sense either */ | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 266 | 		RCNR = 0; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 267 | 	} | 
 | 268 |  | 
| Uli Luckas | e5a2c9c | 2008-06-18 09:54:03 +0100 | [diff] [blame] | 269 | 	device_init_wakeup(&pdev->dev, 1); | 
 | 270 |  | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 271 | 	rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, | 
 | 272 | 		THIS_MODULE); | 
 | 273 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 274 | 	if (IS_ERR(rtc)) { | 
 | 275 | 		ret = PTR_ERR(rtc); | 
 | 276 | 		goto err_dev; | 
 | 277 | 	} | 
 | 278 | 	info->rtc = rtc; | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 279 |  | 
| Marcelo Roberto Jimenez | 7decaa5 | 2010-10-18 22:35:54 +0100 | [diff] [blame] | 280 | 	/* Fix for a nasty initialization problem the in SA11xx RTSR register. | 
 | 281 | 	 * See also the comments in sa1100_rtc_interrupt(). | 
 | 282 | 	 * | 
 | 283 | 	 * Sometimes bit 1 of the RTSR (RTSR_HZ) will wake up 1, which means an | 
 | 284 | 	 * interrupt pending, even though interrupts were never enabled. | 
 | 285 | 	 * In this case, this bit it must be reset before enabling | 
 | 286 | 	 * interruptions to avoid a nonexistent interrupt to occur. | 
 | 287 | 	 * | 
 | 288 | 	 * In principle, the same problem would apply to bit 0, although it has | 
 | 289 | 	 * never been observed to happen. | 
 | 290 | 	 * | 
 | 291 | 	 * This issue is addressed both here and in sa1100_rtc_interrupt(). | 
 | 292 | 	 * If the issue is not addressed here, in the times when the processor | 
 | 293 | 	 * wakes up with the bit set there will be one spurious interrupt. | 
 | 294 | 	 * | 
 | 295 | 	 * The issue is also dealt with in sa1100_rtc_interrupt() to be on the | 
 | 296 | 	 * safe side, once the condition that lead to this strange | 
 | 297 | 	 * initialization is unknown and could in principle happen during | 
 | 298 | 	 * normal processing. | 
 | 299 | 	 * | 
 | 300 | 	 * Notice that clearing bit 1 and 0 is accomplished by writting ONES to | 
 | 301 | 	 * the corresponding bits in RTSR. */ | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 302 | 	RTSR = RTSR_AL | RTSR_HZ; | 
| Marcelo Roberto Jimenez | 7decaa5 | 2010-10-18 22:35:54 +0100 | [diff] [blame] | 303 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 304 | 	return 0; | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 305 | err_dev: | 
| Chao Xie | 0cc0c38 | 2013-02-21 16:45:17 -0800 | [diff] [blame] | 306 | 	clk_disable_unprepare(info->clk); | 
 | 307 | err_enable_clk: | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 308 | 	platform_set_drvdata(pdev, NULL); | 
| Haojian Zhuang | 8e8bbcb | 2012-02-23 23:36:37 +0800 | [diff] [blame] | 309 | 	clk_put(info->clk); | 
 | 310 | err_clk: | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 311 | 	kfree(info); | 
 | 312 | 	return ret; | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 313 | } | 
 | 314 |  | 
 | 315 | static int sa1100_rtc_remove(struct platform_device *pdev) | 
 | 316 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 317 | 	struct sa1100_rtc *info = platform_get_drvdata(pdev); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 318 |  | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 319 | 	if (info) { | 
 | 320 | 		rtc_device_unregister(info->rtc); | 
| Chao Xie | 0cc0c38 | 2013-02-21 16:45:17 -0800 | [diff] [blame] | 321 | 		clk_disable_unprepare(info->clk); | 
| Haojian Zhuang | 8e8bbcb | 2012-02-23 23:36:37 +0800 | [diff] [blame] | 322 | 		clk_put(info->clk); | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 323 | 		platform_set_drvdata(pdev, NULL); | 
 | 324 | 		kfree(info); | 
 | 325 | 	} | 
| Russell King | a0164a5 | 2012-01-19 11:55:21 +0000 | [diff] [blame] | 326 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 327 | 	return 0; | 
 | 328 | } | 
 | 329 |  | 
| Russell King | 6bc54e6 | 2007-11-12 22:49:58 +0000 | [diff] [blame] | 330 | #ifdef CONFIG_PM | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 331 | static int sa1100_rtc_suspend(struct device *dev) | 
| Russell King | 6bc54e6 | 2007-11-12 22:49:58 +0000 | [diff] [blame] | 332 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 333 | 	struct sa1100_rtc *info = dev_get_drvdata(dev); | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 334 | 	if (device_may_wakeup(dev)) | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 335 | 		enable_irq_wake(info->irq_alarm); | 
| Russell King | 6bc54e6 | 2007-11-12 22:49:58 +0000 | [diff] [blame] | 336 | 	return 0; | 
 | 337 | } | 
 | 338 |  | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 339 | static int sa1100_rtc_resume(struct device *dev) | 
| Russell King | 6bc54e6 | 2007-11-12 22:49:58 +0000 | [diff] [blame] | 340 | { | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 341 | 	struct sa1100_rtc *info = dev_get_drvdata(dev); | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 342 | 	if (device_may_wakeup(dev)) | 
| Haojian Zhuang | 3888c09 | 2012-02-21 11:51:13 +0800 | [diff] [blame] | 343 | 		disable_irq_wake(info->irq_alarm); | 
| Russell King | 6bc54e6 | 2007-11-12 22:49:58 +0000 | [diff] [blame] | 344 | 	return 0; | 
 | 345 | } | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 346 |  | 
| Alexey Dobriyan | 4714521 | 2009-12-14 18:00:08 -0800 | [diff] [blame] | 347 | static const struct dev_pm_ops sa1100_rtc_pm_ops = { | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 348 | 	.suspend	= sa1100_rtc_suspend, | 
 | 349 | 	.resume		= sa1100_rtc_resume, | 
 | 350 | }; | 
| Russell King | 6bc54e6 | 2007-11-12 22:49:58 +0000 | [diff] [blame] | 351 | #endif | 
 | 352 |  | 
| Sachin Kamat | c8a6046 | 2013-02-21 16:44:28 -0800 | [diff] [blame] | 353 | #ifdef CONFIG_OF | 
| Haojian Zhuang | 8bec2e9 | 2012-03-05 19:26:42 +0800 | [diff] [blame] | 354 | static struct of_device_id sa1100_rtc_dt_ids[] = { | 
 | 355 | 	{ .compatible = "mrvl,sa1100-rtc", }, | 
 | 356 | 	{ .compatible = "mrvl,mmp-rtc", }, | 
 | 357 | 	{} | 
 | 358 | }; | 
 | 359 | MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids); | 
| Sachin Kamat | c8a6046 | 2013-02-21 16:44:28 -0800 | [diff] [blame] | 360 | #endif | 
| Haojian Zhuang | 8bec2e9 | 2012-03-05 19:26:42 +0800 | [diff] [blame] | 361 |  | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 362 | static struct platform_driver sa1100_rtc_driver = { | 
 | 363 | 	.probe		= sa1100_rtc_probe, | 
 | 364 | 	.remove		= sa1100_rtc_remove, | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 365 | 	.driver		= { | 
| Haojian Zhuang | 5d027cd | 2009-07-21 14:31:09 +0800 | [diff] [blame] | 366 | 		.name	= "sa1100-rtc", | 
 | 367 | #ifdef CONFIG_PM | 
 | 368 | 		.pm	= &sa1100_rtc_pm_ops, | 
 | 369 | #endif | 
| Sachin Kamat | c8a6046 | 2013-02-21 16:44:28 -0800 | [diff] [blame] | 370 | 		.of_match_table = of_match_ptr(sa1100_rtc_dt_ids), | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 371 | 	}, | 
 | 372 | }; | 
 | 373 |  | 
| Axel Lin | 0c4eae6 | 2012-01-10 15:10:48 -0800 | [diff] [blame] | 374 | module_platform_driver(sa1100_rtc_driver); | 
| Richard Purdie | e842f1c | 2006-03-27 01:16:46 -0800 | [diff] [blame] | 375 |  | 
 | 376 | MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); | 
 | 377 | MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)"); | 
 | 378 | MODULE_LICENSE("GPL"); | 
| Kay Sievers | ad28a07 | 2008-04-10 21:29:25 -0700 | [diff] [blame] | 379 | MODULE_ALIAS("platform:sa1100-rtc"); |