From 6f854558c1e67d99acf76168c734f1345284eabd Mon Sep 17 00:00:00 2001 From: Rahul Bedarkar Date: Mon, 9 May 2022 07:02:34 +0000 Subject: [PATCH] drivers/platform/tegra: Add tegra-epl driver Add tegra-epl driver. It is used to report software detected errors via HSP mailbox or MISC-EC registers to safety service module running on the Functional Safety Island (FSI) Bug 3583609 Change-Id: I5bd3e4bd261d00a353743b01c83ed8df6f00e90a Signed-off-by: Rahul Bedarkar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2710277 Reviewed-by: Laxman Dewangan GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/Makefile | 1 + drivers/platform/tegra/tegra-epl.c | 315 +++++++++++++++++++++++++++++ include/linux/tegra-epl.h | 83 ++++++++ include/uapi/linux/tegra-epl.h | 14 ++ 4 files changed, 413 insertions(+) create mode 100644 drivers/platform/tegra/tegra-epl.c create mode 100644 include/linux/tegra-epl.h create mode 100644 include/uapi/linux/tegra-epl.h diff --git a/drivers/platform/tegra/Makefile b/drivers/platform/tegra/Makefile index e650ca04..79db7dfa 100644 --- a/drivers/platform/tegra/Makefile +++ b/drivers/platform/tegra/Makefile @@ -13,4 +13,5 @@ obj-m += tegra-bootloader-debug.o tegra-cactmon-objs := cactmon.o tegra-cactmon-objs += actmon_common.o obj-m += tegra-cactmon.o +obj-m += tegra-epl.o obj-m += tegra-fsicom.o diff --git a/drivers/platform/tegra/tegra-epl.c b/drivers/platform/tegra/tegra-epl.c new file mode 100644 index 00000000..c3f9911d --- /dev/null +++ b/drivers/platform/tegra/tegra-epl.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Timeout in milliseconds */ +#define TIMEOUT 1000 + +/* 32bit data Length */ +#define MAX_LEN 4 + +/* Macro indicating total number of Misc Sw generic errors in Misc EC */ +#define NUM_SW_GENERIC_ERR 5U + +/* Error index offset in mission status register */ +#define ERROR_INDEX_OFFSET 24U + +/* Data type for mailbox client and channel details */ +struct epl_hsp_sm { + struct mbox_client client; + struct mbox_chan *chan; +}; + +/* Data type for accessing TOP2 HSP */ +struct epl_hsp { + struct epl_hsp_sm tx; + struct device dev; +}; + +/* Data type to store Misc Sw Generic error configuration */ +struct epl_misc_sw_err_cfg { + void __iomem *err_code_va; + void __iomem *err_assert_va; + const char *dev_configured; +}; + +static int device_file_major_number; +static const char device_name[] = "epdaemon"; + +static struct platform_device *pdev_local; + +static struct epl_hsp *epl_hsp_v; + +static void __iomem *mission_err_status_va; + +static bool isAddrMappOk = true; + +static struct epl_misc_sw_err_cfg miscerr_cfg[NUM_SW_GENERIC_ERR]; + +static void tegra_hsp_tx_empty_notify(struct mbox_client *cl, + void *data, int empty_value) +{ + pr_debug("EPL: TX empty callback came\n"); +} + +static int tegra_hsp_mb_init(struct device *dev) +{ + int err; + + epl_hsp_v = devm_kzalloc(dev, sizeof(*epl_hsp_v), GFP_KERNEL); + if (!epl_hsp_v) + return -ENOMEM; + + epl_hsp_v->tx.client.dev = dev; + epl_hsp_v->tx.client.tx_block = true; + epl_hsp_v->tx.client.tx_tout = TIMEOUT; + epl_hsp_v->tx.client.tx_done = tegra_hsp_tx_empty_notify; + + epl_hsp_v->tx.chan = mbox_request_channel_byname(&epl_hsp_v->tx.client, + "epl-tx"); + if (IS_ERR(epl_hsp_v->tx.chan)) { + err = PTR_ERR(epl_hsp_v->tx.chan); + dev_err(dev, "failed to get tx mailbox: %d\n", err); + return err; + } + + return 0; +} + +static ssize_t device_file_ioctl( + struct file *fp, unsigned int cmd, unsigned long arg) +{ + uint32_t lData[MAX_LEN]; + int ret; + + if (copy_from_user(lData, (uint8_t *)arg, + MAX_LEN * sizeof(uint32_t))) + return -EACCES; + + switch (cmd) { + + case EPL_REPORT_ERROR_CMD: + ret = mbox_send_message(epl_hsp_v->tx.chan, + (void *) lData); + break; + + default: + return -EINVAL; + } + + return ret; +} + +int epl_get_misc_ec_err_status(struct device *dev, uint8_t err_number, bool *status) +{ + int ret = -EINVAL; + + if (dev && status && (err_number < NUM_SW_GENERIC_ERR)) { + uint32_t mission_err_status = 0U; + uint32_t mask = 0U; + const char *dev_str; + + if (miscerr_cfg[err_number].dev_configured == NULL || isAddrMappOk == false) + return -ENODEV; + + dev_str = dev_driver_string(dev); + + if (strcmp(dev_str, miscerr_cfg[err_number].dev_configured) != 0) + return -EACCES; + + mask = (1U << ((ERROR_INDEX_OFFSET + err_number) % 32U)); + mission_err_status = readl(mission_err_status_va); + + if ((mission_err_status & mask) != 0U) + *status = false; + else + *status = true; + + ret = 0; + } + + return ret; +} +EXPORT_SYMBOL_GPL(epl_get_misc_ec_err_status); + +int epl_report_misc_ec_error(struct device *dev, uint8_t err_number, + uint32_t sw_error_code) +{ + int ret = -EINVAL; + bool status = false; + + ret = epl_get_misc_ec_err_status(dev, err_number, &status); + + if (ret != 0) + return ret; + + if (status == false) + return -EAGAIN; + + /* Updating error code */ + writel(sw_error_code, miscerr_cfg[err_number].err_code_va); + + /* triggering SW generic error */ + writel(0x1U, miscerr_cfg[err_number].err_assert_va); + + return 0; +} +EXPORT_SYMBOL_GPL(epl_report_misc_ec_error); + +int epl_report_error(struct epl_error_report_frame error_report) +{ + int ret = -EINVAL; + + if (epl_hsp_v == NULL) + return -ENODEV; + + ret = mbox_send_message(epl_hsp_v->tx.chan, (void *)&error_report); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(epl_report_error); + +static int __maybe_unused epl_client_suspend(struct device *dev) +{ + pr_debug("tegra-epl: suspend called\n"); + return 0; +} + +static int __maybe_unused epl_client_resume(struct device *dev) +{ + pr_debug("tegra-epl: resume called\n"); + return 0; +} +static SIMPLE_DEV_PM_OPS(epl_client_pm, epl_client_suspend, epl_client_resume); + +static const struct of_device_id epl_client_dt_match[] = { + { .compatible = "nvidia,tegra234-epl-client"}, + {} +}; + +MODULE_DEVICE_TABLE(of, epl_client_dt_match); + +/* File operations */ +static const struct file_operations epl_driver_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = device_file_ioctl, +}; + +static int epl_register_device(void) +{ + int result = 0; + struct class *dev_class; + + result = register_chrdev(0, device_name, &epl_driver_fops); + if (result < 0) { + pr_err("%s> register_chrdev code = %i\n", device_name, result); + return result; + } + device_file_major_number = result; + dev_class = class_create(THIS_MODULE, device_name); + if (dev_class == NULL) { + pr_err("%s> Could not create class for device\n", device_name); + goto class_fail; + } + + if ((device_create(dev_class, NULL, + MKDEV(device_file_major_number, 0), + NULL, device_name)) == NULL) { + pr_err("%s> Could not create device node\n", device_name); + goto device_failure; + } + return 0; + +device_failure: + class_destroy(dev_class); +class_fail: + unregister_chrdev(device_file_major_number, device_name); + return -1; +} + +static void epl_unregister_device(void) +{ + if (device_file_major_number != 0) + unregister_chrdev(device_file_major_number, device_name); +} + +static int epl_client_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device *dev = &pdev->dev; + const struct device_node *np = dev->of_node; + int iterator = 0; + char name[32] = "client-misc-sw-generic-err"; + + epl_register_device(); + ret = tegra_hsp_mb_init(dev); + pdev_local = pdev; + + for (iterator = 0; iterator < NUM_SW_GENERIC_ERR; iterator++) { + name[26] = (char)(iterator+48U); + name[27] = '\0'; + if (of_property_read_string(np, name, &miscerr_cfg[iterator].dev_configured) == 0) { + pr_info("Misc Sw Generic Err #%d configured to client %s\n", + iterator, miscerr_cfg[iterator].dev_configured); + + /* Mapping registers to process address space */ + miscerr_cfg[iterator].err_code_va = + devm_platform_ioremap_resource(pdev, (iterator * 2)); + miscerr_cfg[iterator].err_assert_va = + devm_platform_ioremap_resource(pdev, (iterator * 2) + 1); + + if (IS_ERR(miscerr_cfg[iterator].err_code_va) || + IS_ERR(miscerr_cfg[iterator].err_assert_va)) { + isAddrMappOk = false; + ret = -1; + dev_err(&pdev->dev, "error in mapping misc err register for err #%d\n", + iterator); + } + } else { + pr_info("Misc Sw Generic Err %d not configured for any client\n", iterator); + } + } + + mission_err_status_va = devm_platform_ioremap_resource(pdev, (NUM_SW_GENERIC_ERR * 2) + 1); + if (IS_ERR(mission_err_status_va)) { + isAddrMappOk = false; + dev_err(&pdev->dev, "error in mapping mission error status register\n"); + return PTR_ERR(mission_err_status_va); + } + + return ret; +} + +static int epl_client_remove(struct platform_device *pdev) +{ + epl_unregister_device(); + return 0; +} + +static struct platform_driver epl_client = { + .driver = { + .name = "epl_client", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = of_match_ptr(epl_client_dt_match), + .pm = pm_ptr(&epl_client_pm), + }, + .probe = epl_client_probe, + .remove = epl_client_remove, +}; + +module_platform_driver(epl_client); + +MODULE_DESCRIPTION("tegra: Error Propagation Library driver"); +MODULE_AUTHOR("Prashant Shaw "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/tegra-epl.h b/include/linux/tegra-epl.h new file mode 100644 index 00000000..ce19bf2d --- /dev/null +++ b/include/linux/tegra-epl.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2021-2022, NVIDIA CORPORATION, All rights reserved. + */ + +#ifndef _TEGRA_EPL_H_ +#define _TEGRA_EPL_H_ + +#include + +/** + * @brief Error report frame + */ +struct epl_error_report_frame { + /* Error code indicates error reported by corresponding reporter_id */ + uint32_t error_code; + + /* Extra information for SEH to understand error */ + uint32_t error_attribute; + + /* LSB 32-bit TSC counter when error is detected */ + uint32_t timestamp; + + /* Indicates source of error */ + uint16_t reporter_id; +}; + +/** + * @brief API to check if SW error can be reported via Misc EC + * by reading and checking Misc EC error status register value. + * + * @param[in] dev pointer to the device structure for the kernel driver + * from where API is called. + * @param[in] err_number Generic SW error number for which status needs to + * enquired - [0 to 4]. + * @param[out] status out param updated by API as follows: + * true - SW error can be reported + * false - SW error can not be reported because previous error + * is still active. Client needs to retry later. + * + * @returns + * 0 (success) + * -EINVAL (On invalid arguments) + * -ENODEV (On device driver not loaded or Misc EC not configured) + * -EACCESS (On client not allowed to report error via given Misc EC) + * -EAGAIN (On Misc EC busy, client should retry) + */ +int epl_get_misc_ec_err_status(struct device *dev, uint8_t err_number, bool *status); + + +/** + * @brief API to report SW error to FSI using Misc Generic SW error lines connected to + * the Misc error collator. + * + * @param[in] dev pointer to the device structure for the kernel driver + * from where API is called. + * @param[in] err_number Generic SW error number through which error + * needs to be reported. + * @param[in] sw_error_code Client Defined Error Code, which will be + * forwarded to the application on FSI. + * + * @returns + * 0 (success) + * -EINVAL (On invalid arguments) + * -ENODEV (On device driver not loaded or Misc EC not configured) + * -EACCESS (On client not allowed to report error via given Misc EC) + * -EAGAIN (On Misc EC busy, client should retry) + */ +int epl_report_misc_ec_error(struct device *dev, uint8_t err_number, uint32_t sw_error_code); + +/** + * @brief API to report SW error via TOP2 HSP + * + * @param[in] error_report Error frame to be reported + * + * @return + * 0 (Success) + * -ENODEV (On device driver not loaded or not configured) + * -ETIME (On timeout) + */ +int epl_report_error(struct epl_error_report_frame error_report); + +#endif /* _TEGRA_EPL_H_ */ diff --git a/include/uapi/linux/tegra-epl.h b/include/uapi/linux/tegra-epl.h new file mode 100644 index 00000000..178f3c90 --- /dev/null +++ b/include/uapi/linux/tegra-epl.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) + * + * Copyright (c) 2021-2022, NVIDIA CORPORATION, All rights reserved. + */ + +#ifndef _UAPI_TEGRA_EPL_H_ +#define _UAPI_TEGRA_EPL_H_ + +#include + +/* ioctl call macros */ +#define EPL_REPORT_ERROR_CMD _IOWR('q', 1, uint8_t *) + +#endif /* _UAPI_TEGRA_EPL_H_ */