linux/drivers/rtc/rtc-rs5c313.c
<<
>>
Prefs
   1/*
   2 * Ricoh RS5C313 RTC device/driver
   3 *  Copyright (C) 2007 Nobuhiro Iwamatsu
   4 *
   5 *  2005-09-19 modifed by kogiidena
   6 *
   7 * Based on the old drivers/char/rs5c313_rtc.c  by:
   8 *  Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
   9 *  Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
  10 *
  11 * Based on code written by Paul Gortmaker.
  12 *  Copyright (C) 1996 Paul Gortmaker
  13 *
  14 * This file is subject to the terms and conditions of the GNU General Public
  15 * License.  See the file "COPYING" in the main directory of this archive
  16 * for more details.
  17 *
  18 * Based on other minimal char device drivers, like Alan's
  19 * watchdog, Ted's random, etc. etc.
  20 *
  21 *      1.07    Paul Gortmaker.
  22 *      1.08    Miquel van Smoorenburg: disallow certain things on the
  23 *              DEC Alpha as the CMOS clock is also used for other things.
  24 *      1.09    Nikita Schmidt: epoch support and some Alpha cleanup.
  25 *      1.09a   Pete Zaitcev: Sun SPARC
  26 *      1.09b   Jeff Garzik: Modularize, init cleanup
  27 *      1.09c   Jeff Garzik: SMP cleanup
  28 *      1.10    Paul Barton-Davis: add support for async I/O
  29 *      1.10a   Andrea Arcangeli: Alpha updates
  30 *      1.10b   Andrew Morton: SMP lock fix
  31 *      1.10c   Cesar Barros: SMP locking fixes and cleanup
  32 *      1.10d   Paul Gortmaker: delete paranoia check in rtc_exit
  33 *      1.10e   Maciej W. Rozycki: Handle DECstation's year weirdness.
  34 *      1.11    Takashi Iwai: Kernel access functions
  35 *                            rtc_register/rtc_unregister/rtc_control
  36 *      1.11a   Daniele Bellucci: Audit create_proc_read_entry in rtc_init
  37 *      1.12    Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer
  38 *              CONFIG_HPET_EMULATE_RTC
  39 *      1.13    Nobuhiro Iwamatsu: Updata driver.
  40 */
  41
  42#include <linux/module.h>
  43#include <linux/err.h>
  44#include <linux/rtc.h>
  45#include <linux/platform_device.h>
  46#include <linux/bcd.h>
  47#include <linux/delay.h>
  48#include <asm/io.h>
  49
  50#define DRV_NAME        "rs5c313"
  51#define DRV_VERSION     "1.13"
  52
  53#ifdef CONFIG_SH_LANDISK
  54/*****************************************************/
  55/* LANDISK dependence part of RS5C313                */
  56/*****************************************************/
  57
  58#define SCSMR1          0xFFE00000
  59#define SCSCR1          0xFFE00008
  60#define SCSMR1_CA       0x80
  61#define SCSCR1_CKE      0x03
  62#define SCSPTR1         0xFFE0001C
  63#define SCSPTR1_EIO     0x80
  64#define SCSPTR1_SPB1IO  0x08
  65#define SCSPTR1_SPB1DT  0x04
  66#define SCSPTR1_SPB0IO  0x02
  67#define SCSPTR1_SPB0DT  0x01
  68
  69#define SDA_OEN         SCSPTR1_SPB1IO
  70#define SDA             SCSPTR1_SPB1DT
  71#define SCL_OEN         SCSPTR1_SPB0IO
  72#define SCL             SCSPTR1_SPB0DT
  73
  74/* RICOH RS5C313 CE port */
  75#define RS5C313_CE      0xB0000003
  76
  77/* RICOH RS5C313 CE port bit */
  78#define RS5C313_CE_RTCCE        0x02
  79
  80/* SCSPTR1 data */
  81unsigned char scsptr1_data;
  82
  83#define RS5C313_CEENABLE    ctrl_outb(RS5C313_CE_RTCCE, RS5C313_CE);
  84#define RS5C313_CEDISABLE   ctrl_outb(0x00, RS5C313_CE)
  85#define RS5C313_MISCOP      ctrl_outb(0x02, 0xB0000008)
  86
  87static void rs5c313_init_port(void)
  88{
  89        /* Set SCK as I/O port and Initialize SCSPTR1 data & I/O port. */
  90        ctrl_outb(ctrl_inb(SCSMR1) & ~SCSMR1_CA, SCSMR1);
  91        ctrl_outb(ctrl_inb(SCSCR1) & ~SCSCR1_CKE, SCSCR1);
  92
  93        /* And Initialize SCL for RS5C313 clock */
  94        scsptr1_data = ctrl_inb(SCSPTR1) | SCL; /* SCL:H */
  95        ctrl_outb(scsptr1_data, SCSPTR1);
  96        scsptr1_data = ctrl_inb(SCSPTR1) | SCL_OEN;     /* SCL output enable */
  97        ctrl_outb(scsptr1_data, SCSPTR1);
  98        RS5C313_CEDISABLE;      /* CE:L */
  99}
 100
 101static void rs5c313_write_data(unsigned char data)
 102{
 103        int i;
 104
 105        for (i = 0; i < 8; i++) {
 106                /* SDA:Write Data */
 107                scsptr1_data = (scsptr1_data & ~SDA) |
 108                                ((((0x80 >> i) & data) >> (7 - i)) << 2);
 109                ctrl_outb(scsptr1_data, SCSPTR1);
 110                if (i == 0) {
 111                        scsptr1_data |= SDA_OEN;        /* SDA:output enable */
 112                        ctrl_outb(scsptr1_data, SCSPTR1);
 113                }
 114                ndelay(700);
 115                scsptr1_data &= ~SCL;   /* SCL:L */
 116                ctrl_outb(scsptr1_data, SCSPTR1);
 117                ndelay(700);
 118                scsptr1_data |= SCL;    /* SCL:H */
 119                ctrl_outb(scsptr1_data, SCSPTR1);
 120        }
 121
 122        scsptr1_data &= ~SDA_OEN;       /* SDA:output disable */
 123        ctrl_outb(scsptr1_data, SCSPTR1);
 124}
 125
 126static unsigned char rs5c313_read_data(void)
 127{
 128        int i;
 129        unsigned char data = 0;
 130
 131        for (i = 0; i < 8; i++) {
 132                ndelay(700);
 133                /* SDA:Read Data */
 134                data |= ((ctrl_inb(SCSPTR1) & SDA) >> 2) << (7 - i);
 135                scsptr1_data &= ~SCL;   /* SCL:L */
 136                ctrl_outb(scsptr1_data, SCSPTR1);
 137                ndelay(700);
 138                scsptr1_data |= SCL;    /* SCL:H */
 139                ctrl_outb(scsptr1_data, SCSPTR1);
 140        }
 141        return data & 0x0F;
 142}
 143
 144#endif /* CONFIG_SH_LANDISK */
 145
 146/*****************************************************/
 147/* machine independence part of RS5C313              */
 148/*****************************************************/
 149
 150/* RICOH RS5C313 address */
 151#define RS5C313_ADDR_SEC        0x00
 152#define RS5C313_ADDR_SEC10      0x01
 153#define RS5C313_ADDR_MIN        0x02
 154#define RS5C313_ADDR_MIN10      0x03
 155#define RS5C313_ADDR_HOUR       0x04
 156#define RS5C313_ADDR_HOUR10     0x05
 157#define RS5C313_ADDR_WEEK       0x06
 158#define RS5C313_ADDR_INTINTVREG 0x07
 159#define RS5C313_ADDR_DAY        0x08
 160#define RS5C313_ADDR_DAY10      0x09
 161#define RS5C313_ADDR_MON        0x0A
 162#define RS5C313_ADDR_MON10      0x0B
 163#define RS5C313_ADDR_YEAR       0x0C
 164#define RS5C313_ADDR_YEAR10     0x0D
 165#define RS5C313_ADDR_CNTREG     0x0E
 166#define RS5C313_ADDR_TESTREG    0x0F
 167
 168/* RICOH RS5C313 control register */
 169#define RS5C313_CNTREG_ADJ_BSY  0x01
 170#define RS5C313_CNTREG_WTEN_XSTP        0x02
 171#define RS5C313_CNTREG_12_24    0x04
 172#define RS5C313_CNTREG_CTFG     0x08
 173
 174/* RICOH RS5C313 test register */
 175#define RS5C313_TESTREG_TEST    0x01
 176
 177/* RICOH RS5C313 control bit */
 178#define RS5C313_CNTBIT_READ     0x40
 179#define RS5C313_CNTBIT_AD       0x20
 180#define RS5C313_CNTBIT_DT       0x10
 181
 182static unsigned char rs5c313_read_reg(unsigned char addr)
 183{
 184
 185        rs5c313_write_data(addr | RS5C313_CNTBIT_READ | RS5C313_CNTBIT_AD);
 186        return rs5c313_read_data();
 187}
 188
 189static void rs5c313_write_reg(unsigned char addr, unsigned char data)
 190{
 191        data &= 0x0f;
 192        rs5c313_write_data(addr | RS5C313_CNTBIT_AD);
 193        rs5c313_write_data(data | RS5C313_CNTBIT_DT);
 194        return;
 195}
 196
 197static inline unsigned char rs5c313_read_cntreg(void)
 198{
 199        return rs5c313_read_reg(RS5C313_ADDR_CNTREG);
 200}
 201
 202static inline void rs5c313_write_cntreg(unsigned char data)
 203{
 204        rs5c313_write_reg(RS5C313_ADDR_CNTREG, data);
 205}
 206
 207static inline void rs5c313_write_intintvreg(unsigned char data)
 208{
 209        rs5c313_write_reg(RS5C313_ADDR_INTINTVREG, data);
 210}
 211
 212static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm)
 213{
 214        int data;
 215        int cnt;
 216
 217        cnt = 0;
 218        while (1) {
 219                RS5C313_CEENABLE;       /* CE:H */
 220
 221                /* Initialize control reg. 24 hour */
 222                rs5c313_write_cntreg(0x04);
 223
 224                if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
 225                        break;
 226
 227                RS5C313_CEDISABLE;
 228                ndelay(700);    /* CE:L */
 229
 230                if (cnt++ > 100) {
 231                        dev_err(dev, "%s: timeout error\n", __func__);
 232                        return -EIO;
 233                }
 234        }
 235
 236        data = rs5c313_read_reg(RS5C313_ADDR_SEC);
 237        data |= (rs5c313_read_reg(RS5C313_ADDR_SEC10) << 4);
 238        tm->tm_sec = bcd2bin(data);
 239
 240        data = rs5c313_read_reg(RS5C313_ADDR_MIN);
 241        data |= (rs5c313_read_reg(RS5C313_ADDR_MIN10) << 4);
 242        tm->tm_min = bcd2bin(data);
 243
 244        data = rs5c313_read_reg(RS5C313_ADDR_HOUR);
 245        data |= (rs5c313_read_reg(RS5C313_ADDR_HOUR10) << 4);
 246        tm->tm_hour = bcd2bin(data);
 247
 248        data = rs5c313_read_reg(RS5C313_ADDR_DAY);
 249        data |= (rs5c313_read_reg(RS5C313_ADDR_DAY10) << 4);
 250        tm->tm_mday = bcd2bin(data);
 251
 252        data = rs5c313_read_reg(RS5C313_ADDR_MON);
 253        data |= (rs5c313_read_reg(RS5C313_ADDR_MON10) << 4);
 254        tm->tm_mon = bcd2bin(data) - 1;
 255
 256        data = rs5c313_read_reg(RS5C313_ADDR_YEAR);
 257        data |= (rs5c313_read_reg(RS5C313_ADDR_YEAR10) << 4);
 258        tm->tm_year = bcd2bin(data);
 259
 260        if (tm->tm_year < 70)
 261                tm->tm_year += 100;
 262
 263        data = rs5c313_read_reg(RS5C313_ADDR_WEEK);
 264        tm->tm_wday = bcd2bin(data);
 265
 266        RS5C313_CEDISABLE;
 267        ndelay(700);            /* CE:L */
 268
 269        return 0;
 270}
 271
 272static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm)
 273{
 274        int data;
 275        int cnt;
 276
 277        cnt = 0;
 278        /* busy check. */
 279        while (1) {
 280                RS5C313_CEENABLE;       /* CE:H */
 281
 282                /* Initiatlize control reg. 24 hour */
 283                rs5c313_write_cntreg(0x04);
 284
 285                if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
 286                        break;
 287                RS5C313_MISCOP;
 288                RS5C313_CEDISABLE;
 289                ndelay(700);    /* CE:L */
 290
 291                if (cnt++ > 100) {
 292                        dev_err(dev, "%s: timeout error\n", __func__);
 293                        return -EIO;
 294                }
 295        }
 296
 297        data = bin2bcd(tm->tm_sec);
 298        rs5c313_write_reg(RS5C313_ADDR_SEC, data);
 299        rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4));
 300
 301        data = bin2bcd(tm->tm_min);
 302        rs5c313_write_reg(RS5C313_ADDR_MIN, data );
 303        rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4));
 304
 305        data = bin2bcd(tm->tm_hour);
 306        rs5c313_write_reg(RS5C313_ADDR_HOUR, data);
 307        rs5c313_write_reg(RS5C313_ADDR_HOUR10, (data >> 4));
 308
 309        data = bin2bcd(tm->tm_mday);
 310        rs5c313_write_reg(RS5C313_ADDR_DAY, data);
 311        rs5c313_write_reg(RS5C313_ADDR_DAY10, (data>> 4));
 312
 313        data = bin2bcd(tm->tm_mon + 1);
 314        rs5c313_write_reg(RS5C313_ADDR_MON, data);
 315        rs5c313_write_reg(RS5C313_ADDR_MON10, (data >> 4));
 316
 317        data = bin2bcd(tm->tm_year % 100);
 318        rs5c313_write_reg(RS5C313_ADDR_YEAR, data);
 319        rs5c313_write_reg(RS5C313_ADDR_YEAR10, (data >> 4));
 320
 321        data = bin2bcd(tm->tm_wday);
 322        rs5c313_write_reg(RS5C313_ADDR_WEEK, data);
 323
 324        RS5C313_CEDISABLE;      /* CE:H */
 325        ndelay(700);
 326
 327        return 0;
 328}
 329
 330static void rs5c313_check_xstp_bit(void)
 331{
 332        struct rtc_time tm;
 333        int cnt;
 334
 335        RS5C313_CEENABLE;       /* CE:H */
 336        if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) {
 337                /* INT interval reg. OFF */
 338                rs5c313_write_intintvreg(0x00);
 339                /* Initialize control reg. 24 hour & adjust */
 340                rs5c313_write_cntreg(0x07);
 341
 342                /* busy check. */
 343                for (cnt = 0; cnt < 100; cnt++) {
 344                        if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
 345                                break;
 346                        RS5C313_MISCOP;
 347                }
 348
 349                memset(&tm, 0, sizeof(struct rtc_time));
 350                tm.tm_mday      = 1;
 351                tm.tm_mon       = 1 - 1;
 352                tm.tm_year      = 2000 - 1900;
 353
 354                rs5c313_rtc_set_time(NULL, &tm);
 355                printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to "
 356                                "1 Jan 2000\n");
 357        }
 358        RS5C313_CEDISABLE;
 359        ndelay(700);            /* CE:L */
 360}
 361
 362static const struct rtc_class_ops rs5c313_rtc_ops = {
 363        .read_time = rs5c313_rtc_read_time,
 364        .set_time = rs5c313_rtc_set_time,
 365};
 366
 367static int rs5c313_rtc_probe(struct platform_device *pdev)
 368{
 369        struct rtc_device *rtc = rtc_device_register("rs5c313", &pdev->dev,
 370                                &rs5c313_rtc_ops, THIS_MODULE);
 371
 372        if (IS_ERR(rtc))
 373                return PTR_ERR(rtc);
 374
 375        platform_set_drvdata(pdev, rtc);
 376
 377        return 0;
 378}
 379
 380static int __devexit rs5c313_rtc_remove(struct platform_device *pdev)
 381{
 382        struct rtc_device *rtc = platform_get_drvdata( pdev );
 383
 384        rtc_device_unregister(rtc);
 385
 386        return 0;
 387}
 388
 389static struct platform_driver rs5c313_rtc_platform_driver = {
 390        .driver         = {
 391                .name   = DRV_NAME,
 392                .owner  = THIS_MODULE,
 393        },
 394        .probe  = rs5c313_rtc_probe,
 395        .remove = __devexit_p( rs5c313_rtc_remove ),
 396};
 397
 398static int __init rs5c313_rtc_init(void)
 399{
 400        int err;
 401
 402        err = platform_driver_register(&rs5c313_rtc_platform_driver);
 403        if (err)
 404                return err;
 405
 406        rs5c313_init_port();
 407        rs5c313_check_xstp_bit();
 408
 409        return 0;
 410}
 411
 412static void __exit rs5c313_rtc_exit(void)
 413{
 414        platform_driver_unregister( &rs5c313_rtc_platform_driver );
 415}
 416
 417module_init(rs5c313_rtc_init);
 418module_exit(rs5c313_rtc_exit);
 419
 420MODULE_VERSION(DRV_VERSION);
 421MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu <iwamatsu@nigauri.org>");
 422MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver");
 423MODULE_LICENSE("GPL");
 424MODULE_ALIAS("platform:" DRV_NAME);
 425