RTC Access (time, source)

We have a requirement to read the RTC chip periodically for tertiary
verification of time.

Two questions:
a) how long does it take to “do the dance” required to read the RTC?
(don’t need an exact number, just an order of magnitude; hoping
for “tens of microseconds to hundreds of microseconds” answer).
b) is there any source available for doing this? Is this standard
or does it vary radically?

This is for an x86 platform…

Thanks in advance!

Cheers,
-RK


[If replying via email, you’ll need to click on the URL that’s emailed to you
afterwards to forward the email to me – spam filters and all that]
Robert Krten, PDP minicomputer collector http://www.parse.com/~pdp8/

Armin posted some code in qnx.newuser

Robert Krten wrote:

We have a requirement to read the RTC chip periodically for tertiary
verification of time.

Two questions:
a) how long does it take to “do the dance” required to read the RTC?
(don’t need an exact number, just an order of magnitude; hoping
for “tens of microseconds to hundreds of microseconds” answer).
b) is there any source available for doing this? Is this standard
or does it vary radically?

This is for an x86 platform…

Thanks in advance!

Cheers,
-RK


cburgess@qnx.com

Colin Burgess <cburgess@qnx.com> wrote:

[-- text/plain, encoding 7bit, charset: ISO-8859-1, 24 lines --]

Armin posted some code in qnx.newuser

Hmmm… not sure that that’s what I need; perhaps I was unclear.

By “RTC” I meant the Time of Day clock that the PC uses to find out
“what time is it” when it initially boots up.

The code attached (thanks anyway!) seems to be for setting up a timer
to periodically interrupt me…

I want to read the Time of Day. Same questions as below :slight_smile:

Cheers,
-RK

Robert Krten wrote:
We have a requirement to read the RTC chip periodically for tertiary
verification of time.

Two questions:
a) how long does it take to “do the dance” required to read the RTC?
(don’t need an exact number, just an order of magnitude; hoping
for “tens of microseconds to hundreds of microseconds” answer).
b) is there any source available for doing this? Is this standard
or does it vary radically?

This is for an x86 platform…

Thanks in advance!

Cheers,
-RK

\

cburgess@qnx.com

[-- message/rfc822, encoding 7bit, 149 lines, name: Attached Message --]


[If replying via email, you’ll need to click on the URL that’s emailed to you
afterwards to forward the email to me – spam filters and all that]
Robert Krten, PDP minicomputer collector http://www.parse.com/~pdp8/

Robert Krten <rk@parse.com> wrote:

We have a requirement to read the RTC chip periodically for tertiary
verification of time.

Two questions:
a) how long does it take to “do the dance” required to read the RTC?
(don’t need an exact number, just an order of magnitude; hoping
for “tens of microseconds to hundreds of microseconds” answer).
b) is there any source available for doing this? Is this standard
or does it vary radically?

This is for an x86 platform…

Thanks in advance!

Found the answer, and ported to QNX Neutrino (sections marked RK BEGAN HACK / RK END HACK).

See http://minix1.bio.umass.edu/pub/inet/readclock.c

Thanks!
-RK

/* readclock - read the real time clock Authors: T. Holm & E. Froese */

//
/
/
/
readclock.c /
/
/
/
Read from the 64 byte CMOS RAM area, then write /
/
the time to standard output in a form usable by /
/
date(1). /
/
/
/
If the machine ID byte is 0xFC or 0xF8, the device /
/
/dev/mem exists and can be opened for reading, /
/
and no errors in the CMOS RAM are reported by the /
/
RTC, then the time is read from the clock RAM /
/
area maintained by the RTC. /
/
/
/
The clock RAM values are decoded and written to /
/
standard output in the form: /
/
/
/
mmddyyhhmmss /
/
/
/
If the machine ID does not match 0xFC or 0xF8, /
/
then -q'' is written to standard output. */ /* */ /* If the machine ID is 0xFC or 0xF8 and /dev/mem */ /* is missing, or cannot be accessed, */ /* then an error message is written to stderr, */ /* and -q’’ is written to stdout. /
/
/
/
If the RTC reports errors in the CMOS RAM, /
/
then an error message is written to stderr, /
/
and -q'' is written to stdout. */ /* */ /* Readclock is used as follows when placed */ /* the /etc/rc’’ script: /
/
/
/
/usr/bin/date /usr/bin/readclock </dev/tty /
/
/
/
It is best if this program is owned by bin and NOT /
/
SUID because of the peculiar method of reading from /
/
RTC. When someone makes a /dev/clock driver, this /
/
warning may be removed. /
/
/
/
/
/* origination 1987-Dec-29 efth /
/
robustness 1990-Oct-06 C. Sylvain /
/
incorp. B. Evans ideas 1991-Jul-06 C. Sylvain /
/
combined w/ setclock 1996-Jul-15 Michael Temari /
/
***********************************************************************/


#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

#define CPU_TYPE_SEGMENT 0xFFFF /* Segment for Machine ID in BIOS /
#define CPU_TYPE_OFFSET 0x000E /
Offset for Machine ID */

#define PC_AT 0xFC /* Machine ID byte for PC/AT,
PC/XT286, and PS/2 Models 50, 60 /
#define PS_386 0xF8 /
Machine ID byte for PS/2 Model 80 */

/* Manufacturers usually use the ID value of the IBM model they emulate.

  • However some manufacturers, notably HP and COMPAQ, have had different
  • ideas in the past.
  • Machine ID byte information source:
  • The Programmer’s PC Sourcebook by Thom Hogan,
  • published by Microsoft Press
    */

#define CLK_ELE 0x70 /* CMOS RAM address register port (write only)

  • Bit 7 = 1 NMI disable
  •  0  NMI enable
    
  • Bits 6-0 = RAM address
    */

#define CLK_IO 0x71 /* CMOS RAM data register port (read/write) */

#define YEAR 9 /* Clock register addresses in CMOS RAM /
#define MONTH 8
#define DAY 7
#define WDAY 6
#define HOUR 4
#define MINUTE 2
#define SECOND 0
#define STATUS 0x0B /
Status register B: RTC configuration /
#define HEALTH 0x0E /
Diagnostic status: (should be set by Power

  • On Self-Test [POST])
  • Bit 7 = RTC lost power
  • 6 = Checksum (for addr 0x10-0x2d) bad
  • 5 = Config. Info. bad at POST
  • 4 = Mem. size error at POST
  • 3 = I/O board failed initialization
  • 2 = CMOS time invalid
  • 1-0 = reserved
    */
    #define DIAG_BADBATT 0x80
    #define DIAG_MEMDIRT 0x40
    #define DIAG_BADTIME 0x04

/* CMOS RAM and RTC information source:

  • System BIOS for PC/XT/AT Computers and Compatibles
  • by Phoenix Technologies Ltd., published by Addison-Wesley
    */

#define BCD_TO_DEC(x) ( (x >> 4) * 10 + (x & 0x0f) )
#define DEC_TO_BCD(x) (( ((x/10) << 4) + (x % 10) ) & 0xff)
#define errmsg(s) fputs( s, stderr )

struct time {
unsigned year;
unsigned month;
unsigned day;
unsigned hour;
unsigned minute;
unsigned second;
};

#define _PROTOTYPE(f,a) f a
#define U16_t unsigned short int
#define U8_t unsigned char
#include <x86/inout.h>
#include <sys/neutrino.h>
#define inb in8
#define outb out8
int peek (int s, int o) { return (PC_AT); }

_PROTOTYPE(int main, (int argc, char *argv[]));
_PROTOTYPE(void get_time, (struct time *t));
_PROTOTYPE(void set_time, (struct tm *t));
_PROTOTYPE(int read_register, (int reg_addr));
_PROTOTYPE(void write_register, (int reg_addr, int reg_value));

int main(argc, argv)
int argc;
char *argv[];
{
struct time time1;
struct time time2;
struct tm *tm;
int i;
int utc = 0, setclock = 0;
time_t now;
int cpu_type, cmos_state;

ThreadCtl (_NTO_TCTL_IO, 0);

if(argv[0][0] == ‘s’)
setclock = 1;

cpu_type = peek(CPU_TYPE_SEGMENT, CPU_TYPE_OFFSET);
if (cpu_type < 0) {
errmsg( “Memory I/O failed.\n” );
if(!setclock)
printf("-q1\n");
exit(1);
}
if (cpu_type != PS_386 && cpu_type != PC_AT) {
/* This is probably an XT, exit without complaining. */
if(!setclock)
printf("-q2\n");
else
errmsg(“Not an AT-class machine, cannot set clock.\n”);
exit(1);
}
cmos_state = read_register(HEALTH);
if (cmos_state & (DIAG_BADBATT | DIAG_MEMDIRT | DIAG_BADTIME)) {
errmsg( "\nCMOS RAM error(s) found… " );
fprintf( stderr, “CMOS state = 0x%02x\n”, cmos_state );

if (cmos_state & DIAG_BADBATT)
errmsg( “RTC lost power. Reset CMOS RAM with SETUP.\n” );
if (cmos_state & DIAG_MEMDIRT)
errmsg( “CMOS RAM checksum is bad. Run SETUP.\n” );
if (cmos_state & DIAG_BADTIME)
errmsg( “Time invalid in CMOS RAM. Reset clock with setclock.\n” );
errmsg( “\n” );

if(!setclock)
printf("-q3\n");
exit(1);
}
if(setclock) {
if(argc == 2)
if(argv[1][0] == ‘-’)
if(argv[1][1] == ‘u’)
utc = 1;
if(argc > 1 && !utc) {
errmsg(“Usage: setclock [-u]\n”);
exit(1);
}
time(&now);
if(utc)
tm = gmtime(&now);
else
tm = localtime(&now);
set_time™;
exit(0);
}
for (i = 0; i < 10; i++) {
get_time(&time1);
get_time(&time2);

if (time1.year == time2.year &&
time1.month == time2.month &&
time1.day == time2.day &&
time1.hour == time2.hour &&
time1.minute == time2.minute &&
time1.second == time2.second) {
printf("%02d%02d%02d%02d%02d%02d\n",
time1.month, time1.day, time1.year,
time1.hour, time1.minute, time1.second);
exit(0);
}
}

errmsg( “Failed to get an accurate time.\n” );
printf("-q4\n");
exit(1);
}



//
/* /
/
get_time( time ) /
/
/
/
Update the structure pointed to by time with the current time /
/
as read from CMOS RAM of the RTC. /
/
If necessary, the time is converted into a binary format before /
/
being stored in the structure. /
/
*/
/
/

void get_time(t)
struct time t;
{
t->year = read_register(YEAR);
t->month = read_register(MONTH);
t->day = read_register(DAY);
t->hour = read_register(HOUR);
t->minute = read_register(MINUTE);
t->second = read_register(SECOND);



if ((read_register(STATUS) & 0x04) == 0) {
/
Convert BCD to binary (default RTC mode) /
t->year = BCD_TO_DEC(t->year);
t->month = BCD_TO_DEC(t->month);
t->day = BCD_TO_DEC(t->day);
t->hour = BCD_TO_DEC(t->hour);
t->minute = BCD_TO_DEC(t->minute);
t->second = BCD_TO_DEC(t->second);
}
}


/
/
/
set_time( time ) /
/
Update the hardware real-time clock of the AT. /
/
Set the clock mode to BCD format if mode is binary. /
/
*/
void set_time(t)
struct tm *t;
{
int status;

t->tm_year = (t->tm_year > 99) ? t->tm_year - 1900 : t->tm_year;
t->tm_mon++;
if (((status = read_register(STATUS)) & 0x04) == 0) {
/* Convert binary to BCD if necessary /
t->tm_year = DEC_TO_BCD(t->tm_year);
t->tm_mon = DEC_TO_BCD(t->tm_mon);
t->tm_wday = DEC_TO_BCD(t->tm_wday);
t->tm_mday = DEC_TO_BCD(t->tm_mday);
t->tm_hour = DEC_TO_BCD(t->tm_hour);
t->tm_min = DEC_TO_BCD(t->tm_min);
t->tm_sec = DEC_TO_BCD(t->tm_sec);
}
write_register(STATUS, status | 0x80); /
update clock /
write_register(YEAR, t->tm_year);
write_register(MONTH, t->tm_mon);
write_register(DAY, t->tm_mday);
write_register(WDAY, t->tm_wday);
write_register(HOUR, t->tm_hour);
write_register(MINUTE, t->tm_min);
write_register(SECOND, t->tm_sec);
write_register(STATUS, status); /
update finished */
}

int read_register(reg_addr)
char reg_addr;
{
outb(CLK_ELE, reg_addr);
return inb(CLK_IO);
}

void write_register(reg_addr, reg_value)
char reg_addr;
int reg_value;
{
outb(CLK_ELE, reg_addr);
outb(CLK_IO, reg_value);
}

[If replying via email, you’ll need to click on the URL that’s emailed to you
afterwards to forward the email to me – spam filters and all that]
Robert Krten, PDP minicomputer collector http://www.parse.com/~pdp8/

Evan Hillas <blarg@blarg.blarg> wrote:

Robert Krten wrote:
Colin Burgess <> cburgess@qnx.com> > wrote:

[-- text/plain, encoding 7bit, charset: ISO-8859-1, 24 lines --]


Armin posted some code in qnx.newuser


Hmmm… not sure that that’s what I need; perhaps I was unclear.

By “RTC” I meant the Time of Day clock that the PC uses to find out
“what time is it” when it initially boots up.

The code attached (thanks anyway!) seems to be for setting up a timer
to periodically interrupt me…

I want to read the Time of Day. Same questions as below > :slight_smile:


Same routine, just that the clock info is in the first ten CMOS_ADDRes
plus address 0x32 is the century.

0 second
1 alarm second
2 minute
3 alarm minute
4 hour
5 alarm hour
6 day of week
7 day of month
8 month
9 year

The core is based on a MC146818 RTC chip. There is a few options for
the data encoding so you should prolly dig up some info on default
method used by BIOSes.

Well, shows you how much I know :slight_smile:

Thanks for the update.

Cheers,
-RK


[If replying via email, you’ll need to click on the URL that’s emailed to you
afterwards to forward the email to me – spam filters and all that]
Robert Krten, PDP minicomputer collector http://www.parse.com/~pdp8/

Robert Krten wrote:

Colin Burgess <> cburgess@qnx.com> > wrote:

[-- text/plain, encoding 7bit, charset: ISO-8859-1, 24 lines --]


Armin posted some code in qnx.newuser


Hmmm… not sure that that’s what I need; perhaps I was unclear.

By “RTC” I meant the Time of Day clock that the PC uses to find out
“what time is it” when it initially boots up.

The code attached (thanks anyway!) seems to be for setting up a timer
to periodically interrupt me…

I want to read the Time of Day. Same questions as below > :slight_smile:

Same routine, just that the clock info is in the first ten CMOS_ADDRes
plus address 0x32 is the century.

0 second
1 alarm second
2 minute
3 alarm minute
4 hour
5 alarm hour
6 day of week
7 day of month
8 month
9 year

The core is based on a MC146818 RTC chip. There is a few options for
the data encoding so you should prolly dig up some info on default
method used by BIOSes.

Opps, a bit slow responding … :wink:

“Robert Krten” <rk@parse.com> wrote in message
news:cngabg$eos$1@inn.qnx.com

Robert Krten <> rk@parse.com> > wrote:
We have a requirement to read the RTC chip periodically for tertiary
verification of time.

Two questions:
a) how long does it take to “do the dance” required to read the
RTC?
(don’t need an exact number, just an order of magnitude;
hoping
for “tens of microseconds to hundreds of microseconds”
answer).
b) is there any source available for doing this? Is this
standard
or does it vary radically?

This is for an x86 platform…

Thanks in advance!

Found the answer, and ported to QNX Neutrino (sections marked RK BEGAN
HACK / RK END HACK).

See > http://minix1.bio.umass.edu/pub/inet/readclock.c

Also the source to QNX4 rtc is available on their ftp site.