diff options
author | Alistair Popple <alistair@popple.id.au> | 2014-08-15 14:18:45 +1000 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-08-15 14:36:47 +1000 |
commit | ad48bf878347b096e4d126c4cd7d748dd872f018 (patch) | |
tree | b96395750dc962ba61b72b97c0e545c9df73dddc | |
parent | aa3a45f47b0e85f453a5c2d1414bba668328737e (diff) | |
download | skiboot-ad48bf878347b096e4d126c4cd7d748dd872f018.zip skiboot-ad48bf878347b096e4d126c4cd7d748dd872f018.tar.gz skiboot-ad48bf878347b096e4d126c4cd7d748dd872f018.tar.bz2 |
ipmi: Add an IPMI command to get and set the RTC
Add IPMI GET_SEL_TIME and SET_SEL_TIME commands to the IPMI stack.
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | core/ipmi.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/core/ipmi.c b/core/ipmi.c index 6cfded6..1278c09 100644 --- a/core/ipmi.c +++ b/core/ipmi.c @@ -18,6 +18,98 @@ #include <bt.h> #include <ipmi.h> #include <opal.h> +#include <time.h> +#include <time-utils.h> + +static time_t time = 0; + +static void ipmi_process_storage_resp(struct ipmi_msg *msg) +{ + uint32_t new_time; + + switch (msg->cmd) { + case IPMI_GET_SEL_TIME_CMD: + /* + * I couldn't find any mention of endianess in the IPMI spec, + * but ipmitool seemed to assume little endian? + */ + memcpy(&new_time, msg->resp_data, 4); + time = le32_to_cpu(new_time); + break; + + case IPMI_SET_SEL_TIME_CMD: + /* Nothing to do in this case */ + break; + + default: + printf("Unsupported/invalid IPMI storage command\n"); + } +} + +static uint32_t time_result; +static int64_t ipmi_get_sel_time(void) +{ + struct ipmi_msg *msg = malloc(sizeof(struct ipmi_msg)); + + if (!msg) + return OPAL_HARDWARE; + + msg->cmd = IPMI_GET_SEL_TIME_CMD; + msg->netfn = IPMI_NETFN_STORAGE_REQUEST; + msg->req_data = NULL; + msg->req_data_len = 0; + msg->resp_data = (uint8_t *) &time_result; + msg->resp_data_len = 4; + if (bt_add_ipmi_msg_wait(msg)) + return -1; + + return time_result; +} + +static int64_t ipmi_set_sel_time(uint32_t tv) +{ + struct ipmi_msg *msg = malloc(sizeof(struct ipmi_msg)); + + if (!msg) + return OPAL_HARDWARE; + + msg->cmd = IPMI_SET_SEL_TIME_CMD; + msg->netfn = IPMI_NETFN_STORAGE_REQUEST; + msg->req_data = (uint8_t *) &tv; + msg->req_data_len = 4; + msg->resp_data = NULL; + msg->resp_data_len = 0; + + return bt_add_ipmi_msg_wait(msg); +} + +static int64_t ipmi_opal_rtc_read(uint32_t *y_m_d, + uint64_t *h_m_s_m) +{ + struct tm tm; + + if (ipmi_get_sel_time() < 0) + return OPAL_HARDWARE; + + gmtime_r(&time, &tm); + tm_to_datetime(&tm, y_m_d, h_m_s_m); + return OPAL_SUCCESS; +} + +static int64_t ipmi_opal_rtc_write(uint32_t year_month_day, + uint64_t hour_minute_second_millisecond) +{ + time_t t; + struct tm tm; + + datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm); + t = mktime(&tm); + t = cpu_to_le32(t); + if (ipmi_set_sel_time(t)) + return OPAL_HARDWARE; + + return OPAL_SUCCESS; +} static void ipmi_cmd_done(struct ipmi_msg *msg) { @@ -27,6 +119,10 @@ static void ipmi_cmd_done(struct ipmi_msg *msg) } switch (msg->netfn) { + case IPMI_NETFN_STORAGE_RESPONSE: + ipmi_process_storage_resp(msg); + break; + case IPMI_NETFN_CHASSIS_RESPONSE: break; default: @@ -66,4 +162,6 @@ int64_t ipmi_opal_chassis_control(uint64_t request) void ipmi_init(void) { bt_init(ipmi_cmd_done); + opal_register(OPAL_RTC_READ, ipmi_opal_rtc_read, 2); + opal_register(OPAL_RTC_WRITE, ipmi_opal_rtc_write, 2); } |