diff mbox series

[V3,13/15] core/pldm: Register OPAL_RTC_READ/WRITE calls back

Message ID 20220913102724.65563-14-clombard@linux.vnet.ibm.com
State Superseded
Headers show
Series Complete PLDM responder and enable PLDM support | expand

Commit Message

Christophe Lombard Sept. 13, 2022, 10:27 a.m. UTC
OPAL_RTC_READ/WRITE are used to retrieve and write the time. PLDM stack
provides GetBiosDateTimeReq and SetBiosDateTimeReq commands to exercise.

Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com>
---
 core/pldm/Makefile.inc |   2 +-
 core/pldm/pldm-rtc.c   | 244 +++++++++++++++++++++++++++++++++++++++++
 include/pldm.h         |   5 +
 3 files changed, 250 insertions(+), 1 deletion(-)
 create mode 100644 core/pldm/pldm-rtc.c

Comments

Abhishek Singh Tomar Oct. 3, 2022, 5:28 a.m. UTC | #1
On Tue, Sep 13, 2022 at 12:27:22PM +0200, Christophe Lombard wrote:
> OPAL_RTC_READ/WRITE are used to retrieve and write the time. PLDM stack
> provides GetBiosDateTimeReq and SetBiosDateTimeReq commands to exercise.
> 
> Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com>
Reviedwe-by: Abhishek Singh Tomar <abhishek@linux.ibm.com>
> ---
>  core/pldm/Makefile.inc |   2 +-
>  core/pldm/pldm-rtc.c   | 244 +++++++++++++++++++++++++++++++++++++++++
>  include/pldm.h         |   5 +
>  3 files changed, 250 insertions(+), 1 deletion(-)
>  create mode 100644 core/pldm/pldm-rtc.c
> 
> diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc
> index 5d10d572..00d6c458 100644
> --- a/core/pldm/Makefile.inc
> +++ b/core/pldm/Makefile.inc
> @@ -16,7 +16,7 @@ PLDM_OBJS = pldm-mctp.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-rtc.c b/core/pldm/pldm-rtc.c
> new file mode 100644
> index 00000000..b6702a0b
> --- /dev/null
> +++ b/core/pldm/pldm-rtc.c
> @@ -0,0 +1,244 @@
> +// 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/libpldm/bios.h>
> +#include <pldm/libpldm/utils.h>
> +#include "pldm.h"
> +
> +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;
> +};
> +
> +static enum {idle, waiting, updated, error} time_status;
> +
> +static void cache_get_datetime(struct tm *tm)
> +{
> +	if (tm == NULL)
> +		time_status = error;
> +	else {
> +		rtc_cache_update(tm);
> +		time_status = updated;
> +	}
> +}
> +
> +static void get_date_time_req_complete(struct pldm_rx_data *rx,
> +				       void *data __unused)
> +{
> +	struct get_date_time_resp response;
> +	size_t payload_len;
> +	struct tm tm;
> +	int rc;
> +
> +	if (rx == NULL) {
> +		prlog(PR_ERR, "%s: Response not received\n", __func__);
> +		cache_get_datetime(NULL);
> +		return;
> +	}
> +
> +	/* Decode the message */
> +	payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
> +	rc = decode_get_date_time_resp(
> +			rx->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);
> +		cache_get_datetime(NULL);
> +		return;
> +	}
> +
> +	/* 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, "%s: Invalid date time value\n", __func__);
> +		cache_get_datetime(NULL);
> +		return;
> +	}
> +
> +	cache_get_datetime(&tm);
> +}
> +
> +/*
> + * Send a PLDM GetBiosDateTime request message
> + */
> +static int get_date_time_req(void)
> +{
> +	char request_msg[PKT_SIZE(0)]; /* the command doesn't have a message payload */
> +	int rc;
> +
> +	/* 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;
> +	}
> +
> +	/* Queue and get the response message bytes */
> +	rc = pldm_requester_queue(request_msg, sizeof(request_msg),
> +				  get_date_time_req_complete, NULL);
> +	if (rc) {
> +		prlog(PR_ERR, "Communication Error, req: GetBiosDateTimeReq, rc: %d\n", rc);
> +		return rc;
> +	}
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +static int64_t pldm_opal_rtc_read(__be32 *__ymd, __be64 *__hmsm)
> +{
> +	uint32_t ymd;
> +	uint64_t hmsm;
> +	int rc = OPAL_SUCCESS;
> +
> +	if (!__ymd || !__hmsm)
> +		return OPAL_PARAMETER;
> +
> +	switch (time_status) {
> +	case idle:
> +		rc = get_date_time_req();
> +		if (rc)
> +			return OPAL_HARDWARE;
> +		time_status = waiting;
> +		rc = OPAL_BUSY_EVENT;
> +		break;
> +
> +	case waiting:
> +		rc = 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;
> +		rc = OPAL_SUCCESS;
> +		break;
> +
> +	case error:
> +		time_status = idle;
> +		rc = OPAL_HARDWARE;
> +		break;
> +	}
> +
> +	return rc;
> +}
> +
> +/*
> + * Receive the PLDM SetBiosDateTime response
> + */
> +static void set_date_time_req_complete(struct pldm_rx_data *rx,
> +				       void *data __unused)
> +{
> +	uint8_t completion_code;
> +	size_t payload_len;
> +	int rc;
> +
> +	if (rx == NULL) {
> +		prlog(PR_ERR, "%s: Response not received\n", __func__);
> +		return;
> +	}
> +
> +	/* Decode the message */
> +	payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
> +
> +	rc = decode_set_date_time_resp(rx->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);
> +	}
> +}
> +
> +/*
> + * Send a PLDM SetBiosDateTime request message
> + */
> +static int set_date_time_req(struct tm *tm)
> +{
> +	char request_msg[PKT_SIZE(struct pldm_set_date_time_req)];
> +	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;
> +	}
> +
> +	/* Queue and get the response message bytes */
> +	rc = pldm_requester_queue(request_msg, sizeof(request_msg),
> +				  set_date_time_req_complete, NULL);
> +	if (rc) {
> +		prlog(PR_ERR, "Communication Error, req: SetBiosDateTimeReq, rc: %d\n", rc);
> +		return rc;
> +	}
> +
> +	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 = set_date_time_req(&tm);
> +	if (rc == OPAL_BUSY)
> +		return OPAL_BUSY_EVENT;
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +void pldm_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, pldm_opal_rtc_read, 2);
> +	opal_register(OPAL_RTC_WRITE, pldm_opal_rtc_write, 2);
> +
> +	/* Initialise the rtc cache */
> +	get_date_time_req();
> +}
> diff --git a/include/pldm.h b/include/pldm.h
> index 2100c12c..db24040d 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__ */
> -- 
> 2.37.3
> 
> _______________________________________________
> Skiboot mailing list
> Skiboot@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/skiboot
diff mbox series

Patch

diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc
index 5d10d572..00d6c458 100644
--- a/core/pldm/Makefile.inc
+++ b/core/pldm/Makefile.inc
@@ -16,7 +16,7 @@  PLDM_OBJS = pldm-mctp.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-rtc.c b/core/pldm/pldm-rtc.c
new file mode 100644
index 00000000..b6702a0b
--- /dev/null
+++ b/core/pldm/pldm-rtc.c
@@ -0,0 +1,244 @@ 
+// 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/libpldm/bios.h>
+#include <pldm/libpldm/utils.h>
+#include "pldm.h"
+
+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;
+};
+
+static enum {idle, waiting, updated, error} time_status;
+
+static void cache_get_datetime(struct tm *tm)
+{
+	if (tm == NULL)
+		time_status = error;
+	else {
+		rtc_cache_update(tm);
+		time_status = updated;
+	}
+}
+
+static void get_date_time_req_complete(struct pldm_rx_data *rx,
+				       void *data __unused)
+{
+	struct get_date_time_resp response;
+	size_t payload_len;
+	struct tm tm;
+	int rc;
+
+	if (rx == NULL) {
+		prlog(PR_ERR, "%s: Response not received\n", __func__);
+		cache_get_datetime(NULL);
+		return;
+	}
+
+	/* Decode the message */
+	payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
+	rc = decode_get_date_time_resp(
+			rx->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);
+		cache_get_datetime(NULL);
+		return;
+	}
+
+	/* 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, "%s: Invalid date time value\n", __func__);
+		cache_get_datetime(NULL);
+		return;
+	}
+
+	cache_get_datetime(&tm);
+}
+
+/*
+ * Send a PLDM GetBiosDateTime request message
+ */
+static int get_date_time_req(void)
+{
+	char request_msg[PKT_SIZE(0)]; /* the command doesn't have a message payload */
+	int rc;
+
+	/* 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;
+	}
+
+	/* Queue and get the response message bytes */
+	rc = pldm_requester_queue(request_msg, sizeof(request_msg),
+				  get_date_time_req_complete, NULL);
+	if (rc) {
+		prlog(PR_ERR, "Communication Error, req: GetBiosDateTimeReq, rc: %d\n", rc);
+		return rc;
+	}
+
+	return OPAL_SUCCESS;
+}
+
+static int64_t pldm_opal_rtc_read(__be32 *__ymd, __be64 *__hmsm)
+{
+	uint32_t ymd;
+	uint64_t hmsm;
+	int rc = OPAL_SUCCESS;
+
+	if (!__ymd || !__hmsm)
+		return OPAL_PARAMETER;
+
+	switch (time_status) {
+	case idle:
+		rc = get_date_time_req();
+		if (rc)
+			return OPAL_HARDWARE;
+		time_status = waiting;
+		rc = OPAL_BUSY_EVENT;
+		break;
+
+	case waiting:
+		rc = 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;
+		rc = OPAL_SUCCESS;
+		break;
+
+	case error:
+		time_status = idle;
+		rc = OPAL_HARDWARE;
+		break;
+	}
+
+	return rc;
+}
+
+/*
+ * Receive the PLDM SetBiosDateTime response
+ */
+static void set_date_time_req_complete(struct pldm_rx_data *rx,
+				       void *data __unused)
+{
+	uint8_t completion_code;
+	size_t payload_len;
+	int rc;
+
+	if (rx == NULL) {
+		prlog(PR_ERR, "%s: Response not received\n", __func__);
+		return;
+	}
+
+	/* Decode the message */
+	payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
+
+	rc = decode_set_date_time_resp(rx->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);
+	}
+}
+
+/*
+ * Send a PLDM SetBiosDateTime request message
+ */
+static int set_date_time_req(struct tm *tm)
+{
+	char request_msg[PKT_SIZE(struct pldm_set_date_time_req)];
+	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;
+	}
+
+	/* Queue and get the response message bytes */
+	rc = pldm_requester_queue(request_msg, sizeof(request_msg),
+				  set_date_time_req_complete, NULL);
+	if (rc) {
+		prlog(PR_ERR, "Communication Error, req: SetBiosDateTimeReq, rc: %d\n", rc);
+		return rc;
+	}
+
+	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 = set_date_time_req(&tm);
+	if (rc == OPAL_BUSY)
+		return OPAL_BUSY_EVENT;
+
+	return OPAL_SUCCESS;
+}
+
+void pldm_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, pldm_opal_rtc_read, 2);
+	opal_register(OPAL_RTC_WRITE, pldm_opal_rtc_write, 2);
+
+	/* Initialise the rtc cache */
+	get_date_time_req();
+}
diff --git a/include/pldm.h b/include/pldm.h
index 2100c12c..db24040d 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__ */