linux/drivers/rtc/rtc-ds2404.c
<<
>>
Prefs
   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                        dev_err(&pdev->dev, "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                        dev_err(dev, "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 = devm_kzalloc(&pdev->dev, 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 = devm_rtc_device_register(&pdev->dev, "ds2404",
 248                                        &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        return retval;
 261}
 262
 263static int rtc_remove(struct platform_device *dev)
 264{
 265        struct ds2404 *chip = platform_get_drvdata(dev);
 266
 267        chip->ops->unmap_io(chip);
 268
 269        return 0;
 270}
 271
 272static struct platform_driver rtc_device_driver = {
 273        .probe  = rtc_probe,
 274        .remove = rtc_remove,
 275        .driver = {
 276                .name   = "ds2404",
 277                .owner  = THIS_MODULE,
 278        },
 279};
 280module_platform_driver(rtc_device_driver);
 281
 282MODULE_DESCRIPTION("DS2404 RTC");
 283MODULE_AUTHOR("Sven Schnelle");
 284MODULE_LICENSE("GPL");
 285MODULE_ALIAS("platform:ds2404");
 286
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.