aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-ds1302.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2008-02-06 04:38:44 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 13:41:13 -0500
commit739d340dba45ab786a5553144bbffbee0afe15dd (patch)
treeb0a92107f04ce5717fc5ca4ccb711a9ac1fd5747 /drivers/rtc/rtc-ds1302.c
parente07e232cd96ef0092b2bddc72f9b7caf284633cb (diff)
rtc: ds1302 rtc support
This adds a basic ds1302 RTC driver, which is basically a cleanup and move of the in-tree SH SecureEdge5410 code (which is currently located in arch/sh/board/snapgear/rtc.c) to drivers/rtc. This aims to be a building block that the M32R and CRIS code can be worked on top of, so we can get rid of drivers/char/ds1302.c and arch/cris/arch-v10/drivers/ds1302.c respectively, though more work is needed for this. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Paul Mundt <lethal@linux-sh.org> Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-ds1302.c')
-rw-r--r--drivers/rtc/rtc-ds1302.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
new file mode 100644
index 000000000000..7b002ceeaa7d
--- /dev/null
+++ b/drivers/rtc/rtc-ds1302.c
@@ -0,0 +1,262 @@
1/*
2 * Dallas DS1302 RTC Support
3 *
4 * Copyright (C) 2002 David McCullough
5 * Copyright (C) 2003 - 2007 Paul Mundt
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License version 2. See the file "COPYING" in the main directory of
9 * this archive for more details.
10 */
11#include <linux/init.h>
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/platform_device.h>
15#include <linux/time.h>
16#include <linux/rtc.h>
17#include <linux/spinlock.h>
18#include <linux/io.h>
19#include <linux/bcd.h>
20#include <asm/rtc.h>
21
22#define DRV_NAME "rtc-ds1302"
23#define DRV_VERSION "0.1.0"
24
25#define RTC_CMD_READ 0x81 /* Read command */
26#define RTC_CMD_WRITE 0x80 /* Write command */
27
28#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */
29#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */
30#define RTC_ADDR_YEAR 0x06 /* Address of year register */
31#define RTC_ADDR_DAY 0x05 /* Address of day of week register */
32#define RTC_ADDR_MON 0x04 /* Address of month register */
33#define RTC_ADDR_DATE 0x03 /* Address of day of month register */
34#define RTC_ADDR_HOUR 0x02 /* Address of hour register */
35#define RTC_ADDR_MIN 0x01 /* Address of minute register */
36#define RTC_ADDR_SEC 0x00 /* Address of second register */
37
38#define RTC_RESET 0x1000
39#define RTC_IODATA 0x0800
40#define RTC_SCLK 0x0400
41
42#ifdef CONFIG_SH_SECUREEDGE5410
43#include <asm/snapgear.h>
44#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
45#define get_dp() SECUREEDGE_READ_IOPORT()
46#else
47#error "Add support for your platform"
48#endif
49
50struct ds1302_rtc {
51 struct rtc_device *rtc_dev;
52 spinlock_t lock;
53};
54
55static void ds1302_sendbits(unsigned int val)
56{
57 int i;
58
59 for (i = 8; (i); i--, val >>= 1) {
60 set_dp((get_dp() & ~RTC_IODATA) | ((val & 0x1) ?
61 RTC_IODATA : 0));
62 set_dp(get_dp() | RTC_SCLK); /* clock high */
63 set_dp(get_dp() & ~RTC_SCLK); /* clock low */
64 }
65}
66
67static unsigned int ds1302_recvbits(void)
68{
69 unsigned int val;
70 int i;
71
72 for (i = 0, val = 0; (i < 8); i++) {
73 val |= (((get_dp() & RTC_IODATA) ? 1 : 0) << i);
74 set_dp(get_dp() | RTC_SCLK); /* clock high */
75 set_dp(get_dp() & ~RTC_SCLK); /* clock low */
76 }
77
78 return val;
79}
80
81static unsigned int ds1302_readbyte(unsigned int addr)
82{
83 unsigned int val;
84
85 set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
86
87 set_dp(get_dp() | RTC_RESET);
88 ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_READ);
89 val = ds1302_recvbits();
90 set_dp(get_dp() & ~RTC_RESET);
91
92 return val;
93}
94
95static void ds1302_writebyte(unsigned int addr, unsigned int val)
96{
97 set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
98 set_dp(get_dp() | RTC_RESET);
99 ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_WRITE);
100 ds1302_sendbits(val);
101 set_dp(get_dp() & ~RTC_RESET);
102}
103
104static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm)
105{
106 struct ds1302_rtc *rtc = dev_get_drvdata(dev);
107
108 spin_lock_irq(&rtc->lock);
109
110 tm->tm_sec = BCD2BIN(ds1302_readbyte(RTC_ADDR_SEC));
111 tm->tm_min = BCD2BIN(ds1302_readbyte(RTC_ADDR_MIN));
112 tm->tm_hour = BCD2BIN(ds1302_readbyte(RTC_ADDR_HOUR));
113 tm->tm_wday = BCD2BIN(ds1302_readbyte(RTC_ADDR_DAY));
114 tm->tm_mday = BCD2BIN(ds1302_readbyte(RTC_ADDR_DATE));
115 tm->tm_mon = BCD2BIN(ds1302_readbyte(RTC_ADDR_MON)) - 1;
116 tm->tm_year = BCD2BIN(ds1302_readbyte(RTC_ADDR_YEAR));
117
118 if (tm->tm_year < 70)
119 tm->tm_year += 100;
120
121 spin_unlock_irq(&rtc->lock);
122
123 dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
124 "mday=%d, mon=%d, year=%d, wday=%d\n",
125 __FUNCTION__,
126 tm->tm_sec, tm->tm_min, tm->tm_hour,
127 tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
128
129 if (rtc_valid_tm(tm) < 0)
130 dev_err(dev, "invalid date\n");
131
132 return 0;
133}
134
135static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm)
136{
137 struct ds1302_rtc *rtc = dev_get_drvdata(dev);
138
139 spin_lock_irq(&rtc->lock);
140
141 /* Stop RTC */
142 ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80);
143
144 ds1302_writebyte(RTC_ADDR_SEC, BIN2BCD(tm->tm_sec));
145 ds1302_writebyte(RTC_ADDR_MIN, BIN2BCD(tm->tm_min));
146 ds1302_writebyte(RTC_ADDR_HOUR, BIN2BCD(tm->tm_hour));
147 ds1302_writebyte(RTC_ADDR_DAY, BIN2BCD(tm->tm_wday));
148 ds1302_writebyte(RTC_ADDR_DATE, BIN2BCD(tm->tm_mday));
149 ds1302_writebyte(RTC_ADDR_MON, BIN2BCD(tm->tm_mon + 1));
150 ds1302_writebyte(RTC_ADDR_YEAR, BIN2BCD(tm->tm_year % 100));
151
152 /* Start RTC */
153 ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80);
154
155 spin_unlock_irq(&rtc->lock);
156
157 return 0;
158}
159
160static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd,
161 unsigned long arg)
162{
163 switch (cmd) {
164#ifdef RTC_SET_CHARGE
165 case RTC_SET_CHARGE:
166 {
167 struct ds1302_rtc *rtc = dev_get_drvdata(dev);
168 int tcs_val;
169
170 if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int)))
171 return -EFAULT;
172
173 spin_lock_irq(&rtc->lock);
174 ds1302_writebyte(RTC_ADDR_TCR, (0xa0 | tcs_val * 0xf));
175 spin_unlock_irq(&rtc->lock);
176 return 0;
177 }
178#endif
179 }
180
181 return -ENOIOCTLCMD;
182}
183
184static struct rtc_class_ops ds1302_rtc_ops = {
185 .read_time = ds1302_rtc_read_time,
186 .set_time = ds1302_rtc_set_time,
187 .ioctl = ds1302_rtc_ioctl,
188};
189
190static int __devinit ds1302_rtc_probe(struct platform_device *pdev)
191{
192 struct ds1302_rtc *rtc;
193 int ret;
194
195 /* Reset */
196 set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
197
198 /* Write a magic value to the DS1302 RAM, and see if it sticks. */
199 ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
200 if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
201 return -ENODEV;
202
203 rtc = kzalloc(sizeof(struct ds1302_rtc), GFP_KERNEL);
204 if (unlikely(!rtc))
205 return -ENOMEM;
206
207 spin_lock_init(&rtc->lock);
208 rtc->rtc_dev = rtc_device_register("ds1302", &pdev->dev,
209 &ds1302_rtc_ops, THIS_MODULE);
210 if (IS_ERR(rtc->rtc_dev)) {
211 ret = PTR_ERR(rtc->rtc_dev);
212 goto out;
213 }
214
215 platform_set_drvdata(pdev, rtc);
216
217 return 0;
218out:
219 kfree(rtc);
220 return ret;
221}
222
223static int __devexit ds1302_rtc_remove(struct platform_device *pdev)
224{
225 struct ds1302_rtc *rtc = platform_get_drvdata(pdev);
226
227 if (likely(rtc->rtc_dev))
228 rtc_device_unregister(rtc->rtc_dev);
229
230 platform_set_drvdata(pdev, NULL);
231
232 kfree(rtc);
233
234 return 0;
235}
236
237static struct platform_driver ds1302_platform_driver = {
238 .driver = {
239 .name = DRV_NAME,
240 .owner = THIS_MODULE,
241 },
242 .probe = ds1302_rtc_probe,
243 .remove = __devexit_p(ds1302_rtc_remove),
244};
245
246static int __init ds1302_rtc_init(void)
247{
248 return platform_driver_register(&ds1302_platform_driver);
249}
250
251static void __exit ds1302_rtc_exit(void)
252{
253 platform_driver_unregister(&ds1302_platform_driver);
254}
255
256module_init(ds1302_rtc_init);
257module_exit(ds1302_rtc_exit);
258
259MODULE_DESCRIPTION("Dallas DS1302 RTC driver");
260MODULE_VERSION(DRV_VERSION);
261MODULE_AUTHOR("Paul Mundt, David McCullough");
262MODULE_LICENSE("GPL v2");