diff options
author | Sven Schnelle <svens@stackframe.org> | 2012-10-04 20:13:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:05:01 -0400 |
commit | 7418a1198991beb5bf6656b2ac0bcbde378c5539 (patch) | |
tree | 8805574089fb5bbad34ab38abbf6b6ac6edc5720 /drivers/rtc | |
parent | 92589c986b3360ce15d239fd5113a856412a0b3f (diff) |
rtc: add Dallas DS2404 driver
[akpm@linux-foundation.org: fix warning]
Signed-off-by: Sven Schnelle <svens@stackframe.org>
Cc: 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')
-rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds2404.c | 303 |
3 files changed, 313 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a4a805c273bb..d4681c97f7cc 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -660,6 +660,15 @@ config RTC_DRV_V3020 | |||
660 | This driver can also be built as a module. If so, the module | 660 | This driver can also be built as a module. If so, the module |
661 | will be called rtc-v3020. | 661 | will be called rtc-v3020. |
662 | 662 | ||
663 | config RTC_DRV_DS2404 | ||
664 | tristate "Dallas DS2404" | ||
665 | help | ||
666 | If you say yes here you get support for the | ||
667 | Dallas DS2404 RTC chip. | ||
668 | |||
669 | This driver can also be built as a module. If so, the module | ||
670 | will be called rtc-ds2404. | ||
671 | |||
663 | config RTC_DRV_WM831X | 672 | config RTC_DRV_WM831X |
664 | tristate "Wolfson Microelectronics WM831x RTC" | 673 | tristate "Wolfson Microelectronics WM831x RTC" |
665 | depends on MFD_WM831X | 674 | depends on MFD_WM831X |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0d5b2b66f90d..7ecdb2983dde 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -43,6 +43,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o | |||
43 | obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o | 43 | obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o |
44 | obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o | 44 | obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o |
45 | obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o | 45 | obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o |
46 | obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o | ||
46 | obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o | 47 | obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o |
47 | obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o | 48 | obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o |
48 | obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o | 49 | obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o |
diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c new file mode 100644 index 000000000000..5ea9df7c8c31 --- /dev/null +++ b/drivers/rtc/rtc-ds2404.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Sven Schnelle <svens@stackframe.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/rtc.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/bcd.h> | ||
16 | #include <linux/rtc-ds2404.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | #include <linux/io.h> | ||
22 | |||
23 | #define DS2404_STATUS_REG 0x200 | ||
24 | #define DS2404_CONTROL_REG 0x201 | ||
25 | #define DS2404_RTC_REG 0x202 | ||
26 | |||
27 | #define DS2404_WRITE_SCRATCHPAD_CMD 0x0f | ||
28 | #define DS2404_READ_SCRATCHPAD_CMD 0xaa | ||
29 | #define DS2404_COPY_SCRATCHPAD_CMD 0x55 | ||
30 | #define DS2404_READ_MEMORY_CMD 0xf0 | ||
31 | |||
32 | struct ds2404; | ||
33 | |||
34 | struct ds2404_chip_ops { | ||
35 | int (*map_io)(struct ds2404 *chip, struct platform_device *pdev, | ||
36 | struct ds2404_platform_data *pdata); | ||
37 | void (*unmap_io)(struct ds2404 *chip); | ||
38 | }; | ||
39 | |||
40 | #define DS2404_RST 0 | ||
41 | #define DS2404_CLK 1 | ||
42 | #define DS2404_DQ 2 | ||
43 | |||
44 | struct ds2404_gpio { | ||
45 | const char *name; | ||
46 | unsigned int gpio; | ||
47 | }; | ||
48 | |||
49 | struct ds2404 { | ||
50 | struct ds2404_gpio *gpio; | ||
51 | struct ds2404_chip_ops *ops; | ||
52 | struct rtc_device *rtc; | ||
53 | }; | ||
54 | |||
55 | static struct ds2404_gpio ds2404_gpio[] = { | ||
56 | { "RTC RST", 0 }, | ||
57 | { "RTC CLK", 0 }, | ||
58 | { "RTC DQ", 0 }, | ||
59 | }; | ||
60 | |||
61 | static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev, | ||
62 | struct ds2404_platform_data *pdata) | ||
63 | { | ||
64 | int i, err; | ||
65 | |||
66 | ds2404_gpio[DS2404_RST].gpio = pdata->gpio_rst; | ||
67 | ds2404_gpio[DS2404_CLK].gpio = pdata->gpio_clk; | ||
68 | ds2404_gpio[DS2404_DQ].gpio = pdata->gpio_dq; | ||
69 | |||
70 | for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) { | ||
71 | err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name); | ||
72 | if (err) { | ||
73 | printk(KERN_ERR "error mapping gpio %s: %d\n", | ||
74 | ds2404_gpio[i].name, err); | ||
75 | goto err_request; | ||
76 | } | ||
77 | if (i != DS2404_DQ) | ||
78 | gpio_direction_output(ds2404_gpio[i].gpio, 1); | ||
79 | } | ||
80 | |||
81 | chip->gpio = ds2404_gpio; | ||
82 | return 0; | ||
83 | |||
84 | err_request: | ||
85 | while (--i >= 0) | ||
86 | gpio_free(ds2404_gpio[i].gpio); | ||
87 | return err; | ||
88 | } | ||
89 | |||
90 | static void ds2404_gpio_unmap(struct ds2404 *chip) | ||
91 | { | ||
92 | int i; | ||
93 | |||
94 | for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) | ||
95 | gpio_free(ds2404_gpio[i].gpio); | ||
96 | } | ||
97 | |||
98 | static struct ds2404_chip_ops ds2404_gpio_ops = { | ||
99 | .map_io = ds2404_gpio_map, | ||
100 | .unmap_io = ds2404_gpio_unmap, | ||
101 | }; | ||
102 | |||
103 | static void ds2404_reset(struct device *dev) | ||
104 | { | ||
105 | gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0); | ||
106 | udelay(1000); | ||
107 | gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 1); | ||
108 | gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); | ||
109 | gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 0); | ||
110 | udelay(10); | ||
111 | } | ||
112 | |||
113 | static void ds2404_write_byte(struct device *dev, u8 byte) | ||
114 | { | ||
115 | int i; | ||
116 | |||
117 | gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 1); | ||
118 | for (i = 0; i < 8; i++) { | ||
119 | gpio_set_value(ds2404_gpio[DS2404_DQ].gpio, byte & (1 << i)); | ||
120 | udelay(10); | ||
121 | gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1); | ||
122 | udelay(10); | ||
123 | gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); | ||
124 | udelay(10); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | static u8 ds2404_read_byte(struct device *dev) | ||
129 | { | ||
130 | int i; | ||
131 | u8 ret = 0; | ||
132 | |||
133 | gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio); | ||
134 | |||
135 | for (i = 0; i < 8; i++) { | ||
136 | gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); | ||
137 | udelay(10); | ||
138 | if (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio)) | ||
139 | ret |= 1 << i; | ||
140 | gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1); | ||
141 | udelay(10); | ||
142 | } | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | static void ds2404_read_memory(struct device *dev, u16 offset, | ||
147 | int length, u8 *out) | ||
148 | { | ||
149 | ds2404_reset(dev); | ||
150 | ds2404_write_byte(dev, DS2404_READ_MEMORY_CMD); | ||
151 | ds2404_write_byte(dev, offset & 0xff); | ||
152 | ds2404_write_byte(dev, (offset >> 8) & 0xff); | ||
153 | while (length--) | ||
154 | *out++ = ds2404_read_byte(dev); | ||
155 | } | ||
156 | |||
157 | static void ds2404_write_memory(struct device *dev, u16 offset, | ||
158 | int length, u8 *out) | ||
159 | { | ||
160 | int i; | ||
161 | u8 ta01, ta02, es; | ||
162 | |||
163 | ds2404_reset(dev); | ||
164 | ds2404_write_byte(dev, DS2404_WRITE_SCRATCHPAD_CMD); | ||
165 | ds2404_write_byte(dev, offset & 0xff); | ||
166 | ds2404_write_byte(dev, (offset >> 8) & 0xff); | ||
167 | |||
168 | for (i = 0; i < length; i++) | ||
169 | ds2404_write_byte(dev, out[i]); | ||
170 | |||
171 | ds2404_reset(dev); | ||
172 | ds2404_write_byte(dev, DS2404_READ_SCRATCHPAD_CMD); | ||
173 | |||
174 | ta01 = ds2404_read_byte(dev); | ||
175 | ta02 = ds2404_read_byte(dev); | ||
176 | es = ds2404_read_byte(dev); | ||
177 | |||
178 | for (i = 0; i < length; i++) { | ||
179 | if (out[i] != ds2404_read_byte(dev)) { | ||
180 | printk(KERN_ERR "read invalid data\n"); | ||
181 | return; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | ds2404_reset(dev); | ||
186 | ds2404_write_byte(dev, DS2404_COPY_SCRATCHPAD_CMD); | ||
187 | ds2404_write_byte(dev, ta01); | ||
188 | ds2404_write_byte(dev, ta02); | ||
189 | ds2404_write_byte(dev, es); | ||
190 | |||
191 | gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio); | ||
192 | while (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio)) | ||
193 | ; | ||
194 | } | ||
195 | |||
196 | static void ds2404_enable_osc(struct device *dev) | ||
197 | { | ||
198 | u8 in[1] = { 0x10 }; /* enable oscillator */ | ||
199 | ds2404_write_memory(dev, 0x201, 1, in); | ||
200 | } | ||
201 | |||
202 | static int ds2404_read_time(struct device *dev, struct rtc_time *dt) | ||
203 | { | ||
204 | unsigned long time = 0; | ||
205 | |||
206 | ds2404_read_memory(dev, 0x203, 4, (u8 *)&time); | ||
207 | time = le32_to_cpu(time); | ||
208 | |||
209 | rtc_time_to_tm(time, dt); | ||
210 | return rtc_valid_tm(dt); | ||
211 | } | ||
212 | |||
213 | static int ds2404_set_mmss(struct device *dev, unsigned long secs) | ||
214 | { | ||
215 | u32 time = cpu_to_le32(secs); | ||
216 | ds2404_write_memory(dev, 0x203, 4, (u8 *)&time); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static const struct rtc_class_ops ds2404_rtc_ops = { | ||
221 | .read_time = ds2404_read_time, | ||
222 | .set_mmss = ds2404_set_mmss, | ||
223 | }; | ||
224 | |||
225 | static int rtc_probe(struct platform_device *pdev) | ||
226 | { | ||
227 | struct ds2404_platform_data *pdata = pdev->dev.platform_data; | ||
228 | struct ds2404 *chip; | ||
229 | int retval = -EBUSY; | ||
230 | |||
231 | chip = kzalloc(sizeof(struct ds2404), GFP_KERNEL); | ||
232 | if (!chip) | ||
233 | return -ENOMEM; | ||
234 | |||
235 | chip->ops = &ds2404_gpio_ops; | ||
236 | |||
237 | retval = chip->ops->map_io(chip, pdev, pdata); | ||
238 | if (retval) | ||
239 | goto err_chip; | ||
240 | |||
241 | dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n", | ||
242 | chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio, | ||
243 | chip->gpio[DS2404_DQ].gpio); | ||
244 | |||
245 | platform_set_drvdata(pdev, chip); | ||
246 | |||
247 | chip->rtc = rtc_device_register("ds2404", | ||
248 | &pdev->dev, &ds2404_rtc_ops, THIS_MODULE); | ||
249 | if (IS_ERR(chip->rtc)) { | ||
250 | retval = PTR_ERR(chip->rtc); | ||
251 | goto err_io; | ||
252 | } | ||
253 | |||
254 | ds2404_enable_osc(&pdev->dev); | ||
255 | return 0; | ||
256 | |||
257 | err_io: | ||
258 | chip->ops->unmap_io(chip); | ||
259 | err_chip: | ||
260 | kfree(chip); | ||
261 | return retval; | ||
262 | } | ||
263 | |||
264 | static int rtc_remove(struct platform_device *dev) | ||
265 | { | ||
266 | struct ds2404 *chip = platform_get_drvdata(dev); | ||
267 | struct rtc_device *rtc = chip->rtc; | ||
268 | |||
269 | if (rtc) | ||
270 | rtc_device_unregister(rtc); | ||
271 | |||
272 | chip->ops->unmap_io(chip); | ||
273 | kfree(chip); | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static struct platform_driver rtc_device_driver = { | ||
279 | .probe = rtc_probe, | ||
280 | .remove = rtc_remove, | ||
281 | .driver = { | ||
282 | .name = "ds2404", | ||
283 | .owner = THIS_MODULE, | ||
284 | }, | ||
285 | }; | ||
286 | |||
287 | static __init int ds2404_init(void) | ||
288 | { | ||
289 | return platform_driver_register(&rtc_device_driver); | ||
290 | } | ||
291 | |||
292 | static __exit void ds2404_exit(void) | ||
293 | { | ||
294 | platform_driver_unregister(&rtc_device_driver); | ||
295 | } | ||
296 | |||
297 | module_init(ds2404_init); | ||
298 | module_exit(ds2404_exit); | ||
299 | |||
300 | MODULE_DESCRIPTION("DS2404 RTC"); | ||
301 | MODULE_AUTHOR("Sven Schnelle"); | ||
302 | MODULE_LICENSE("GPL"); | ||
303 | MODULE_ALIAS("platform:ds2404"); | ||