Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  *  This file interfaces with the real-time clock found in
0005  *  a Motorola MC146818A (common on PC hardware)
0006  *
0007  *  Year 2K Notes:
0008  *
0009  *  This chip only uses a two digit field to store the year.  This
0010  *  code uses the RTEMS Epoch as a pivot year.  This lets us map the
0011  *  two digit year field as follows:
0012  *
0013  *    + two digit years 0-87 are mapped to 2000-2087.
0014  *    + two digit years 88-99 are mapped to 1988-1999.
0015  *
0016  *  This is less than the time span supported by RTEMS.
0017  *
0018  *  COPYRIGHT (c) 1989-1999.
0019  *  On-Line Applications Research Corporation (OAR).
0020  *
0021  * Redistribution and use in source and binary forms, with or without
0022  * modification, are permitted provided that the following conditions
0023  * are met:
0024  * 1. Redistributions of source code must retain the above copyright
0025  *    notice, this list of conditions and the following disclaimer.
0026  * 2. Redistributions in binary form must reproduce the above copyright
0027  *    notice, this list of conditions and the following disclaimer in the
0028  *    documentation and/or other materials provided with the distribution.
0029  *
0030  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0031  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0032  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0033  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0034  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0035  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0036  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0037  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0038  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0039  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0040  * POSSIBILITY OF SUCH DAMAGE.
0041  */
0042 #include <rtems.h>
0043 #include <libchip/rtc.h>
0044 #include <libchip/mc146818a.h>
0045 
0046 #define From_BCD( _x ) ((((_x) >> 4) * 10) + ((_x) & 0x0F))
0047 #define To_BCD( _x )   ((((_x) / 10) << 4) + ((_x) % 10))
0048 
0049 /*
0050  * See if chip is present
0051  */
0052 bool mc146818a_probe(
0053   int minor
0054 )
0055 {
0056   uint32_t     mc146818a;
0057   getRegister_f  getReg;
0058   uint32_t     value;
0059 
0060   /*
0061    * Verify that chip is present and that time is valid
0062    */
0063   mc146818a = RTC_Table[ minor ].ulCtrlPort1;
0064   getReg = RTC_Table[ minor ].getRegister;
0065   value = (*getReg)( mc146818a, MC146818A_STATUSD );
0066   if ((value == 0) || (value == 0xFF))
0067     return false;
0068   return true;
0069 }
0070 
0071 /*
0072  * Initialize chip
0073  */
0074 static void mc146818a_initialize(
0075   int minor
0076 )
0077 {
0078   uintptr_t      mc146818a;
0079   setRegister_f  setReg;
0080 
0081   mc146818a = RTC_Table[ minor ].ulCtrlPort1;
0082   setReg = RTC_Table[ minor ].setRegister;
0083 
0084   (*setReg)(
0085     mc146818a,
0086     MC146818A_STATUSA,
0087     MC146818ASA_DIVIDER|MC146818ASA_1024
0088   );
0089   (*setReg)(
0090     mc146818a,
0091     MC146818A_STATUSB,
0092     MC146818ASB_24HR
0093   );
0094 }
0095 
0096 /*
0097  * Read time from chip
0098  */
0099 static int mc146818a_get_time(
0100   int                minor,
0101   rtems_time_of_day *time
0102 )
0103 {
0104   uintptr_t             mc146818a;
0105   getRegister_f         getReg;
0106   uint32_t              value;
0107   rtems_interrupt_level level;
0108 
0109   mc146818a = RTC_Table[ minor ].ulCtrlPort1;
0110   getReg = RTC_Table[ minor ].getRegister;
0111 
0112   /*
0113    * No time if power failed
0114    */
0115   if (((*getReg)( mc146818a, MC146818A_STATUSD ) & MC146818ASD_PWR) == 0)
0116     return -1;
0117 
0118   /*
0119    * Wait for time update to complete
0120    */
0121   rtems_interrupt_disable( level );
0122   while (((*getReg)( mc146818a, MC146818A_STATUSA ) & MC146818ASA_TUP) != 0) {
0123       rtems_interrupt_flash( level );
0124   }
0125 
0126   /*
0127    * Read the time (we have at least 244 usec to do this)
0128    */
0129   value = (*getReg)( mc146818a, MC146818A_YEAR );
0130   value = From_BCD( value );
0131   if ( value < 88 )
0132     time->year = 2000 + value;
0133   else
0134     time->year = 1900 + value;
0135 
0136   value = (*getReg)( mc146818a, MC146818A_MONTH );
0137   time->month = From_BCD( value );
0138 
0139   value = (*getReg)( mc146818a, MC146818A_DAY );
0140   time->day = From_BCD( value );
0141 
0142   value = (*getReg)( mc146818a, MC146818A_HRS );
0143   time->hour = From_BCD( value );
0144 
0145   value = (*getReg)( mc146818a, MC146818A_MIN );
0146   time->minute = From_BCD( value );
0147 
0148   value = (*getReg)( mc146818a, MC146818A_SEC );
0149   rtems_interrupt_enable( level );
0150   time->second = From_BCD( value );
0151   time->ticks  = 0;
0152 
0153   return 0;
0154 }
0155 
0156 /*
0157  * Set time into chip
0158  */
0159 static int mc146818a_set_time(
0160   int                      minor,
0161   const rtems_time_of_day *time
0162 )
0163 {
0164   uint32_t     mc146818a;
0165   setRegister_f  setReg;
0166 
0167   mc146818a = RTC_Table[ minor ].ulCtrlPort1;
0168   setReg = RTC_Table[ minor ].setRegister;
0169 
0170   /*
0171    * Stop the RTC
0172    */
0173   (*setReg)( mc146818a, MC146818A_STATUSB, MC146818ASB_HALT|MC146818ASB_24HR );
0174 
0175   if ( time->year >= 2088 )
0176     rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );
0177 
0178   (*setReg)( mc146818a, MC146818A_YEAR,  To_BCD(time->year % 100) );
0179   (*setReg)( mc146818a, MC146818A_MONTH, To_BCD(time->month) );
0180   (*setReg)( mc146818a, MC146818A_DAY,   To_BCD(time->day) );
0181   (*setReg)( mc146818a, MC146818A_HRS,   To_BCD(time->hour) );
0182   (*setReg)( mc146818a, MC146818A_MIN,   To_BCD(time->minute) );
0183   (*setReg)( mc146818a, MC146818A_SEC,   To_BCD(time->second) );
0184 
0185   /*
0186    * Restart the RTC
0187    */
0188   (*setReg)( mc146818a, MC146818A_STATUSB, MC146818ASB_24HR );
0189   return 0;
0190 }
0191 
0192 /*
0193  *  Driver function table
0194  */
0195 rtc_fns mc146818a_fns = {
0196   mc146818a_initialize,
0197   mc146818a_get_time,
0198   mc146818a_set_time
0199 };