linux/drivers/rtc/rtc-m48t86.c
<<
>>
Prefs
   1/*
   2 * ST M48T86 / Dallas DS12887 RTC driver
   3 * Copyright (c) 2006 Tower Technologies
   4 *
   5 * Author: Alessandro Zummo <a.zummo@towertech.it>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 * This drivers only supports the clock running in BCD and 24H mode.
  12 * If it will be ever adapted to binary and 12H mode, care must be taken
  13 * to not introduce bugs.
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/rtc.h>
  18#include <linux/platform_device.h>
  19#include <linux/bcd.h>
  20#include <linux/io.h>
  21
  22#define M48T86_SEC              0x00
  23#define M48T86_SECALRM          0x01
  24#define M48T86_MIN              0x02
  25#define M48T86_MINALRM          0x03
  26#define M48T86_HOUR             0x04
  27#define M48T86_HOURALRM         0x05
  28#define M48T86_DOW              0x06 /* 1 = sunday */
  29#define M48T86_DOM              0x07
  30#define M48T86_MONTH            0x08 /* 1 - 12 */
  31#define M48T86_YEAR             0x09 /* 0 - 99 */
  32#define M48T86_A                0x0a
  33#define M48T86_B                0x0b
  34#define M48T86_B_SET            BIT(7)
  35#define M48T86_B_DM             BIT(2)
  36#define M48T86_B_H24            BIT(1)
  37#define M48T86_C                0x0c
  38#define M48T86_D                0x0d
  39#define M48T86_D_VRT            BIT(7)
  40#define M48T86_NVRAM(x)         (0x0e + (x))
  41#define M48T86_NVRAM_LEN        114
  42
  43struct m48t86_rtc_info {
  44        void __iomem *index_reg;
  45        void __iomem *data_reg;
  46        struct rtc_device *rtc;
  47};
  48
  49static unsigned char m48t86_readb(struct device *dev, unsigned long addr)
  50{
  51        struct m48t86_rtc_info *info = dev_get_drvdata(dev);
  52        unsigned char value;
  53
  54        writeb(addr, info->index_reg);
  55        value = readb(info->data_reg);
  56
  57        return value;
  58}
  59
  60static void m48t86_writeb(struct device *dev,
  61                          unsigned char value, unsigned long addr)
  62{
  63        struct m48t86_rtc_info *info = dev_get_drvdata(dev);
  64
  65        writeb(addr, info->index_reg);
  66        writeb(value, info->data_reg);
  67}
  68
  69static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
  70{
  71        unsigned char reg;
  72
  73        reg = m48t86_readb(dev, M48T86_B);
  74
  75        if (reg & M48T86_B_DM) {
  76                /* data (binary) mode */
  77                tm->tm_sec      = m48t86_readb(dev, M48T86_SEC);
  78                tm->tm_min      = m48t86_readb(dev, M48T86_MIN);
  79                tm->tm_hour     = m48t86_readb(dev, M48T86_HOUR) & 0x3f;
  80                tm->tm_mday     = m48t86_readb(dev, M48T86_DOM);
  81                /* tm_mon is 0-11 */
  82                tm->tm_mon      = m48t86_readb(dev, M48T86_MONTH) - 1;
  83                tm->tm_year     = m48t86_readb(dev, M48T86_YEAR) + 100;
  84                tm->tm_wday     = m48t86_readb(dev, M48T86_DOW);
  85        } else {
  86                /* bcd mode */
  87                tm->tm_sec      = bcd2bin(m48t86_readb(dev, M48T86_SEC));
  88                tm->tm_min      = bcd2bin(m48t86_readb(dev, M48T86_MIN));
  89                tm->tm_hour     = bcd2bin(m48t86_readb(dev, M48T86_HOUR) &
  90                                          0x3f);
  91                tm->tm_mday     = bcd2bin(m48t86_readb(dev, M48T86_DOM));
  92                /* tm_mon is 0-11 */
  93                tm->tm_mon      = bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1;
  94                tm->tm_year     = bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100;
  95                tm->tm_wday     = bcd2bin(m48t86_readb(dev, M48T86_DOW));
  96        }
  97
  98        /* correct the hour if the clock is in 12h mode */
  99        if (!(reg & M48T86_B_H24))
 100                if (m48t86_readb(dev, M48T86_HOUR) & 0x80)
 101                        tm->tm_hour += 12;
 102
 103        return 0;
 104}
 105
 106static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
 107{
 108        unsigned char reg;
 109
 110        reg = m48t86_readb(dev, M48T86_B);
 111
 112        /* update flag and 24h mode */
 113        reg |= M48T86_B_SET | M48T86_B_H24;
 114        m48t86_writeb(dev, reg, M48T86_B);
 115
 116        if (reg & M48T86_B_DM) {
 117                /* data (binary) mode */
 118                m48t86_writeb(dev, tm->tm_sec, M48T86_SEC);
 119                m48t86_writeb(dev, tm->tm_min, M48T86_MIN);
 120                m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR);
 121                m48t86_writeb(dev, tm->tm_mday, M48T86_DOM);
 122                m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH);
 123                m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR);
 124                m48t86_writeb(dev, tm->tm_wday, M48T86_DOW);
 125        } else {
 126                /* bcd mode */
 127                m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC);
 128                m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN);
 129                m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR);
 130                m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM);
 131                m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH);
 132                m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR);
 133                m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW);
 134        }
 135
 136        /* update ended */
 137        reg &= ~M48T86_B_SET;
 138        m48t86_writeb(dev, reg, M48T86_B);
 139
 140        return 0;
 141}
 142
 143static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)
 144{
 145        unsigned char reg;
 146
 147        reg = m48t86_readb(dev, M48T86_B);
 148
 149        seq_printf(seq, "mode\t\t: %s\n",
 150                   (reg & M48T86_B_DM) ? "binary" : "bcd");
 151
 152        reg = m48t86_readb(dev, M48T86_D);
 153
 154        seq_printf(seq, "battery\t\t: %s\n",
 155                   (reg & M48T86_D_VRT) ? "ok" : "exhausted");
 156
 157        return 0;
 158}
 159
 160static const struct rtc_class_ops m48t86_rtc_ops = {
 161        .read_time      = m48t86_rtc_read_time,
 162        .set_time       = m48t86_rtc_set_time,
 163        .proc           = m48t86_rtc_proc,
 164};
 165
 166static int m48t86_nvram_read(void *priv, unsigned int off, void *buf,
 167                             size_t count)
 168{
 169        struct device *dev = priv;
 170        unsigned int i;
 171
 172        for (i = 0; i < count; i++)
 173                ((u8 *)buf)[i] = m48t86_readb(dev, M48T86_NVRAM(off + i));
 174
 175        return 0;
 176}
 177
 178static int m48t86_nvram_write(void *priv, unsigned int off, void *buf,
 179                              size_t count)
 180{
 181        struct device *dev = priv;
 182        unsigned int i;
 183
 184        for (i = 0; i < count; i++)
 185                m48t86_writeb(dev, ((u8 *)buf)[i], M48T86_NVRAM(off + i));
 186
 187        return 0;
 188}
 189
 190/*
 191 * The RTC is an optional feature at purchase time on some Technologic Systems
 192 * boards. Verify that it actually exists by checking if the last two bytes
 193 * of the NVRAM can be changed.
 194 *
 195 * This is based on the method used in their rtc7800.c example.
 196 */
 197static bool m48t86_verify_chip(struct platform_device *pdev)
 198{
 199        unsigned int offset0 = M48T86_NVRAM(M48T86_NVRAM_LEN - 2);
 200        unsigned int offset1 = M48T86_NVRAM(M48T86_NVRAM_LEN - 1);
 201        unsigned char tmp0, tmp1;
 202
 203        tmp0 = m48t86_readb(&pdev->dev, offset0);
 204        tmp1 = m48t86_readb(&pdev->dev, offset1);
 205
 206        m48t86_writeb(&pdev->dev, 0x00, offset0);
 207        m48t86_writeb(&pdev->dev, 0x55, offset1);
 208        if (m48t86_readb(&pdev->dev, offset1) == 0x55) {
 209                m48t86_writeb(&pdev->dev, 0xaa, offset1);
 210                if (m48t86_readb(&pdev->dev, offset1) == 0xaa &&
 211                    m48t86_readb(&pdev->dev, offset0) == 0x00) {
 212                        m48t86_writeb(&pdev->dev, tmp0, offset0);
 213                        m48t86_writeb(&pdev->dev, tmp1, offset1);
 214
 215                        return true;
 216                }
 217        }
 218        return false;
 219}
 220
 221static int m48t86_rtc_probe(struct platform_device *pdev)
 222{
 223        struct m48t86_rtc_info *info;
 224        struct resource *res;
 225        unsigned char reg;
 226        int err;
 227        struct nvmem_config m48t86_nvmem_cfg = {
 228                .name = "m48t86_nvram",
 229                .word_size = 1,
 230                .stride = 1,
 231                .size = M48T86_NVRAM_LEN,
 232                .reg_read = m48t86_nvram_read,
 233                .reg_write = m48t86_nvram_write,
 234                .priv = &pdev->dev,
 235        };
 236
 237        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 238        if (!info)
 239                return -ENOMEM;
 240
 241        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 242        if (!res)
 243                return -ENODEV;
 244        info->index_reg = devm_ioremap_resource(&pdev->dev, res);
 245        if (IS_ERR(info->index_reg))
 246                return PTR_ERR(info->index_reg);
 247
 248        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 249        if (!res)
 250                return -ENODEV;
 251        info->data_reg = devm_ioremap_resource(&pdev->dev, res);
 252        if (IS_ERR(info->data_reg))
 253                return PTR_ERR(info->data_reg);
 254
 255        dev_set_drvdata(&pdev->dev, info);
 256
 257        if (!m48t86_verify_chip(pdev)) {
 258                dev_info(&pdev->dev, "RTC not present\n");
 259                return -ENODEV;
 260        }
 261
 262        info->rtc = devm_rtc_allocate_device(&pdev->dev);
 263        if (IS_ERR(info->rtc))
 264                return PTR_ERR(info->rtc);
 265
 266        info->rtc->ops = &m48t86_rtc_ops;
 267        info->rtc->nvram_old_abi = true;
 268
 269        err = rtc_register_device(info->rtc);
 270        if (err)
 271                return err;
 272
 273        rtc_nvmem_register(info->rtc, &m48t86_nvmem_cfg);
 274
 275        /* read battery status */
 276        reg = m48t86_readb(&pdev->dev, M48T86_D);
 277        dev_info(&pdev->dev, "battery %s\n",
 278                 (reg & M48T86_D_VRT) ? "ok" : "exhausted");
 279
 280        return 0;
 281}
 282
 283static struct platform_driver m48t86_rtc_platform_driver = {
 284        .driver         = {
 285                .name   = "rtc-m48t86",
 286        },
 287        .probe          = m48t86_rtc_probe,
 288};
 289
 290module_platform_driver(m48t86_rtc_platform_driver);
 291
 292MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
 293MODULE_DESCRIPTION("M48T86 RTC driver");
 294MODULE_LICENSE("GPL");
 295MODULE_ALIAS("platform:rtc-m48t86");
 296