11//! MC146818-compatible CMOS RTC driver for x86_64, x86
22
3- use super :: RtcError ;
3+ use super :: { DateTime , RtcError } ;
4+ use awkernel_lib:: interrupt:: InterruptGuard ;
45use core:: arch:: asm;
56
6- const _IO_RTC : u16 = 0x070 ;
7+ const IO_RTC : u16 = 0x070 ;
78
89// The registers, and the bits within each register.
910
10- const _MC_SEC : u8 = 0x00 ; // Time of year: seconds (0-59)
11- const _MC_MIN : u8 = 0x02 ; // Time of year: minutes (0-59)
12- const _MC_HOUR : u8 = 0x04 ; // Time of year: hour (see above)
13- const _MC_DOW : u8 = 0x06 ; // Time of year: day of week (1-7)
14- const _MC_DOM : u8 = 0x07 ; // Time of year: day of month (1-31)
15- const _MC_MONTH : u8 = 0x08 ; // Time of year: month (1-12)
16- const _MC_YEAR : u8 = 0x09 ; // Time of year: year in century (0-99)
11+ const MC_SEC : u8 = 0x00 ; // Time of year: seconds (0-59)
12+ const MC_MIN : u8 = 0x02 ; // Time of year: minutes (0-59)
13+ const MC_HOUR : u8 = 0x04 ; // Time of year: hour (see above)
14+ const MC_DOW : u8 = 0x06 ; // Time of year: day of week (1-7)
15+ const MC_DOM : u8 = 0x07 ; // Time of year: day of month (1-31)
16+ const MC_MONTH : u8 = 0x08 ; // Time of year: month (1-12)
17+ const MC_YEAR : u8 = 0x09 ; // Time of year: year in century (0-99)
1718
18- const _MC_REGA : u8 = 0x0a ; // Control register A
19+ const MC_REGA : u8 = 0x0a ; // Control register A
1920
2021const _MC_REGA_RSMASK: u8 = 0x0f ; // Interrupt rate select mask
2122const _MC_REGA_DVMASK: u8 = 0x70 ; // Divisor select mask
22- const _MC_REGA_UIP : u8 = 0x80 ; // Update in progress; read only
23+ const MC_REGA_UIP : u8 = 0x80 ; // Update in progress; read only
2324
24- const _MC_REGB : u8 = 0x0b ; // Control register B
25+ const MC_REGB : u8 = 0x0b ; // Control register B
2526
2627const _MC_REGB_DSE: u8 = 0x01 ; // Daylight Saving Enable
27- const _MC_REGB_24HR : u8 = 0x02 ; // 24-hour mode (AM/PM mode when clear)
28+ const MC_REGB_24HR : u8 = 0x02 ; // 24-hour mode (AM/PM mode when clear)
2829const _MC_REGB_DM: u8 = 0x04 ; // Binary mode (BCD mode when clear)
2930const _MC_REGB_SQWE: u8 = 0x08 ; // Square wave enable, ONLY in BQ3285E
3031const _MC_REGB_UIE: u8 = 0x10 ; // Update End interrupt enable
3132const _MC_REGB_AIE: u8 = 0x20 ; // Alarm interrupt enable
3233const _MC_REGB_PIE: u8 = 0x40 ; // Periodic interrupt enable
33- const _MC_REGB_SET : u8 = 0x80 ; // Allow time to be set; stops updates
34+ const MC_REGB_SET : u8 = 0x80 ; // Allow time to be set; stops updates
3435
3536const _MC_REGC: u8 = 0x0c ; // Control register C
3637
@@ -39,17 +40,16 @@ const _MC_REGC_AF: u8 = 0x20; // Alarm interrupt flag
3940const _MC_REGC_PF: u8 = 0x40 ; // Periodic interrupt flag
4041const _MC_REGC_IRQF: u8 = 0x80 ; // Interrupt request pending flag
4142
42- const _MC_REGD : u8 = 0x0d ; // Control register D
43+ const MC_REGD : u8 = 0x0d ; // Control register D
4344
4445// MC_REGD_UNUSED 0x7f UNUSED
45- const _MC_REGD_VRT : u8 = 0x80 ; // Valid RAM and Time bit
46+ const MC_REGD_VRT : u8 = 0x80 ; // Valid RAM and Time bit
4647
4748// Number of TOD registers
4849const _MC_NREGS: usize = 0xe ; // 14 registers; CMOS follows
49- const _MC_NTODREGS : usize = 0xa ; // 10 of those are for TOD and alarm
50+ const MC_NTODREGS : usize = 0xa ; // 10 of those are for TOD and alarm
5051
51- #[ allow( dead_code) ]
52- type McTodRegs = [ u8 ; _MC_NTODREGS] ;
52+ type McTodRegs = [ u8 ; MC_NTODREGS ] ;
5353
5454pub struct Mc146818Rtc ;
5555
@@ -65,11 +65,11 @@ impl Default for Mc146818Rtc {
6565 }
6666}
6767impl Mc146818Rtc {
68- fn _mc146818_read ( reg : u8 ) -> u8 {
68+ fn mc146818_read ( reg : u8 ) -> u8 {
6969 unsafe {
7070 asm ! (
7171 "out dx, al" ,
72- in( "dx" ) _IO_RTC ,
72+ in( "dx" ) IO_RTC ,
7373 in( "al" ) reg,
7474 options( nostack, preserves_flags)
7575 ) ;
@@ -79,19 +79,19 @@ impl Mc146818Rtc {
7979 let value: u8 ;
8080 asm ! (
8181 "in al, dx" ,
82- in( "dx" ) _IO_RTC + 1 ,
82+ in( "dx" ) IO_RTC + 1 ,
8383 out( "al" ) value,
8484 options( nostack, preserves_flags)
8585 ) ;
8686 value
8787 }
8888 }
8989
90- fn _mc146818_write ( reg : u8 , value : u8 ) {
90+ fn mc146818_write ( reg : u8 , value : u8 ) {
9191 unsafe {
9292 asm ! (
9393 "out dx, al" ,
94- in( "dx" ) _IO_RTC ,
94+ in( "dx" ) IO_RTC ,
9595 in( "al" ) reg,
9696 options( nostack, preserves_flags)
9797 ) ;
@@ -100,7 +100,7 @@ impl Mc146818Rtc {
100100
101101 asm ! (
102102 "out dx, al" ,
103- in( "dx" ) _IO_RTC + 1 ,
103+ in( "dx" ) IO_RTC + 1 ,
104104 in( "al" ) value,
105105 options( nostack, preserves_flags)
106106 ) ;
@@ -109,52 +109,103 @@ impl Mc146818Rtc {
109109 }
110110 }
111111
112- fn _bcdtobin ( bcd : u8 ) -> u8 {
112+ fn bcdtobin ( bcd : u8 ) -> u8 {
113113 ( ( ( bcd >> 4 ) & 0x0f ) * 10 ) + ( bcd & 0x0f )
114114 }
115115
116- fn _bintobcd ( bin : u8 ) -> u8 {
116+ fn bintobcd ( bin : u8 ) -> u8 {
117117 ( ( ( bin / 10 ) << 4 ) & 0xf0 ) | ( ( bin % 10 ) & 0x0f )
118118 }
119119
120- fn _rtcget ( ) -> Result < McTodRegs , RtcError > {
121- let mut regs = [ 0u8 ; _MC_NTODREGS ] ;
120+ fn rtcget ( ) -> Result < McTodRegs , RtcError > {
121+ let mut regs = [ 0u8 ; MC_NTODREGS ] ;
122122
123- if ( Self :: _mc146818_read ( _MC_REGD ) & _MC_REGD_VRT ) == 0 {
123+ if ( Self :: mc146818_read ( MC_REGD ) & MC_REGD_VRT ) == 0 {
124124 return Err ( RtcError :: HardwareError ) ;
125125 }
126126
127127 // update in progress; spin loop
128- while ( Self :: _mc146818_read ( _MC_REGA ) & _MC_REGA_UIP ) != 0 {
128+ while ( Self :: mc146818_read ( MC_REGA ) & MC_REGA_UIP ) != 0 {
129129 continue ;
130130 }
131131
132132 loop {
133133 // read all of the tod/alarm regs
134- for ( i, reg) in regs. iter_mut ( ) . enumerate ( ) . take ( _MC_NTODREGS ) {
135- * reg = Self :: _mc146818_read ( i as u8 ) ;
134+ for ( i, reg) in regs. iter_mut ( ) . enumerate ( ) . take ( MC_NTODREGS ) {
135+ * reg = Self :: mc146818_read ( i as u8 ) ;
136136 }
137137
138- if regs[ _MC_SEC as usize ] == Self :: _mc146818_read ( _MC_SEC ) {
138+ if regs[ MC_SEC as usize ] == Self :: mc146818_read ( MC_SEC ) {
139139 break ;
140140 }
141141 }
142142
143143 Ok ( regs)
144144 }
145145
146- fn _rtcput ( regs : & McTodRegs ) -> Result < ( ) , RtcError > {
146+ fn rtcput ( regs : & McTodRegs ) -> Result < ( ) , RtcError > {
147147 // stop updates while setting
148- Self :: _mc146818_write ( _MC_REGB , Self :: _mc146818_read ( _MC_REGB ) | _MC_REGB_SET ) ;
148+ Self :: mc146818_write ( MC_REGB , Self :: mc146818_read ( MC_REGB ) | MC_REGB_SET ) ;
149149
150150 // write all of the tod/alarm regs
151- for ( i, & reg) in regs. iter ( ) . enumerate ( ) . take ( _MC_NTODREGS ) {
152- Self :: _mc146818_write ( i as u8 , reg) ;
151+ for ( i, & reg) in regs. iter ( ) . enumerate ( ) . take ( MC_NTODREGS ) {
152+ Self :: mc146818_write ( i as u8 , reg) ;
153153 }
154154
155155 // reenable updates
156- Self :: _mc146818_write ( _MC_REGB , Self :: _mc146818_read ( _MC_REGB ) & !_MC_REGB_SET ) ;
156+ Self :: mc146818_write ( MC_REGB , Self :: mc146818_read ( MC_REGB ) & !MC_REGB_SET ) ;
157157
158158 Ok ( ( ) )
159159 }
160+
161+ pub fn gettime ( & self ) -> Result < DateTime , RtcError > {
162+ let regs = {
163+ let _guard = InterruptGuard :: new ( ) ;
164+ Self :: rtcget ( ) ?
165+ } ;
166+
167+ let second = Self :: bcdtobin ( regs[ MC_SEC as usize ] ) ;
168+ let minute = Self :: bcdtobin ( regs[ MC_MIN as usize ] ) ;
169+ let hour = Self :: bcdtobin ( regs[ MC_HOUR as usize ] ) ;
170+ let day = Self :: bcdtobin ( regs[ MC_DOM as usize ] ) ;
171+ let month = Self :: bcdtobin ( regs[ MC_MONTH as usize ] ) ;
172+ let year = Self :: bcdtobin ( regs[ MC_YEAR as usize ] ) ;
173+
174+ // TODO: clock_expandyear()
175+ let full_year = 2000 + year as u16 ;
176+
177+ Ok ( DateTime :: new ( full_year, month, day, hour, minute, second) )
178+ }
179+
180+ pub fn settime ( & self , time : & DateTime ) -> Result < ( ) , RtcError > {
181+ let mut regs = [ 0u8 ; MC_NTODREGS ] ;
182+
183+ regs[ MC_SEC as usize ] = Self :: bintobcd ( time. second ) ;
184+ regs[ MC_MIN as usize ] = Self :: bintobcd ( time. minute ) ;
185+ regs[ MC_HOUR as usize ] = Self :: bintobcd ( time. hour ) ;
186+ regs[ MC_DOM as usize ] = Self :: bintobcd ( time. day ) ;
187+ regs[ MC_MONTH as usize ] = Self :: bintobcd ( time. month ) ;
188+ regs[ MC_YEAR as usize ] = Self :: bintobcd ( ( time. year % 100 ) as u8 ) ;
189+
190+ // Day of week (1-7) - not calculated currently, just using placeholder
191+ // NOTE: Implement day of week calculation if needed
192+ regs[ MC_DOW as usize ] = 1 ;
193+
194+ regs[ 7 ] = 0 ;
195+ regs[ 8 ] = 0 ;
196+ regs[ 9 ] = 0 ;
197+
198+ {
199+ let _guard = InterruptGuard :: new ( ) ;
200+ Self :: rtcput ( & regs) ?;
201+ }
202+
203+ // TODO: century byte update
204+
205+ Ok ( ( ) )
206+ }
207+
208+ pub fn init ( & self ) {
209+ Self :: mc146818_write ( MC_REGB , MC_REGB_24HR ) ;
210+ }
160211}
0 commit comments