Mon 14 Oct 23:06:38 CEST 2024
This commit is contained in:
		
							parent
							
								
									eb18aae083
								
							
						
					
					
						commit
						c9375c1ea8
					
				
							
								
								
									
										196
									
								
								kernel/rtc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								kernel/rtc.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,196 @@ | ||||||
|  | /*
 | ||||||
|  | Copyright (C) 2015-2019 The University of Notre Dame | ||||||
|  | This software is distributed under the GNU General Public License. | ||||||
|  | See the file LICENSE for details. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Driver for the Motorola MC 146818A Real Time Clock | ||||||
|  | Recommended reading: page 11-15 of the RTC data sheet | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include "kernel/types.h" | ||||||
|  | #include "ioports.h" | ||||||
|  | #include "rtc.h" | ||||||
|  | #include "console.h" | ||||||
|  | #include "string.h" | ||||||
|  | #include "interrupt.h" | ||||||
|  | 
 | ||||||
|  | #define RTC_BASE 0x80 | ||||||
|  | 
 | ||||||
|  | #define RTC_SECONDS        (RTC_BASE+0) | ||||||
|  | #define RTC_SECONDS_ALARM  (RTC_BASE+1) | ||||||
|  | #define RTC_MINUTES        (RTC_BASE+2) | ||||||
|  | #define RTC_MINUTES_ALARM  (RTC_BASE+3) | ||||||
|  | #define RTC_HOURS          (RTC_BASE+4) | ||||||
|  | #define RTC_HOURS_ALARM    (RTC_BASE+5) | ||||||
|  | #define RTC_DAY_OF_WEEK    (RTC_BASE+6) | ||||||
|  | #define RTC_DAY_OF_MONTH   (RTC_BASE+7) | ||||||
|  | #define RTC_MONTH          (RTC_BASE+8) | ||||||
|  | #define RTC_YEAR           (RTC_BASE+9) | ||||||
|  | 
 | ||||||
|  | #define RTC_REGISTER_A	   (RTC_BASE+10) | ||||||
|  | #define RTC_REGISTER_B     (RTC_BASE+11) | ||||||
|  | #define RTC_REGISTER_C     (RTC_BASE+12) | ||||||
|  | #define RTC_REGISTER_D     (RTC_BASE+13) | ||||||
|  | 
 | ||||||
|  | #define RTC_ADDRESS_PORT 0x70 | ||||||
|  | #define RTC_DATA_PORT 0x71 | ||||||
|  | 
 | ||||||
|  | /* Register A bits */ | ||||||
|  | 
 | ||||||
|  | #define RTC_A_UIP (1<<7) | ||||||
|  | #define RTC_A_DV2 (1<<6) | ||||||
|  | #define RTC_A_DV1 (1<<5) | ||||||
|  | #define RTC_A_DV0 (1<<4) | ||||||
|  | #define RTC_A_RS3 (1<<3) | ||||||
|  | #define RTC_A_RS2 (1<<2) | ||||||
|  | #define RTC_A_RS1 (1<<1) | ||||||
|  | #define RTC_A_RS0 (1<<0) | ||||||
|  | 
 | ||||||
|  | /* Register B bits */ | ||||||
|  | 
 | ||||||
|  | #define RTC_B_SET  (1<<7)	/* if set, may write new time */ | ||||||
|  | #define RTC_B_PIE  (1<<6)	/* periodic interrupt enabled */ | ||||||
|  | #define RTC_B_AIE  (1<<5)	/* alarm interrupt enabled */ | ||||||
|  | #define RTC_B_UIE  (1<<4)	/* update interrupt enabled */ | ||||||
|  | #define RTC_B_SQWE (1<<3)	/* square wave enabled */ | ||||||
|  | #define RTC_B_DM   (1<<2)	/* data mode: 1=binary 0=decimal */ | ||||||
|  | #define RTC_B_2412 (1<<1)	/* 1=24 hour mode 0=12 hour mode */ | ||||||
|  | #define RTC_B_DSE  (1<<0)	/* daylight savings enable */ | ||||||
|  | 
 | ||||||
|  | /* Register C bits */ | ||||||
|  | /* Note that reading C is necessary to acknowledge an interrupt */ | ||||||
|  | 
 | ||||||
|  | #define RTC_C_IRQF (1<<7)	/* 1=any interrupt pending */ | ||||||
|  | #define RTC_C_PF   (1<<6)	/* periodic interrupt pending */ | ||||||
|  | #define RTC_C_AF   (1<<5)	/* alarm interrupt pending */ | ||||||
|  | #define RTC_C_UF   (1<<4)	/* update interrupt pending */ | ||||||
|  | 
 | ||||||
|  | #define SECS_PER_MIN  60 | ||||||
|  | #define SECS_PER_HOUR 3600 | ||||||
|  | #define SECS_PER_DAY  SECS_PER_HOUR * 24 | ||||||
|  | #define DAYS_PER_WEEK 7 | ||||||
|  | #define SECS_PER_WEEK SECS_PER_DAY * DAYS_PER_WEEK | ||||||
|  | #define SECS_PER_YEAR SECS_PER_WEEK * 52 | ||||||
|  | 
 | ||||||
|  | #define LEAP_YEAR(Y) ( (Y>0) && !(Y%4) && ( (Y%100) || !(Y%400) ) ) | ||||||
|  | 
 | ||||||
|  | uint32_t boottime; | ||||||
|  | 
 | ||||||
|  | static const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | ||||||
|  | 
 | ||||||
|  | static uint8_t rtc_bcd_to_binary(uint8_t bcd) | ||||||
|  | { | ||||||
|  | 	return (bcd & 0x0f) + (bcd >> 4) * 10; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t rtc_read_port(uint16_t address) | ||||||
|  | { | ||||||
|  | 	outb_slow(address, RTC_ADDRESS_PORT); | ||||||
|  | 	return inb_slow(RTC_DATA_PORT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rtc_write_port(uint8_t value, uint16_t address) | ||||||
|  | { | ||||||
|  | 	outb_slow(address, RTC_ADDRESS_PORT); | ||||||
|  | 	outb_slow(value, RTC_DATA_PORT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct rtc_time cached_time; | ||||||
|  | 
 | ||||||
|  | static void rtc_fetch_time() | ||||||
|  | { | ||||||
|  | 	struct rtc_time t; | ||||||
|  | 
 | ||||||
|  | 	int addpm = 0; | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		t.second = rtc_read_port(RTC_SECONDS); | ||||||
|  | 		t.minute = rtc_read_port(RTC_MINUTES); | ||||||
|  | 		t.hour = rtc_read_port(RTC_HOURS); | ||||||
|  | 		t.day = rtc_read_port(RTC_DAY_OF_MONTH); | ||||||
|  | 		t.month = rtc_read_port(RTC_MONTH); | ||||||
|  | 		t.year = rtc_read_port(RTC_YEAR); | ||||||
|  | 	} while(t.second != rtc_read_port(RTC_SECONDS)); | ||||||
|  | 
 | ||||||
|  | 	if(t.hour & 0x80) { | ||||||
|  | 		addpm = 1; | ||||||
|  | 		t.hour &= 0x7f; | ||||||
|  | 	} else { | ||||||
|  | 		addpm = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t.second = rtc_bcd_to_binary(t.second); | ||||||
|  | 	t.minute = rtc_bcd_to_binary(t.minute); | ||||||
|  | 	t.hour = rtc_bcd_to_binary(t.hour); | ||||||
|  | 	if(addpm) | ||||||
|  | 		t.hour += 12; | ||||||
|  | 	t.day = rtc_bcd_to_binary(t.day); | ||||||
|  | 	t.month = rtc_bcd_to_binary(t.month); | ||||||
|  | 	t.year = rtc_bcd_to_binary(t.year); | ||||||
|  | 
 | ||||||
|  | 	if(t.year >= 70) { | ||||||
|  | 		t.year += 1900; | ||||||
|  | 	} else { | ||||||
|  | 		t.year += 2000; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cached_time = t; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rtc_interrupt_handler(int intr, int code) | ||||||
|  | { | ||||||
|  | 	rtc_fetch_time(); | ||||||
|  | 	rtc_read_port(RTC_REGISTER_C); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rtc_init() | ||||||
|  | { | ||||||
|  | 	uint8_t status; | ||||||
|  | 
 | ||||||
|  | 	status = rtc_read_port(RTC_REGISTER_B); | ||||||
|  | 	status |= RTC_B_UIE; | ||||||
|  | 	rtc_write_port(status, RTC_REGISTER_B); | ||||||
|  | 
 | ||||||
|  | 	interrupt_register(40, rtc_interrupt_handler); | ||||||
|  | 	interrupt_enable(40); | ||||||
|  | 
 | ||||||
|  | 	rtc_fetch_time(); | ||||||
|  | 	struct rtc_time t = {0}; | ||||||
|  | 	rtc_read(&t); | ||||||
|  | 	boottime = rtc_time_to_timestamp(&t); | ||||||
|  | 
 | ||||||
|  | 	printf("rtc: ready\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rtc_read(struct rtc_time *tout) | ||||||
|  | { | ||||||
|  | 	memcpy(tout, &cached_time, sizeof(cached_time)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t rtc_time_to_timestamp(struct rtc_time *t) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	uint32_t seconds; | ||||||
|  | 
 | ||||||
|  | 	seconds = (t->year - 1970) * (SECS_PER_DAY * 365); | ||||||
|  | 	for(i = 1970; i < t->year; i++) { | ||||||
|  | 		if(LEAP_YEAR(i)) { | ||||||
|  | 			seconds += SECS_PER_DAY; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for(i = 1; i < t->month; i++) { | ||||||
|  | 		if((i == 2) && LEAP_YEAR(t->year)) { | ||||||
|  | 			seconds += SECS_PER_DAY * 29; | ||||||
|  | 		} else { | ||||||
|  | 			seconds += SECS_PER_DAY * monthDays[i - 1]; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	seconds += (t->day - 1) * SECS_PER_DAY; | ||||||
|  | 	seconds += t->hour * SECS_PER_HOUR; | ||||||
|  | 	seconds += t->minute * SECS_PER_MIN; | ||||||
|  | 	seconds += t->second; | ||||||
|  | 	return seconds; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user