aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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
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')
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-ds1302.c262
3 files changed, 269 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 842d8a5616ba..0cc448c0eddd 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -306,6 +306,12 @@ config RTC_DRV_DS1216
306 help 306 help
307 If you say yes here you get support for the Dallas DS1216 RTC chips. 307 If you say yes here you get support for the Dallas DS1216 RTC chips.
308 308
309config RTC_DRV_DS1302
310 tristate "Dallas DS1302"
311 depends on SH_SECUREEDGE5410
312 help
313 If you say yes here you get support for the Dallas DS1302 RTC chips.
314
309config RTC_DRV_DS1553 315config RTC_DRV_DS1553
310 tristate "Maxim/Dallas DS1553" 316 tristate "Maxim/Dallas DS1553"
311 help 317 help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 465db4dd50b2..b5c3d3996444 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
22obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o 22obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
23obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o 23obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
24obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o 24obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
25obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
25obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o 26obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
26obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o 27obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o
27obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o 28obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
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");