aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/ipmi/ipmi-rtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/ipmi/ipmi-rtc.c')
-rw-r--r--roms/skiboot/hw/ipmi/ipmi-rtc.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/roms/skiboot/hw/ipmi/ipmi-rtc.c b/roms/skiboot/hw/ipmi/ipmi-rtc.c
new file mode 100644
index 000000000..52da2946c
--- /dev/null
+++ b/roms/skiboot/hw/ipmi/ipmi-rtc.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Talk to a Real Time Clock (RTC) over IPMI
+ *
+ * Copyright 2013-2015 IBM Corp.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ipmi.h>
+#include <time.h>
+#include <time-utils.h>
+#include <device.h>
+#include <opal.h>
+#include <rtc.h>
+
+static enum {idle, waiting, updated, error} time_status;
+
+static void get_sel_time_error(struct ipmi_msg *msg)
+{
+ time_status = error;
+ ipmi_free_msg(msg);
+}
+
+static void get_sel_time_complete(struct ipmi_msg *msg)
+{
+ struct tm tm;
+ le32 result;
+ time_t time;
+
+ memcpy(&result, msg->data, 4);
+ time = le32_to_cpu(result);
+ gmtime_r(&time, &tm);
+ rtc_cache_update(&tm);
+ time_status = updated;
+ ipmi_free_msg(msg);
+}
+
+static int64_t ipmi_get_sel_time(void)
+{
+ struct ipmi_msg *msg;
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_GET_SEL_TIME,
+ get_sel_time_complete, NULL, NULL, 0, 4);
+ if (!msg)
+ return OPAL_HARDWARE;
+
+ msg->error = get_sel_time_error;
+
+ return ipmi_queue_msg(msg);
+}
+
+static int64_t ipmi_set_sel_time(uint32_t _tv)
+{
+ struct ipmi_msg *msg;
+ const le32 tv = cpu_to_le32(_tv);
+
+ msg = ipmi_mkmsg_simple(IPMI_SET_SEL_TIME, (void*)&tv, sizeof(tv));
+ if (!msg)
+ return OPAL_HARDWARE;
+
+ return ipmi_queue_msg(msg);
+}
+
+static int64_t ipmi_opal_rtc_read(__be32 *__ymd, __be64 *__hmsm)
+{
+ int ret = 0;
+ uint32_t ymd;
+ uint64_t hmsm;
+
+ if (!__ymd || !__hmsm)
+ return OPAL_PARAMETER;
+
+ switch(time_status) {
+ case idle:
+ if (ipmi_get_sel_time() < 0)
+ return OPAL_HARDWARE;
+ time_status = waiting;
+ ret = OPAL_BUSY_EVENT;
+ break;
+
+ case waiting:
+ ret = OPAL_BUSY_EVENT;
+ break;
+
+ case updated:
+ rtc_cache_get_datetime(&ymd, &hmsm);
+ *__ymd = cpu_to_be32(ymd);
+ *__hmsm = cpu_to_be64(hmsm);
+ time_status = idle;
+ ret = OPAL_SUCCESS;
+ break;
+
+ case error:
+ time_status = idle;
+ ret = OPAL_HARDWARE;
+ break;
+ }
+
+ return ret;
+}
+
+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);
+ if (ipmi_set_sel_time(t))
+ return OPAL_HARDWARE;
+
+ return OPAL_SUCCESS;
+}
+
+void ipmi_rtc_init(void)
+{
+ struct dt_node *np = dt_new(opal_node, "rtc");
+ dt_add_property_strings(np, "compatible", "ibm,opal-rtc");
+
+ opal_register(OPAL_RTC_READ, ipmi_opal_rtc_read, 2);
+ opal_register(OPAL_RTC_WRITE, ipmi_opal_rtc_write, 2);
+
+ /* Initialise the rtc cache */
+ ipmi_get_sel_time();
+}