/// PIT /// Timer stuff // CMOS /* http://www.brokenthorn.com/Resources/OSDevPit.html https://wiki.osdev.org/CMOS vcc/gnd - voltage/ground D0-D7 - data lines (data bus) wr/rd - writing / reading (system control bus) cs - ignore wr/rd or not (address bus) a0-a1 (address bus) // the three 16bit down counters/timers/channels clk 0-2 (in) gate 0-2 (in) out 0-2 (out) //typical out1 -> pic interrupt on every tick (system timer) out2 - was used for genearting dram memory refresh (Do not use) out3 -> pc speaker gate pins : depend on mode of operation we do have modes 0-5. mode0: counts down to zero , triggers interrupt and waits mode1: mode2: rate generator (sys timer) .... */ #define FOOLOS_MODULE_NAME "timer" #include "timer.h" #include "asm/x86.h" #include "lib/logger/log.h" // TODO: use mutex? do we need volatile at all!?? // abstract atomic variable set,get,increment static volatile uint64_t task_system_clock=0; static volatile uint64_t task_system_clock_start=0; //called by interrupt void timer_tick() { task_system_clock++; } // get value uint64_t timer_get_ticks() { return task_system_clock; } /// uint64_t timer_get_ms() { uint64_t t=timer_get_ticks(); uint64_t s=t/25; uint64_t ms=t*1000/25-s*1000; return s*1000+ms; } uint64_t timer_get_uptime_ms() { return timer_get_ms()-task_system_clock_start*1000; } // CMOS RTC // read real time clock register unsigned char get_rtc_reg(int reg) { x86_outb(0x70, reg); //cmos at addr 0x70 return x86_inb(0x71); //cmos data at addr 0x71 } // check if cmos rtc update in progress int get_rtc_update_flag() { x86_outb(0x70, 0x0A); return (x86_inb(0x71) & 0x80); } // get real time clock rom cmos (seconds since jan) uint64_t get_rtc_time() { int CURRENT_YEAR = 2018; // Change this each year! int century_register = 0x00; // Set by ACPI table parsing code if possible unsigned char second; unsigned char minute; unsigned char hour; unsigned char day; unsigned char month; unsigned int year; unsigned char century; unsigned char last_second; unsigned char last_minute; unsigned char last_hour; unsigned char last_day; unsigned char last_month; unsigned char last_year; unsigned char last_century; unsigned char registerB; // Note: This uses the "read registers until you get the same values twice in a row" technique // to avoid getting dodgy/inconsistent values due to RTC updates while (get_rtc_update_flag()); // Make sure an update isn't in progress second = get_rtc_reg(0x00); minute = get_rtc_reg(0x02); hour = get_rtc_reg(0x04); day = get_rtc_reg(0x07); month = get_rtc_reg(0x08); year = get_rtc_reg(0x09); if(century_register != 0) { century = get_rtc_reg(century_register); } do { last_second = second; last_minute = minute; last_hour = hour; last_day = day; last_month = month; last_year = year; last_century = century; while (get_rtc_update_flag()); // Make sure an update isn't in progress second = get_rtc_reg(0x00); minute = get_rtc_reg(0x02); hour = get_rtc_reg(0x04); day = get_rtc_reg(0x07); month = get_rtc_reg(0x08); year = get_rtc_reg(0x09); if(century_register != 0) { century = get_rtc_reg(century_register); } } while( (last_second != second) || (last_minute != minute) || (last_hour != hour) || (last_day != day) || (last_month != month) || (last_year != year) || (last_century != century) ); registerB = get_rtc_reg(0x0B); // Convert BCD to binary values if necessary if (!(registerB & 0x04)) { second = (second & 0x0F) + ((second / 16) * 10); minute = (minute & 0x0F) + ((minute / 16) * 10); hour = ( (hour & 0x0F) + (((hour & 0x70) / 16) * 10) ) | (hour & 0x80); day = (day & 0x0F) + ((day / 16) * 10); month = (month & 0x0F) + ((month / 16) * 10); year = (year & 0x0F) + ((year / 16) * 10); if(century_register != 0) { century = (century & 0x0F) + ((century / 16) * 10); } } // Convert 12 hour clock to 24 hour clock if necessary if (!(registerB & 0x02) && (hour & 0x80)) { hour = ((hour & 0x7F) + 12) % 24; } // Calculate the full (4-digit) year if(century_register != 0) { year += century * 100; } else { year += (CURRENT_YEAR / 100) * 100; if(year < CURRENT_YEAR) year += 100; } // return ((((uint64_t)year-1970)*356;//+month*30+day)*24+hour*60+minute)*60+second; // thank you doug16k @ #osdev // https://github.com/doug65536/dgos/blob/eab7080e69360493381669e7ce0ff27587d3127a/kernel/lib/time.cc int days[] = { 31, (year-1900) % 4 ? 28 : (year-1900) % 100 ? 29 : (year-1900) % 400 ? 28 : 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int yday = 0; for (int m = 1; m < month; ++m) yday += days[m-1]; yday += day - 1; uint64_t epoch_seconds= ((uint64_t)(second)) + minute * 60 + hour * 3600 + (yday) * 86400 + (year-1900 - 70) * 365 * 86400 + ((year-1900 - 69) / 4) * 86400 - ((year-1900 - 1) / 100) * 86400 + ((year-1900 + 299) / 400) * 86400; log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"CMOS Hardware Clock: %u-%u-%u %u:%u:%u",day,month,year,hour,minute,second); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"CMOS Hardware Clock: %u seconds since Epoch",epoch_seconds); return epoch_seconds; } // PIT // TODO: move to asm file void timer_init() { uint64_t epoch_time=get_rtc_time(); task_system_clock_start=epoch_time; task_system_clock=epoch_time*25; // config out timer on channel 0 : mode 2 (sys timer) // http://en.wikipedia.org/wiki/Intel_8253#Control_Word_Register // http://www.brokenthorn.com/Resources/OSDevPit.html // int0 will be triggered ~25 times a second. __asm__("pusha"); __asm__("mov %0, %%dx"::"X" (1193180 / 25)); __asm__("mov $0b00110100, %al"); __asm__("out %al,$0x43"); __asm__("mov %dx,%ax"); __asm__("out %al, $0x40"); __asm__("xchg %ah,%al"); __asm__("out %al, $0x40"); __asm__("popa"); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"Configured PIT Channel 0 : Mode 2 : 1/25 s."); }