linux/drivers/rtc/rtc-ds1216.c
<<
>>
Prefs
   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#include <linux/slab.h>
  13
  14#define DRV_VERSION "0.2"
  15
  16struct ds1216_regs {
  17        u8 tsec;
  18        u8 sec;
  19        u8 min;
  20        u8 hour;
  21        u8 wday;
  22        u8 mday;
  23        u8 month;
  24        u8 year;
  25};
  26
  27#define DS1216_HOUR_1224        (1 << 7)
  28#define DS1216_HOUR_AMPM        (1 << 5)
  29
  30struct ds1216_priv {
  31        struct rtc_device *rtc;
  32        void __iomem *ioaddr;
  33        size_t size;
  34        unsigned long baseaddr;
  35};
  36
  37static const u8 magic[] = {
  38        0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c
  39};
  40
  41/*
  42 * Read the 64 bit we'd like to have - It a series
  43 * of 64 bits showing up in the LSB of the base register.
  44 *
  45 */
  46static void ds1216_read(u8 __iomem *ioaddr, u8 *buf)
  47{
  48        unsigned char c;
  49        int i, j;
  50
  51        for (i = 0; i < 8; i++) {
  52                c = 0;
  53                for (j = 0; j < 8; j++)
  54                        c |= (readb(ioaddr) & 0x1) << j;
  55                buf[i] = c;
  56        }
  57}
  58
  59static void ds1216_write(u8 __iomem *ioaddr, const u8 *buf)
  60{
  61        unsigned char c;
  62        int i, j;
  63
  64        for (i = 0; i < 8; i++) {
  65                c = buf[i];
  66                for (j = 0; j < 8; j++) {
  67                        writeb(c, ioaddr);
  68                        c = c >> 1;
  69                }
  70        }
  71}
  72
  73static void ds1216_switch_ds_to_clock(u8 __iomem *ioaddr)
  74{
  75        /* Reset magic pointer */
  76        readb(ioaddr);
  77        /* Write 64 bit magic to DS1216 */
  78        ds1216_write(ioaddr, magic);
  79}
  80
  81static int ds1216_rtc_read_time(struct device *dev, struct rtc_time *tm)
  82{
  83        struct platform_device *pdev = to_platform_device(dev);
  84        struct ds1216_priv *priv = platform_get_drvdata(pdev);
  85        struct ds1216_regs regs;
  86
  87        ds1216_switch_ds_to_clock(priv->ioaddr);
  88        ds1216_read(priv->ioaddr, (u8 *)&regs);
  89
  90        tm->tm_sec = bcd2bin(regs.sec);
  91        tm->tm_min = bcd2bin(regs.min);
  92        if (regs.hour & DS1216_HOUR_1224) {
  93                /* AM/PM mode */
  94                tm->tm_hour = bcd2bin(regs.hour & 0x1f);
  95                if (regs.hour & DS1216_HOUR_AMPM)
  96                        tm->tm_hour += 12;
  97        } else
  98                tm->tm_hour = bcd2bin(regs.hour & 0x3f);
  99        tm->tm_wday = (regs.wday & 7) - 1;
 100        tm->tm_mday = bcd2bin(regs.mday & 0x3f);
 101        tm->tm_mon = bcd2bin(regs.month & 0x1f);
 102        tm->tm_year = bcd2bin(regs.year);
 103        if (tm->tm_year < 70)
 104                tm->tm_year += 100;
 105
 106        return rtc_valid_tm(tm);
 107}
 108
 109static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm)
 110{
 111        struct platform_device *pdev = to_platform_device(dev);
 112        struct ds1216_priv *priv = platform_get_drvdata(pdev);
 113        struct ds1216_regs regs;
 114
 115        ds1216_switch_ds_to_clock(priv->ioaddr);
 116        ds1216_read(priv->ioaddr, (u8 *)&regs);
 117
 118        regs.tsec = 0; /* clear 0.1 and 0.01 seconds */
 119        regs.sec = bin2bcd(tm->tm_sec);
 120        regs.min = bin2bcd(tm->tm_min);
 121        regs.hour &= DS1216_HOUR_1224;
 122        if (regs.hour && tm->tm_hour > 12) {
 123                regs.hour |= DS1216_HOUR_AMPM;
 124                tm->tm_hour -= 12;
 125        }
 126        regs.hour |= bin2bcd(tm->tm_hour);
 127        regs.wday &= ~7;
 128        regs.wday |= tm->tm_wday;
 129        regs.mday = bin2bcd(tm->tm_mday);
 130        regs.month = bin2bcd(tm->tm_mon);
 131        regs.year = bin2bcd(tm->tm_year % 100);
 132
 133        ds1216_switch_ds_to_clock(priv->ioaddr);
 134        ds1216_write(priv->ioaddr, (u8 *)&regs);
 135        return 0;
 136}
 137
 138static const struct rtc_class_ops ds1216_rtc_ops = {
 139        .read_time      = ds1216_rtc_read_time,
 140        .set_time       = ds1216_rtc_set_time,
 141};
 142
 143static int __init ds1216_rtc_probe(struct platform_device *pdev)
 144{
 145        struct resource *res;
 146        struct ds1216_priv *priv;
 147        int ret = 0;
 148        u8 dummy[8];
 149
 150        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 151        if (!res)
 152                return -ENODEV;
 153        priv = kzalloc(sizeof *priv, GFP_KERNEL);
 154        if (!priv)
 155                return -ENOMEM;
 156
 157        platform_set_drvdata(pdev, priv);
 158
 159        priv->size = resource_size(res);
 160        if (!request_mem_region(res->start, priv->size, pdev->name)) {
 161                ret = -EBUSY;
 162                goto out;
 163        }
 164        priv->baseaddr = res->start;
 165        priv->ioaddr = ioremap(priv->baseaddr, priv->size);
 166        if (!priv->ioaddr) {
 167                ret = -ENOMEM;
 168                goto out;
 169        }
 170        priv->rtc = rtc_device_register("ds1216", &pdev->dev,
 171                                  &ds1216_rtc_ops, THIS_MODULE);
 172        if (IS_ERR(priv->rtc)) {
 173                ret = PTR_ERR(priv->rtc);
 174                goto out;
 175        }
 176
 177        /* dummy read to get clock into a known state */
 178        ds1216_read(priv->ioaddr, dummy);
 179        return 0;
 180
 181out:
 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
 190static int __exit 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
 201static struct platform_driver ds1216_rtc_platform_driver = {
 202        .driver         = {
 203                .name   = "rtc-ds1216",
 204                .owner  = THIS_MODULE,
 205        },
 206        .remove         = __exit_p(ds1216_rtc_remove),
 207};
 208
 209static int __init ds1216_rtc_init(void)
 210{
 211        return platform_driver_probe(&ds1216_rtc_platform_driver, ds1216_rtc_probe);
 212}
 213
 214static void __exit ds1216_rtc_exit(void)
 215{
 216        platform_driver_unregister(&ds1216_rtc_platform_driver);
 217}
 218
 219MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
 220MODULE_DESCRIPTION("DS1216 RTC driver");
 221MODULE_LICENSE("GPL");
 222MODULE_VERSION(DRV_VERSION);
 223MODULE_ALIAS("platform:rtc-ds1216");
 224
 225module_init(ds1216_rtc_init);
 226module_exit(ds1216_rtc_exit);
 227
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.