aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorSven Schnelle <svens@stackframe.org>2012-10-04 20:13:47 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-05 14:05:01 -0400
commit7418a1198991beb5bf6656b2ac0bcbde378c5539 (patch)
tree8805574089fb5bbad34ab38abbf6b6ac6edc5720 /drivers/rtc
parent92589c986b3360ce15d239fd5113a856412a0b3f (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/Kconfig9
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-ds2404.c303
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
663config 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
663config RTC_DRV_WM831X 672config 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
43obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o 43obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
44obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o 44obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
45obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o 45obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
46obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o
46obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o 47obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
47obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o 48obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
48obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o 49obj-$(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
32struct ds2404;
33
34struct 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
44struct ds2404_gpio {
45 const char *name;
46 unsigned int gpio;
47};
48
49struct ds2404 {
50 struct ds2404_gpio *gpio;
51 struct ds2404_chip_ops *ops;
52 struct rtc_device *rtc;
53};
54
55static struct ds2404_gpio ds2404_gpio[] = {
56 { "RTC RST", 0 },
57 { "RTC CLK", 0 },
58 { "RTC DQ", 0 },
59};
60
61static 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
84err_request:
85 while (--i >= 0)
86 gpio_free(ds2404_gpio[i].gpio);
87 return err;
88}
89
90static 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
98static struct ds2404_chip_ops ds2404_gpio_ops = {
99 .map_io = ds2404_gpio_map,
100 .unmap_io = ds2404_gpio_unmap,
101};
102
103static 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
113static 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
128static 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
146static 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
157static 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
196static 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
202static 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
213static 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
220static const struct rtc_class_ops ds2404_rtc_ops = {
221 .read_time = ds2404_read_time,
222 .set_mmss = ds2404_set_mmss,
223};
224
225static 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
257err_io:
258 chip->ops->unmap_io(chip);
259err_chip:
260 kfree(chip);
261 return retval;
262}
263
264static 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
278static 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
287static __init int ds2404_init(void)
288{
289 return platform_driver_register(&rtc_device_driver);
290}
291
292static __exit void ds2404_exit(void)
293{
294 platform_driver_unregister(&rtc_device_driver);
295}
296
297module_init(ds2404_init);
298module_exit(ds2404_exit);
299
300MODULE_DESCRIPTION("DS2404 RTC");
301MODULE_AUTHOR("Sven Schnelle");
302MODULE_LICENSE("GPL");
303MODULE_ALIAS("platform:ds2404");