diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc
index 01117680..cf174c86 100644
--- a/core/pldm/Makefile.inc
+++ b/core/pldm/Makefile.inc
@@ -16,7 +16,7 @@ PLDM_OBJS = pldm-common.o pldm-responder.o pldm-requester.o
 PLDM_OBJS += pldm-base-requests.o pldm-platform-requests.o
 PLDM_OBJS += pldm-bios-requests.o pldm-fru-requests.o
 PLDM_OBJS += pldm-file-io-requests.o pldm-lid-files.o
-PLDM_OBJS += pldm-watchdog.o
+PLDM_OBJS += pldm-watchdog.o pldm-rtc.o
 
 PLDM = $(PLDM_DIR)/built-in.a
 $(PLDM): $(PLDM_OBJS:%=$(PLDM_DIR)/%)
diff --git a/core/pldm/pldm-bios-requests.c b/core/pldm/pldm-bios-requests.c
index 66a46bb8..09a30b2b 100644
--- a/core/pldm/pldm-bios-requests.c
+++ b/core/pldm/pldm-bios-requests.c
@@ -6,9 +6,11 @@
 #include <opal.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 #include <inttypes.h>
 #include <pldm/libpldm/bios.h>
 #include <pldm/libpldm/bios_table.h>
+#include <pldm/libpldm/utils.h>
 #include "pldm.h"
 
 /*
@@ -84,6 +86,138 @@ static void bios_init_complete(bool success)
 	bios_ready = true;
 }
 
+struct get_date_time_resp {
+	uint8_t completion_code;
+	uint8_t seconds;
+	uint8_t minutes;
+	uint8_t hours;
+	uint8_t day;
+	uint8_t month;
+	uint16_t year;
+};
+
+/*
+ * Send/receive a PLDM GetBiosDateTime request message
+ */
+int pldm_bios_get_date_time_req(struct tm *tm)
+{
+	char request_msg[PKT_SIZE(0)]; /* the command doesn't have a message payload */
+	size_t response_len, payload_len;
+	void *response_msg;
+	int rc;
+
+	struct get_date_time_resp response;
+
+	/* Encode the date time request */
+	rc = encode_get_date_time_req(
+				DEFAULT_INSTANCE_ID,
+				(struct pldm_msg *)request_msg);
+	if (rc != PLDM_SUCCESS) {
+		prlog(PR_ERR, "Encode GetBiosDateTimeReq Error, rc: %d\n",
+			      rc);
+		return OPAL_PARAMETER;
+	}
+
+	/* Send and get the response message bytes */
+	rc = pldm_do_request(BMC_EID, request_msg, sizeof(request_msg),
+			     &response_msg, &response_len);
+	if (rc) {
+		prlog(PR_ERR, "Communication Error, req: GetBiosDateTimeReq, rc: %d\n", rc);
+		return rc;
+	}
+
+	/* Decode the message */
+	payload_len = response_len - sizeof(struct pldm_msg_hdr);
+	rc = decode_get_date_time_resp(
+				response_msg,
+				payload_len,
+				&response.completion_code,
+				&response.seconds,
+				&response.minutes,
+				&response.hours,
+				&response.day,
+				&response.month,
+				&response.year);
+	if (rc != PLDM_SUCCESS || response.completion_code != PLDM_SUCCESS) {
+		prlog(PR_ERR, "Decode GetBiosDateTimeReq Error, rc: %d, cc: %d\n",
+			      rc, response.completion_code);
+		free(response_msg);
+		return OPAL_PARAMETER;
+	}
+
+	/* The data arrives from BMC in BCD format. Convert it to
+	 * decimal for processing
+	 */
+	tm->tm_sec = bcd2dec8(response.seconds);
+	tm->tm_min = bcd2dec8(response.minutes);
+	tm->tm_hour = bcd2dec8(response.hours);
+	tm->tm_mday = bcd2dec8(response.day);
+	tm->tm_mon = bcd2dec8(response.month);
+	tm->tm_year = bcd2dec16(response.year);
+
+	if (!is_time_legal(tm->tm_sec, tm->tm_min, tm->tm_hour,
+			   tm->tm_mday, tm->tm_mon, tm->tm_year)) {
+		prlog(PR_ERR, "Invalid date time value\n");
+		free(response_msg);
+		return OPAL_PARAMETER;
+	}
+
+	free(response_msg);
+
+	return OPAL_SUCCESS;
+}
+
+/*
+ * Send/receive a PLDM SetBiosDateTime request message
+ */
+int pldm_bios_set_date_time_req(struct tm *tm)
+{
+	char request_msg[PKT_SIZE(struct pldm_set_date_time_req)];
+	size_t response_len, payload_len;
+	uint8_t completion_code;
+	void *response_msg;
+	int rc;
+
+	/* Encode the date time request */
+	rc = encode_set_date_time_req(
+				DEFAULT_INSTANCE_ID,
+				tm->tm_sec, tm->tm_min, tm->tm_hour,
+				tm->tm_mday, tm->tm_mon, tm->tm_year,
+				(struct pldm_msg *)request_msg,
+				sizeof(struct pldm_set_date_time_req));
+	if (rc != PLDM_SUCCESS) {
+		prlog(PR_ERR, "Encode SetBiosDateTimeReq Error, rc: %d\n",
+			      rc);
+		return OPAL_PARAMETER;
+	}
+
+	/* Send and get the response message bytes */
+	rc = pldm_do_request(BMC_EID, request_msg, sizeof(request_msg),
+			     &response_msg, &response_len);
+	if (rc) {
+		prlog(PR_ERR, "Communication Error, req: SetBiosDateTimeReq, rc: %d\n", rc);
+		return rc;
+	}
+
+	/* Decode the message */
+	payload_len = response_len - sizeof(struct pldm_msg_hdr);
+	rc = decode_set_date_time_resp(
+				response_msg,
+				payload_len,
+				&completion_code);
+	if (rc != PLDM_SUCCESS || (completion_code > PLDM_ERROR)) {
+		/* FIXME: Time value from OPAL_RTC_WRITE is never correct */
+		prlog(PR_ERR, "Decode SetBiosDateTimeReq Error, rc: %d, cc: %d\n",
+			      rc, completion_code);
+		free(response_msg);
+		return OPAL_PARAMETER;
+	}
+
+	free(response_msg);
+
+	return OPAL_SUCCESS;
+}
+
 /*
  * parse a string, format:
  * <ATTR_a>=<lid_id_1>,<ATTR_b>=<lid_id_2>
diff --git a/core/pldm/pldm-rtc.c b/core/pldm/pldm-rtc.c
new file mode 100644
index 00000000..e449b73e
--- /dev/null
+++ b/core/pldm/pldm-rtc.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+// Copyright 2022 IBM Corp.
+
+#define pr_fmt(fmt) "PLDM: " fmt
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <time-utils.h>
+#include <device.h>
+#include <opal.h>
+#include <rtc.h>
+#include "pldm.h"
+
+static int64_t get_and_cache_date_time(struct tm *tm)
+{
+	int rc;
+
+	rc = pldm_bios_get_date_time_req(tm);
+	if (!rc)
+		rtc_cache_update(tm);
+
+	return rc;
+}
+
+static int64_t pldm_opal_rtc_read(__be32 *__ymd, __be64 *__hmsm)
+{
+	struct tm tm;
+	uint32_t ymd;
+	uint64_t hmsm;
+	int rc;
+
+	if (!__ymd || !__hmsm)
+		return OPAL_PARAMETER;
+
+	rc = get_and_cache_date_time(&tm);
+	if ((rc) && (rc != OPAL_BUSY))
+		return OPAL_HARDWARE;
+
+	if (rc == OPAL_BUSY)
+		return OPAL_BUSY_EVENT;
+
+	rtc_cache_get_datetime(&ymd, &hmsm);
+	*__ymd = cpu_to_be32(ymd);
+	*__hmsm = cpu_to_be64(hmsm);
+
+	return OPAL_SUCCESS;
+}
+
+static int64_t pldm_opal_rtc_write(uint32_t year_month_day,
+				   uint64_t hour_minute_second_millisecond)
+{
+	struct tm tm;
+	int rc;
+
+	datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm);
+
+	rc = pldm_bios_set_date_time_req(&tm);
+	if (rc == OPAL_BUSY)
+		return OPAL_BUSY_EVENT;
+
+	return OPAL_SUCCESS;
+}
+
+void pldm_rtc_init(void)
+{
+	struct tm tm;
+	struct dt_node *np = dt_new(opal_node, "rtc");
+
+	dt_add_property_strings(np, "compatible", "ibm,opal-rtc");
+
+	opal_register(OPAL_RTC_READ, pldm_opal_rtc_read, 2);
+	opal_register(OPAL_RTC_WRITE, pldm_opal_rtc_write, 2);
+
+	get_and_cache_date_time(&tm);
+}
diff --git a/core/pldm/pldm.h b/core/pldm/pldm.h
index ef884995..81df9f8b 100644
--- a/core/pldm/pldm.h
+++ b/core/pldm/pldm.h
@@ -5,6 +5,7 @@
 #ifndef __COREPLDM_H__
 #define __COREPLDM_H__
 
+#include <time.h>
 #include <pldm/libpldm/base.h>
 #include <pldm/libpldm/utils.h>
 #include <pldm.h>
@@ -94,6 +95,8 @@ void pldm_fru_add_record(uint32_t *fru_table_length,
 int pldm_fru_get_table(void **fru_record_table_bytes,
 		       uint32_t *fru_record_table_size);
 
+int pldm_bios_get_date_time_req(struct tm *tm);
+int pldm_bios_set_date_time_req(struct tm *tm);
 int pldm_bios_find_lid_by_attr_name(const char *name, char **lid);
 int pldm_bios_get_lids_id(char **lid_ids_string);
 int pldm_bios_init(void);
diff --git a/include/pldm.h b/include/pldm.h
index 5acdcbbe..019e811f 100644
--- a/include/pldm.h
+++ b/include/pldm.h
@@ -54,4 +54,9 @@ int pldm_watchdog_init(void);
 int pldm_platform_send_progress_state_change(
 		enum pldm_state_set_boot_progress_state_values state);
 
+/**
+ * Configure real-time clock
+ */
+void pldm_rtc_init(void);
+
 #endif /* __PLDM_H__ */
