diff options
-rw-r--r-- | drivers/rtc/Kconfig | 6 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1216.c | 226 |
3 files changed, 233 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index bcd18765a4ad..6210280135c8 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -280,6 +280,12 @@ config RTC_DRV_CMOS | |||
280 | This driver can also be built as a module. If so, the module | 280 | This driver can also be built as a module. If so, the module |
281 | will be called rtc-cmos. | 281 | will be called rtc-cmos. |
282 | 282 | ||
283 | config RTC_DRV_DS1216 | ||
284 | tristate "Dallas DS1216" | ||
285 | depends on RTC_CLASS && SNI_RM | ||
286 | help | ||
287 | If you say yes here you get support for the Dallas DS1216 RTC chips. | ||
288 | |||
283 | config RTC_DRV_DS1553 | 289 | config RTC_DRV_DS1553 |
284 | tristate "Dallas DS1553" | 290 | tristate "Dallas DS1553" |
285 | depends on RTC_CLASS | 291 | depends on RTC_CLASS |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index ca9166d319b8..3109af9a1651 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -44,3 +44,4 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o | |||
44 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o | 44 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o |
45 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o | 45 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o |
46 | obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o | 46 | obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o |
47 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o | ||
diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c new file mode 100644 index 000000000000..83efb88f8f23 --- /dev/null +++ b/drivers/rtc/rtc-ds1216.c | |||
@@ -0,0 +1,226 @@ | |||
1 | /* | ||
2 | * Dallas DS1216 RTC driver | ||
3 | * | ||
4 | * Copyright (c) 2007 Thomas Bogendoerfer | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/rtc.h> | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/bcd.h> | ||
12 | |||
13 | #define DRV_VERSION "0.1" | ||
14 | |||
15 | struct ds1216_regs { | ||
16 | u8 tsec; | ||
17 | u8 sec; | ||
18 | u8 min; | ||
19 | u8 hour; | ||
20 | u8 wday; | ||
21 | u8 mday; | ||
22 | u8 month; | ||
23 | u8 year; | ||
24 | }; | ||
25 | |||
26 | #define DS1216_HOUR_1224 (1 << 7) | ||
27 | #define DS1216_HOUR_AMPM (1 << 5) | ||
28 | |||
29 | struct ds1216_priv { | ||
30 | struct rtc_device *rtc; | ||
31 | void __iomem *ioaddr; | ||
32 | size_t size; | ||
33 | unsigned long baseaddr; | ||
34 | }; | ||
35 | |||
36 | static const u8 magic[] = { | ||
37 | 0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c | ||
38 | }; | ||
39 | |||
40 | /* | ||
41 | * Read the 64 bit we'd like to have - It a series | ||
42 | * of 64 bits showing up in the LSB of the base register. | ||
43 | * | ||
44 | */ | ||
45 | static void ds1216_read(u8 __iomem *ioaddr, u8 *buf) | ||
46 | { | ||
47 | unsigned char c; | ||
48 | int i, j; | ||
49 | |||
50 | for (i = 0; i < 8; i++) { | ||
51 | c = 0; | ||
52 | for (j = 0; j < 8; j++) | ||
53 | c |= (readb(ioaddr) & 0x1) << j; | ||
54 | buf[i] = c; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | static void ds1216_write(u8 __iomem *ioaddr, const u8 *buf) | ||
59 | { | ||
60 | unsigned char c; | ||
61 | int i, j; | ||
62 | |||
63 | for (i = 0; i < 8; i++) { | ||
64 | c = buf[i]; | ||
65 | for (j = 0; j < 8; j++) { | ||
66 | writeb(c, ioaddr); | ||
67 | c = c >> 1; | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | static void ds1216_switch_ds_to_clock(u8 __iomem *ioaddr) | ||
73 | { | ||
74 | /* Reset magic pointer */ | ||
75 | readb(ioaddr); | ||
76 | /* Write 64 bit magic to DS1216 */ | ||
77 | ds1216_write(ioaddr, magic); | ||
78 | } | ||
79 | |||
80 | static int ds1216_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
81 | { | ||
82 | struct platform_device *pdev = to_platform_device(dev); | ||
83 | struct ds1216_priv *priv = platform_get_drvdata(pdev); | ||
84 | struct ds1216_regs regs; | ||
85 | |||
86 | ds1216_switch_ds_to_clock(priv->ioaddr); | ||
87 | ds1216_read(priv->ioaddr, (u8 *)®s); | ||
88 | |||
89 | tm->tm_sec = BCD2BIN(regs.sec); | ||
90 | tm->tm_min = BCD2BIN(regs.min); | ||
91 | if (regs.hour & DS1216_HOUR_1224) { | ||
92 | /* AM/PM mode */ | ||
93 | tm->tm_hour = BCD2BIN(regs.hour & 0x1f); | ||
94 | if (regs.hour & DS1216_HOUR_AMPM) | ||
95 | tm->tm_hour += 12; | ||
96 | } else | ||
97 | tm->tm_hour = BCD2BIN(regs.hour & 0x3f); | ||
98 | tm->tm_wday = (regs.wday & 7) - 1; | ||
99 | tm->tm_mday = BCD2BIN(regs.mday & 0x3f); | ||
100 | tm->tm_mon = BCD2BIN(regs.month & 0x1f); | ||
101 | tm->tm_year = BCD2BIN(regs.year); | ||
102 | if (tm->tm_year < 70) | ||
103 | tm->tm_year += 100; | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
108 | { | ||
109 | struct platform_device *pdev = to_platform_device(dev); | ||
110 | struct ds1216_priv *priv = platform_get_drvdata(pdev); | ||
111 | struct ds1216_regs regs; | ||
112 | |||
113 | ds1216_switch_ds_to_clock(priv->ioaddr); | ||
114 | ds1216_read(priv->ioaddr, (u8 *)®s); | ||
115 | |||
116 | regs.tsec = 0; /* clear 0.1 and 0.01 seconds */ | ||
117 | regs.sec = BIN2BCD(tm->tm_sec); | ||
118 | regs.min = BIN2BCD(tm->tm_min); | ||
119 | regs.hour &= DS1216_HOUR_1224; | ||
120 | if (regs.hour && tm->tm_hour > 12) { | ||
121 | regs.hour |= DS1216_HOUR_AMPM; | ||
122 | tm->tm_hour -= 12; | ||
123 | } | ||
124 | regs.hour |= BIN2BCD(tm->tm_hour); | ||
125 | regs.wday &= ~7; | ||
126 | regs.wday |= tm->tm_wday; | ||
127 | regs.mday = BIN2BCD(tm->tm_mday); | ||
128 | regs.month = BIN2BCD(tm->tm_mon); | ||
129 | regs.year = BIN2BCD(tm->tm_year % 100); | ||
130 | |||
131 | ds1216_switch_ds_to_clock(priv->ioaddr); | ||
132 | ds1216_write(priv->ioaddr, (u8 *)®s); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static const struct rtc_class_ops ds1216_rtc_ops = { | ||
137 | .read_time = ds1216_rtc_read_time, | ||
138 | .set_time = ds1216_rtc_set_time, | ||
139 | }; | ||
140 | |||
141 | static int __devinit ds1216_rtc_probe(struct platform_device *pdev) | ||
142 | { | ||
143 | struct rtc_device *rtc; | ||
144 | struct resource *res; | ||
145 | struct ds1216_priv *priv; | ||
146 | int ret = 0; | ||
147 | u8 dummy[8]; | ||
148 | |||
149 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
150 | if (!res) | ||
151 | return -ENODEV; | ||
152 | priv = kzalloc(sizeof *priv, GFP_KERNEL); | ||
153 | if (!priv) | ||
154 | return -ENOMEM; | ||
155 | priv->size = res->end - res->start + 1; | ||
156 | if (!request_mem_region(res->start, priv->size, pdev->name)) { | ||
157 | ret = -EBUSY; | ||
158 | goto out; | ||
159 | } | ||
160 | priv->baseaddr = res->start; | ||
161 | priv->ioaddr = ioremap(priv->baseaddr, priv->size); | ||
162 | if (!priv->ioaddr) { | ||
163 | ret = -ENOMEM; | ||
164 | goto out; | ||
165 | } | ||
166 | rtc = rtc_device_register("ds1216", &pdev->dev, | ||
167 | &ds1216_rtc_ops, THIS_MODULE); | ||
168 | if (IS_ERR(rtc)) { | ||
169 | ret = PTR_ERR(rtc); | ||
170 | goto out; | ||
171 | } | ||
172 | priv->rtc = rtc; | ||
173 | platform_set_drvdata(pdev, priv); | ||
174 | |||
175 | /* dummy read to get clock into a known state */ | ||
176 | ds1216_read(priv->ioaddr, dummy); | ||
177 | return 0; | ||
178 | |||
179 | out: | ||
180 | if (priv->rtc) | ||
181 | rtc_device_unregister(priv->rtc); | ||
182 | if (priv->ioaddr) | ||
183 | iounmap(priv->ioaddr); | ||
184 | if (priv->baseaddr) | ||
185 | release_mem_region(priv->baseaddr, priv->size); | ||
186 | kfree(priv); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | static int __devexit ds1216_rtc_remove(struct platform_device *pdev) | ||
191 | { | ||
192 | struct ds1216_priv *priv = platform_get_drvdata(pdev); | ||
193 | |||
194 | rtc_device_unregister(priv->rtc); | ||
195 | iounmap(priv->ioaddr); | ||
196 | release_mem_region(priv->baseaddr, priv->size); | ||
197 | kfree(priv); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static struct platform_driver ds1216_rtc_platform_driver = { | ||
202 | .driver = { | ||
203 | .name = "rtc-ds1216", | ||
204 | .owner = THIS_MODULE, | ||
205 | }, | ||
206 | .probe = ds1216_rtc_probe, | ||
207 | .remove = __devexit_p(ds1216_rtc_remove), | ||
208 | }; | ||
209 | |||
210 | static int __init ds1216_rtc_init(void) | ||
211 | { | ||
212 | return platform_driver_register(&ds1216_rtc_platform_driver); | ||
213 | } | ||
214 | |||
215 | static void __exit ds1216_rtc_exit(void) | ||
216 | { | ||
217 | platform_driver_unregister(&ds1216_rtc_platform_driver); | ||
218 | } | ||
219 | |||
220 | MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); | ||
221 | MODULE_DESCRIPTION("DS1216 RTC driver"); | ||
222 | MODULE_LICENSE("GPL"); | ||
223 | MODULE_VERSION(DRV_VERSION); | ||
224 | |||
225 | module_init(ds1216_rtc_init); | ||
226 | module_exit(ds1216_rtc_exit); | ||