diff options
-rw-r--r-- | core/Makefile.inc | 3 | ||||
-rw-r--r-- | core/time-utils.c | 94 | ||||
-rw-r--r-- | hw/fsp/fsp-rtc.c | 156 | ||||
-rw-r--r-- | include/time-utils.h | 26 |
4 files changed, 130 insertions, 149 deletions
diff --git a/core/Makefile.inc b/core/Makefile.inc index 274318d..714da3a 100644 --- a/core/Makefile.inc +++ b/core/Makefile.inc @@ -6,8 +6,7 @@ CORE_OBJS += malloc.o lock.o cpu.o utils.o fdt.o opal.o interrupts.o CORE_OBJS += timebase.o opal-msg.o pci.o pci-opal.o fast-reboot.o CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o CORE_OBJS += hostservices.o platform.o nvram.o flash-nvram.o hmi.o -CORE_OBJS += console-log.o ipmi.o +CORE_OBJS += console-log.o ipmi.o time-utils.o CORE=core/built-in.o $(CORE): $(CORE_OBJS:%=core/%) - diff --git a/core/time-utils.c b/core/time-utils.c new file mode 100644 index 0000000..10a4da4 --- /dev/null +++ b/core/time-utils.c @@ -0,0 +1,94 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <time-utils.h> + +/* MSB is byte 3, LSB is byte 0 */ +static unsigned int bcd_byte(uint32_t bcd, int byteno) +{ + bcd >>= byteno * 8; + return (bcd >> 4 & 0xf) * 10 + (bcd & 0xf); +} + +static uint32_t int_to_bcd2(unsigned int x) +{ + return (((x / 10) << 4) & 0xf0) | (x % 10); +} + +static uint32_t int_to_bcd4(unsigned int x) +{ + return int_to_bcd2(x / 100) << 8 | int_to_bcd2(x % 100); +} + +/* + * Converts an OPAL formated datetime into a struct tm. We ignore microseconds + * as Linux doesn't use them anyway. + * + * | year | month | mday | + * +------------------------------------+ + * | hour | minute | secs | reserved | + * +------------------------------------+ + * | microseconds | + */ + +void datetime_to_tm(uint32_t y_m_d, uint64_t h_m_s_m, struct tm *tm) +{ + uint32_t x; + + tm->tm_year = bcd_byte(y_m_d, 3) * 100 + bcd_byte(y_m_d, 2); + tm->tm_mon = bcd_byte(y_m_d, 1) - 1; + tm->tm_mday = bcd_byte(y_m_d, 0); + + x = h_m_s_m >> 32; + tm->tm_hour = bcd_byte(x, 3); + tm->tm_min = bcd_byte(x, 2); + tm->tm_sec = bcd_byte(x, 1); +} + +/* + * The OPAL API is defined as returned a u64 of a similar + * format to the FSP message; the 32-bit date field is + * in the format: + * + * | year | month | mday | + * + * ... and the 64-bit time field is in the format + * + * | hour | minutes | secs | millisec | + * | ------------------------------------- + * | millisec | reserved | + * + * We simply ignore the microseconds/milliseconds for now + * as I don't quite understand why the OPAL API defines that + * it needs 6 digits for the milliseconds :-) I suspect the + * doc got that wrong and it's supposed to be micro but + * let's ignore it. + * + * Note that Linux doesn't use nor set the ms field anyway. + */ +void tm_to_datetime(struct tm *tm, uint32_t *y_m_d, uint64_t *h_m_s_m) +{ + uint64_t h_m_s; + *y_m_d = int_to_bcd4(tm->tm_year) << 16 | + int_to_bcd2(tm->tm_mon + 1) << 8 | + int_to_bcd2(tm->tm_mday); + + h_m_s = int_to_bcd2(tm->tm_hour) << 24 | + int_to_bcd2(tm->tm_min) << 16 | + int_to_bcd2(tm->tm_sec) << 8; + + *h_m_s_m = h_m_s << 32; +} diff --git a/hw/fsp/fsp-rtc.c b/hw/fsp/fsp-rtc.c index 2ec88ca..69b2c73 100644 --- a/hw/fsp/fsp-rtc.c +++ b/hw/fsp/fsp-rtc.c @@ -19,6 +19,7 @@ #include <lock.h> #include <timebase.h> #include <time.h> +#include <time-utils.h> #include <opal-msg.h> #include <fsp-elog.h> #include <device.h> @@ -97,155 +98,14 @@ DEFINE_LOG_ENTRY(OPAL_RC_RTC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_RTC, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA, NULL); -static int days_in_month(int month, int year) -{ - static int month_days[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, - }; - - assert(1 <= month && month <= 12); - - /* we may need to update this in the year 4000, pending a - * decision on whether or not it's a leap year */ - if (month == 2) { - bool is_leap = !(year % 400) || ((year % 100) && !(year % 4)); - return is_leap ? 29 : 28; - } - - return month_days[month - 1]; -} - static void tm_add(struct tm *in, struct tm *out, unsigned long secs) { - unsigned long year, month, mday, hour, minute, second, d; - static const unsigned long sec_in_400_years = - ((303ul * 365) + (97 * 366)) * 24 * 60 * 60; - assert(in); assert(out); - second = in->tm_sec; - minute = in->tm_min; - hour = in->tm_hour; - mday = in->tm_mday; - month = in->tm_mon; - year = in->tm_year; - - second += secs; - - /* There are the same number of seconds in any 400-year block; this - * limits the iterations in the loop below */ - year += 400 * (second / sec_in_400_years); - second = second % sec_in_400_years; - - if (second >= 60) { - minute += second / 60; - second = second % 60; - } - - if (minute >= 60) { - hour += minute / 60; - minute = minute % 60; - } - - if (hour >= 24) { - mday += hour / 24; - hour = hour % 24; - } - - for (d = days_in_month(month, year); mday >= d; - d = days_in_month(month, year)) { - month++; - if (month > 12) { - month = 1; - year++; - } - mday -= d; - } - - out->tm_year = year; - out->tm_mon = month; - out->tm_mday = mday; - out->tm_hour = hour; - out->tm_min = minute; - out->tm_sec = second; -} - -/* MSB is byte 3, LSB is byte 0 */ -static unsigned int bcd_byte(uint32_t bcd, int byteno) -{ - bcd >>= byteno * 8; - return (bcd >> 4 & 0xf) * 10 + (bcd & 0xf); -} - -static uint32_t int_to_bcd2(unsigned int x) -{ - return (((x / 10) << 4) & 0xf0) | (x % 10); -} - -static uint32_t int_to_bcd4(unsigned int x) -{ - return int_to_bcd2(x / 100) << 8 | int_to_bcd2(x % 100); -} - -static void rtc_to_tm(struct fsp_msg *msg, struct tm *tm) -{ - uint32_t x; - - /* The FSP returns in BCD: - * - * | year | month | mday | - * +------------------------------------+ - * | hour | minute | secs | reserved | - * +------------------------------------+ - * | microseconds | - */ - x = msg->data.words[0]; - tm->tm_year = bcd_byte(x, 3) * 100 + bcd_byte(x, 2); - tm->tm_mon = bcd_byte(x, 1); - tm->tm_mday = bcd_byte(x, 0); - - x = msg->data.words[1]; - tm->tm_hour = bcd_byte(x, 3); - tm->tm_min = bcd_byte(x, 2); - tm->tm_sec = bcd_byte(x, 1); -} - -static void tm_to_datetime(struct tm *tm, uint32_t *y_m_d, uint64_t *h_m_s_m) -{ - uint64_t h_m_s; - /* - * The OPAL API is defined as returned a u64 of a similar - * format to the FSP message; the 32-bit date field is - * in the format: - * - * | year | year | month | day | - * - */ - *y_m_d = int_to_bcd4(tm->tm_year) << 16 | - int_to_bcd2(tm->tm_mon) << 8 | - int_to_bcd2(tm->tm_mday); - - /* - * ... and the 64-bit time field is in the format - * - * | hour | minutes | secs | millisec | - * | ------------------------------------- - * | millisec | reserved | - * - * We simply ignore the microseconds/milliseconds for now - * as I don't quite understand why the OPAL API defines that - * it needs 6 digits for the milliseconds :-) I suspect the - * doc got that wrong and it's supposed to be micro but - * let's ignore it. - * - * Note that Linux doesn't use nor set the ms field anyway. - */ - h_m_s = int_to_bcd2(tm->tm_hour) << 24 | - int_to_bcd2(tm->tm_min) << 16 | - int_to_bcd2(tm->tm_sec) << 8; - - *h_m_s_m = h_m_s << 32; + *out = *in; + out->tm_sec += secs; + mktime(out); } static void fsp_tpo_req_complete(struct fsp_msg *read_resp) @@ -313,7 +173,8 @@ static void fsp_rtc_process_read(struct fsp_msg *read_resp) case FSP_STATUS_SUCCESS: /* Save the read RTC value in our cache */ - rtc_to_tm(read_resp, &rtc_tod_cache.tm); + datetime_to_tm(read_resp->data.words[0], + (u64) read_resp->data.words[1] << 32, &rtc_tod_cache.tm); rtc_tod_cache.tb = mftb(); rtc_tod_state = RTC_TOD_VALID; break; @@ -514,7 +375,7 @@ static int64_t fsp_opal_rtc_write(uint32_t year_month_day, w0 = year_month_day; w1 = (hour_minute_second_millisecond >> 32) & 0xffffff00; w2 = 0; - + rtc_write_msg = fsp_mkmsg(FSP_CMD_WRITE_TOD, 3, w0, w1, w2); if (!rtc_write_msg) { DBG(" -> allocation failed !\n"); @@ -524,7 +385,8 @@ static int64_t fsp_opal_rtc_write(uint32_t year_month_day, DBG(" -> req at %p\n", rtc_write_msg); if (fsp_in_reset) { - rtc_to_tm(rtc_write_msg, &rtc_tod_cache.tm); + datetime_to_tm(rtc_write_msg->data.words[0], + (u64) rtc_write_msg->data.words[1] << 32, &rtc_tod_cache.tm); rtc_tod_cache.tb = mftb(); rtc_tod_cache.dirty = true; fsp_freemsg(rtc_write_msg); diff --git a/include/time-utils.h b/include/time-utils.h new file mode 100644 index 0000000..721b6f7 --- /dev/null +++ b/include/time-utils.h @@ -0,0 +1,26 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIME_UTILS_H +#define __TIME_UTILS_H + +#include <stdint.h> +#include <time.h> + +void tm_to_datetime(struct tm *tm, uint32_t *y_m_d, uint64_t *h_m_s_m); +void datetime_to_tm(uint32_t y_m_d, uint64_t h_m_s_m, struct tm *tm); + +#endif |