aboutsummaryrefslogtreecommitdiff
path: root/core/ipmi.c
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2014-08-15 14:18:45 +1000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-08-15 14:36:47 +1000
commitad48bf878347b096e4d126c4cd7d748dd872f018 (patch)
treeb96395750dc962ba61b72b97c0e545c9df73dddc /core/ipmi.c
parentaa3a45f47b0e85f453a5c2d1414bba668328737e (diff)
downloadskiboot-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>
Diffstat (limited to 'core/ipmi.c')
-rw-r--r--core/ipmi.c98
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);
}