Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/*
 *  Constant and architecture independent procedures
 *  for NEC uPD4990A serial I/O real-time clock.
 *
 *  Copyright 2001  TAKAI Kousuke <tak@kmc.kyoto-u.ac.jp>
 *                  Kyoto University Microcomputer Club (KMC).
 *
 *  References:
 *      uPD4990A serial I/O real-time clock users' manual (Japanese)
 *      No. S12828JJ4V0UM00 (4th revision), NEC Corporation, 1999.
 */


#ifndef _LINUX_uPD4990A_H
#define _LINUX_uPD4990A_H

#include <asm/byteorder.h>

#include <asm/upd4990a.h>

/* Serial commands (4 bits) */
#define UPD4990A_REGISTER_HOLD                  (0x0)
#define UPD4990A_REGISTER_SHIFT                 (0x1)
#define UPD4990A_TIME_SET_AND_COUNTER_HOLD      (0x2)
#define UPD4990A_TIME_READ                      (0x3)
#define UPD4990A_TP_64HZ                        (0x4)
#define UPD4990A_TP_256HZ                       (0x5)
#define UPD4990A_TP_2048HZ                      (0x6)
#define UPD4990A_TP_4096HZ                      (0x7)
#define UPD4990A_TP_1S                          (0x8)
#define UPD4990A_TP_10S                         (0x9)
#define UPD4990A_TP_30S                         (0xA)
#define UPD4990A_TP_60S                         (0xB)
#define UPD4990A_INTERRUPT_RESET                (0xC)
#define UPD4990A_INTERRUPT_TIMER_START          (0xD)
#define UPD4990A_INTERRUPT_TIMER_STOP           (0xE)
#define UPD4990A_TEST_MODE_SET                  (0xF)

/* Parallel commands (3 bits)
   0-6 are same with serial commands.  */

#define UPD4990A_PAR_SERIAL_MODE                7

#ifndef UPD4990A_DELAY
# include <linux/delay.h>
# define UPD4990A_DELAY(usec)   udelay((usec))
#endif
#ifndef UPD4990A_OUTPUT_DATA
# define UPD4990A_OUTPUT_DATA(bit)                      \
        do {                                            \
                UPD4990A_OUTPUT_DATA_CLK((bit), 0);     \
                UPD4990A_DELAY(1); /* t-DSU */          \
                UPD4990A_OUTPUT_DATA_CLK((bit), 1);     \
                UPD4990A_DELAY(1); /* t-DHLD */ \
        } while (0)

#endif

static __inline__ void upd4990a_serial_command(int command)
{
        UPD4990A_OUTPUT_DATA(command >> 0);
        UPD4990A_OUTPUT_DATA(command >> 1);
        UPD4990A_OUTPUT_DATA(command >> 2);
        UPD4990A_OUTPUT_DATA(command >> 3);
        UPD4990A_DELAY(1);      /* t-HLD */
        UPD4990A_OUTPUT_STROBE(1);
        UPD4990A_DELAY(1);      /* t-STB & t-d1 */
        UPD4990A_OUTPUT_STROBE(0);
        /* 19 microseconds extra delay is needed
           iff previous mode is TIME READ command  */

}

struct upd4990a_raw_data {
        u8      sec;            /* BCD */
        u8      min;            /* BCD */
        u8      hour;           /* BCD */
        u8      mday;           /* BCD */
#if   defined __LITTLE_ENDIAN_BITFIELD
        unsigned wday :4;       /* 0-6 */
        unsigned mon :4;        /* 1-based */
#elif defined __BIG_ENDIAN_BITFIELD
        unsigned mon :4;        /* 1-based */
        unsigned wday :4;       /* 0-6 */
#else
# error Unknown bitfield endian!
#endif
        u8      year;           /* BCD */
};

static __inline__ void upd4990a_get_time(struct upd4990a_raw_data *buf,
                                          int leave_register_hold)
{
        int byte;

        upd4990a_serial_command(UPD4990A_TIME_READ);
        upd4990a_serial_command(UPD4990A_REGISTER_SHIFT);
        UPD4990A_DELAY(19);     /* t-d2 - t-d1 */

        for (byte = 0; byte < 6; byte++) {
                u8 tmp;
                int bit;

                for (tmp = 0, bit = 0; bit < 8; bit++) {
                        tmp = (tmp | (UPD4990A_READ_DATA() << 8)) >> 1;
                        UPD4990A_OUTPUT_CLK(1);
                        UPD4990A_DELAY(1);
                        UPD4990A_OUTPUT_CLK(0);
                        UPD4990A_DELAY(1);
                }
                ((u8 *) buf)[byte] = tmp;
        }

        /* The uPD4990A users' manual says that we should issue `Register
           Hold' command after each data retrieval, or next `Time Read'
           command may not work correctly.  */

        if (!leave_register_hold)
                upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
}

static __inline__ void upd4990a_set_time(const struct upd4990a_raw_data *data,
                                          int time_set_only)
{
        int byte;

        if (!time_set_only)
                upd4990a_serial_command(UPD4990A_REGISTER_SHIFT);

        for (byte = 0; byte < 6; byte++) {
                int bit;
                u8 tmp = ((const u8 *) data)[byte];

                for (bit = 0; bit < 8; bit++, tmp >>= 1)
                        UPD4990A_OUTPUT_DATA(tmp);
        }

        upd4990a_serial_command(UPD4990A_TIME_SET_AND_COUNTER_HOLD);

        /* Release counter hold and start the clock.  */
        if (!time_set_only)
                upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
}

#endif /* _LINUX_uPD4990A_H */