diff options
| author | manuel <manuel@mausz.at> | 2012-03-27 11:51:08 +0200 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2012-03-27 11:51:08 +0200 |
| commit | 4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch) | |
| tree | 868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /devices/rtc.c | |
| parent | eae0bd57f0a26314a94785061888d193d186944a (diff) | |
| download | progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2 progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip | |
reorganize file structure to match the upstream requirements
Diffstat (limited to 'devices/rtc.c')
| -rw-r--r-- | devices/rtc.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/devices/rtc.c b/devices/rtc.c new file mode 100644 index 0000000..d99eb46 --- /dev/null +++ b/devices/rtc.c | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #include "devices/rtc.h" | ||
| 2 | #include <stdio.h> | ||
| 3 | #include "threads/io.h" | ||
| 4 | |||
| 5 | /* This code is an interface to the MC146818A-compatible real | ||
| 6 | time clock found on PC motherboards. See [MC146818A] for | ||
| 7 | hardware details. */ | ||
| 8 | |||
| 9 | /* I/O register addresses. */ | ||
| 10 | #define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */ | ||
| 11 | #define CMOS_REG_IO 0x71 /* Contains the selected data byte. */ | ||
| 12 | |||
| 13 | /* Indexes of CMOS registers with real-time clock functions. | ||
| 14 | Note that all of these registers are in BCD format, | ||
| 15 | so that 0x59 means 59, not 89. */ | ||
| 16 | #define RTC_REG_SEC 0 /* Second: 0x00...0x59. */ | ||
| 17 | #define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */ | ||
| 18 | #define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */ | ||
| 19 | #define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */ | ||
| 20 | #define RTC_REG_MON 8 /* Month: 0x01...0x12. */ | ||
| 21 | #define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */ | ||
| 22 | |||
| 23 | /* Indexes of CMOS control registers. */ | ||
| 24 | #define RTC_REG_A 0x0a /* Register A: update-in-progress. */ | ||
| 25 | #define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */ | ||
| 26 | #define RTC_REG_C 0x0c /* Register C: pending interrupts. */ | ||
| 27 | #define RTC_REG_D 0x0d /* Register D: valid time? */ | ||
| 28 | |||
| 29 | /* Register A. */ | ||
| 30 | #define RTCSA_UIP 0x80 /* Set while time update in progress. */ | ||
| 31 | |||
| 32 | /* Register B. */ | ||
| 33 | #define RTCSB_SET 0x80 /* Disables update to let time be set. */ | ||
| 34 | #define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */ | ||
| 35 | #define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */ | ||
| 36 | |||
| 37 | static int bcd_to_bin (uint8_t); | ||
| 38 | static uint8_t cmos_read (uint8_t index); | ||
| 39 | |||
| 40 | /* Returns number of seconds since Unix epoch of January 1, | ||
| 41 | 1970. */ | ||
| 42 | time_t | ||
| 43 | rtc_get_time (void) | ||
| 44 | { | ||
| 45 | static const int days_per_month[12] = | ||
| 46 | { | ||
| 47 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
| 48 | }; | ||
| 49 | int sec, min, hour, mday, mon, year; | ||
| 50 | time_t time; | ||
| 51 | int i; | ||
| 52 | |||
| 53 | /* Get time components. | ||
| 54 | |||
| 55 | We repeatedly read the time until it is stable from one read | ||
| 56 | to another, in case we start our initial read in the middle | ||
| 57 | of an update. This strategy is not recommended by the | ||
| 58 | MC146818A datasheet, but it is simpler than any of their | ||
| 59 | suggestions and, furthermore, it is also used by Linux. | ||
| 60 | |||
| 61 | The MC146818A can be configured for BCD or binary format, | ||
| 62 | but for historical reasons everyone always uses BCD format | ||
| 63 | except on obscure non-PC platforms, so we don't bother | ||
| 64 | trying to detect the format in use. */ | ||
| 65 | do | ||
| 66 | { | ||
| 67 | sec = bcd_to_bin (cmos_read (RTC_REG_SEC)); | ||
| 68 | min = bcd_to_bin (cmos_read (RTC_REG_MIN)); | ||
| 69 | hour = bcd_to_bin (cmos_read (RTC_REG_HOUR)); | ||
| 70 | mday = bcd_to_bin (cmos_read (RTC_REG_MDAY)); | ||
| 71 | mon = bcd_to_bin (cmos_read (RTC_REG_MON)); | ||
| 72 | year = bcd_to_bin (cmos_read (RTC_REG_YEAR)); | ||
| 73 | } | ||
| 74 | while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC))); | ||
| 75 | |||
| 76 | /* Translate years-since-1900 into years-since-1970. | ||
| 77 | If it's before the epoch, assume that it has passed 2000. | ||
| 78 | This will break at 2070, but that's long after our 31-bit | ||
| 79 | time_t breaks in 2038. */ | ||
| 80 | if (year < 70) | ||
| 81 | year += 100; | ||
| 82 | year -= 70; | ||
| 83 | |||
| 84 | /* Break down all components into seconds. */ | ||
| 85 | time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60; | ||
| 86 | for (i = 1; i <= mon; i++) | ||
| 87 | time += days_per_month[i - 1] * 24 * 60 * 60; | ||
| 88 | if (mon > 2 && year % 4 == 0) | ||
| 89 | time += 24 * 60 * 60; | ||
| 90 | time += (mday - 1) * 24 * 60 * 60; | ||
| 91 | time += hour * 60 * 60; | ||
| 92 | time += min * 60; | ||
| 93 | time += sec; | ||
| 94 | |||
| 95 | return time; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Returns the integer value of the given BCD byte. */ | ||
| 99 | static int | ||
| 100 | bcd_to_bin (uint8_t x) | ||
| 101 | { | ||
| 102 | return (x & 0x0f) + ((x >> 4) * 10); | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Reads a byte from the CMOS register with the given INDEX and | ||
| 106 | returns the byte read. */ | ||
| 107 | static uint8_t | ||
| 108 | cmos_read (uint8_t index) | ||
| 109 | { | ||
| 110 | outb (CMOS_REG_SET, index); | ||
| 111 | return inb (CMOS_REG_IO); | ||
| 112 | } | ||
