Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Sven Schnelle and committed by
Linus Torvalds
7418a119 92589c98

+333
+9
drivers/rtc/Kconfig
··· 660 660 This driver can also be built as a module. If so, the module 661 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 672 config RTC_DRV_WM831X 664 673 tristate "Wolfson Microelectronics WM831x RTC" 665 674 depends on MFD_WM831X
+1
drivers/rtc/Makefile
··· 43 43 obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o 44 44 obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o 45 45 obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o 46 + obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o 46 47 obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o 47 48 obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o 48 49 obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
+303
drivers/rtc/rtc-ds2404.c
··· 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");
+20
include/linux/rtc-ds2404.h
··· 1 + /* 2 + * ds2404.h - platform data structure for the DS2404 RTC. 3 + * 4 + * This file is subject to the terms and conditions of the GNU General Public 5 + * License. See the file "COPYING" in the main directory of this archive 6 + * for more details. 7 + * 8 + * Copyright (C) 2012 Sven Schnelle <svens@stackframe.org> 9 + */ 10 + 11 + #ifndef __LINUX_DS2404_H 12 + #define __LINUX_DS2404_H 13 + 14 + struct ds2404_platform_data { 15 + 16 + unsigned int gpio_rst; 17 + unsigned int gpio_clk; 18 + unsigned int gpio_dq; 19 + }; 20 + #endif