From 0e7a054913714c771c78acda6deb25e7876e1367 Mon Sep 17 00:00:00 2001 From: koenz Date: Tue, 8 Nov 2022 16:08:28 +0800 Subject: [PATCH] drivers/misc/mods: Add mods_bpmpipc component - Add mods_bpmpipc component for support BPMP MRQ comunication on "product_security" fused chips - Add ioctl "MODS_ESC_BPMP_UPHY_LANE_EOM_SCAN" for uphy eom read DVS: https://builds4u.nvidia.com/dvs/#/change/3220339765791234.2?showTab=DVS JIRA TM-949 Bug 3846090 Change-Id: I1aebf99b7516d55cd84e01a6ba36801546bbb1ed Signed-off-by: koenz Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2828417 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Bharat Nihalani GVS: Gerrit_Virtual_Submit --- drivers/misc/mods/Makefile | 1 + drivers/misc/mods/mods_bpmpipc.c | 360 ++++++++++++++++++++++++++++++ drivers/misc/mods/mods_internal.h | 10 + drivers/misc/mods/mods_krnl.c | 12 + include/uapi/misc/mods.h | 23 ++ 5 files changed, 406 insertions(+) create mode 100644 drivers/misc/mods/mods_bpmpipc.c diff --git a/drivers/misc/mods/Makefile b/drivers/misc/mods/Makefile index a37c0aa0..4d9132a0 100644 --- a/drivers/misc/mods/Makefile +++ b/drivers/misc/mods/Makefile @@ -16,6 +16,7 @@ mods-y += mods_mem.o mods-$(CONFIG_ACPI) += mods_acpi.o mods-$(CONFIG_TEGRA_NVADSP) += mods_adsp.o mods-$(CONFIG_ARM_FFA_TRANSPORT) += mods_arm_ffa.o +mods-$(CONFIG_TEGRA_IVC) += mods_bpmpipc.o mods-$(CONFIG_COMMON_CLK) += mods_clock.o mods-$(CONFIG_DEBUG_FS) += mods_debugfs.o mods-$(CONFIG_DMA_ENGINE) += mods_dma.o diff --git a/drivers/misc/mods/mods_bpmpipc.c b/drivers/misc/mods/mods_bpmpipc.c new file mode 100644 index 00000000..f1ba81a0 --- /dev/null +++ b/drivers/misc/mods/mods_bpmpipc.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of NVIDIA MODS kernel driver. + * + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA MODS kernel driver is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * NVIDIA MODS kernel driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NVIDIA MODS kernel driver. + * If not, see . + */ + +#include "mods_internal.h" + +#include +#include +#include + +#include +#include + +#define IVC_CHANNEL_SIZE 256 +#define MRQ_MSG_SIZE 128 +#define BPMP_MAIL_DO_ACK (1U << 0U) +#define BPMP_IVC_TIMEOUT 120000 /* really large timeout to support simulation platforms */ + +static DEFINE_MUTEX(mods_bpmpipc_lock); + +static const u32 MODS_CMD_UPHY_LANE_EOM_SCAN = 9; + +struct mods_cmd_uphy_lane_eom_scan_request { + u32 brick; + u32 lane; + u32 pcie_gen5; +}; + +struct mods_cmd_uphy_lane_eom_scan_response { + u32 data; +}; + +struct mods_mrq_uphy_request { + u16 lane; + u16 cmd; + struct mods_cmd_uphy_lane_eom_scan_request lane_eom_scan; +}; + +struct mods_mrq_uphy_response { + struct mods_cmd_uphy_lane_eom_scan_response eom_status; +}; + +struct bpmp_ipc_ch { + bool is_init; + struct tegra_ivc ivc; + void __iomem *db_base; + void __iomem *req_base; + void __iomem *resp_base; + phys_addr_t db_phys_addr; + phys_addr_t req_phys_addr; + phys_addr_t resp_phys_addr; +}; + +static struct bpmp_ipc_ch mods_bpmp_ch = {.is_init = false}; + +static void bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) +{ + struct bpmp_ipc_ch *bpmp_ipc_ch = (struct bpmp_ipc_ch *)data; + + __raw_writel(1, bpmp_ipc_ch->db_base); +} + +static int bpmp_ipc_send(struct mods_client *client, + struct tegra_ivc *ivc, + const void *data, + size_t sz) +{ + void *frame; + + frame = tegra_ivc_write_get_next_frame(ivc); + if (IS_ERR(frame)) { + cl_error("failed to get next tegra-ivc output frame!\n"); + return PTR_ERR(frame); + } + + memcpy_toio(frame, data, sz); + + tegra_ivc_write_advance(ivc); + + return 0; +} + +static int bpmp_ipc_recv(struct mods_client *client, + struct tegra_ivc *ivc, + void *data, + size_t sz, + u32 timeout_ms) +{ + int err; + const void *frame; + ktime_t end; + + end = ktime_add_us(ktime_get(), timeout_ms * 1000); + do { + frame = tegra_ivc_read_get_next_frame(ivc); + if (!IS_ERR(frame)) + break; + } while (ktime_before(ktime_get(), end)); + + if (IS_ERR(frame)) { + frame = tegra_ivc_read_get_next_frame(ivc); + + if (IS_ERR(frame)) { + cl_error("get next tegra-ivc input frame timeout\n"); + return -ETIMEDOUT; + } + } + + memcpy_fromio(data, frame, sz); + + err = tegra_ivc_read_advance(ivc); + if (err < 0) + cl_error("tegra_ivc read failed: %d\n", err); + + return err; +} + +static int bpmp_transfer(struct mods_client *client, + struct tegra_bpmp_message *msg) +{ + int err; + struct tegra_bpmp_mb_data req; + struct tegra_bpmp_mb_data resp; + + req.code = msg->mrq; + req.flags = BPMP_MAIL_DO_ACK; + memcpy(req.data, msg->tx.data, msg->tx.size); + err = bpmp_ipc_send(client, &mods_bpmp_ch.ivc, &req, sizeof(req)); + + if (err == 0) { + err = bpmp_ipc_recv(client, &mods_bpmp_ch.ivc, + &resp, + sizeof(resp), + BPMP_IVC_TIMEOUT); + } + + if (err == 0) { + memcpy(msg->rx.data, resp.data, msg->rx.size); + msg->rx.ret = resp.code; + } + + return err; +} + +static int mrq_uphy_lane_eom_scan(struct mods_client *client, + u32 brick, + u32 lane, + u32 pcie_gen5, + u32 *data) +{ + int err; + struct mods_mrq_uphy_request req = { + .cmd = cpu_to_le32(MODS_CMD_UPHY_LANE_EOM_SCAN) + }; + struct mods_mrq_uphy_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_UPHY, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + + req.lane_eom_scan.brick = brick; + req.lane_eom_scan.lane = lane; + req.lane_eom_scan.pcie_gen5 = pcie_gen5; + + err = bpmp_transfer(client, &msg); + + if (err < 0) { + return err; + } else if (msg.rx.ret < 0) { + err = -EINVAL; + return err; + } + + *data = resp.eom_status.data; + return err; +} + +static int bpmp_ioremap(struct mods_client *client, + struct bpmp_ipc_ch *bpmp_ipc_ch, + u64 db_phys_addr, + u64 req_phys_addr, + u64 resp_phys_addr) +{ + bpmp_ipc_ch->db_phys_addr = db_phys_addr; + bpmp_ipc_ch->req_phys_addr = req_phys_addr; + bpmp_ipc_ch->resp_phys_addr = resp_phys_addr; + + bpmp_ipc_ch->db_base = ioremap(bpmp_ipc_ch->db_phys_addr, 64); + if (!bpmp_ipc_ch->db_base) { + cl_error("failed to remap aperture: 0x%llx\n", + (unsigned long long)bpmp_ipc_ch->db_phys_addr); + return -ENOMEM; + } + bpmp_ipc_ch->req_base = ioremap(bpmp_ipc_ch->req_phys_addr, IVC_CHANNEL_SIZE); + if (!bpmp_ipc_ch->req_base) { + iounmap(bpmp_ipc_ch->db_base); + cl_error("failed to remap aperture: 0x%llx\n", + (unsigned long long)bpmp_ipc_ch->req_phys_addr); + return -ENOMEM; + } + bpmp_ipc_ch->resp_base = ioremap(bpmp_ipc_ch->resp_phys_addr, IVC_CHANNEL_SIZE); + if (!bpmp_ipc_ch->resp_base) { + iounmap(bpmp_ipc_ch->db_base); + iounmap(bpmp_ipc_ch->req_base); + cl_error("failed to remap aperture: 0x%llx\n", + (unsigned long long)bpmp_ipc_ch->resp_phys_addr); + return -ENOMEM; + } + + return OK; +} + +static void bpmp_iounmap(struct bpmp_ipc_ch *bpmp_ipc_ch) +{ + iounmap(bpmp_ipc_ch->db_base); + iounmap(bpmp_ipc_ch->req_base); + iounmap(bpmp_ipc_ch->resp_base); + + bpmp_ipc_ch->db_phys_addr = 0; + bpmp_ipc_ch->req_phys_addr = 0; + bpmp_ipc_ch->resp_phys_addr = 0; +} + +static int bpmp_ipc_channel_init(struct mods_client *client, + struct bpmp_ipc_ch *bpmp_ipc_ch) +{ + int err; + ktime_t end; + + err = tegra_ivc_init(&bpmp_ipc_ch->ivc, NULL, + bpmp_ipc_ch->resp_base, 0, + bpmp_ipc_ch->req_base, 0, + 1, MRQ_MSG_SIZE, + bpmp_ivc_notify, bpmp_ipc_ch); + + + if (err != 0) { + cl_error("tegra-ivc init failed: %d\n", err); + return err; + } + + tegra_ivc_reset(&bpmp_ipc_ch->ivc); + + end = ktime_add_us(ktime_get(), 2000 * 1000); + + while (tegra_ivc_notified(&bpmp_ipc_ch->ivc) != 0) { + usleep_range(100, 200); + if (ktime_after(ktime_get(), end)) { + cl_error("initialize IVC connection timeout\n"); + err = -ETIMEDOUT; + break; + } + } + + bpmp_ipc_ch->is_init = true; + + return err; +} + +static void bpmp_ipc_channel_uninit(struct bpmp_ipc_ch *bpmp_ipc_ch) +{ + tegra_ivc_cleanup(&bpmp_ipc_ch->ivc); +} + +int mods_bpmpipc_init(struct mods_client *client, + u64 db_phys_addr, + u64 req_phys_addr, + u64 resp_phys_addr) +{ + int err = OK; + + if (mods_bpmp_ch.is_init) { + if (mods_bpmp_ch.db_phys_addr == db_phys_addr && + mods_bpmp_ch.req_phys_addr == req_phys_addr && + mods_bpmp_ch.resp_phys_addr == resp_phys_addr) + return OK; + mods_bpmpipc_cleanup(); + } + + err = bpmp_ioremap(client, + &mods_bpmp_ch, + db_phys_addr, + req_phys_addr, + resp_phys_addr); + if (err != OK) + return err; + + err = bpmp_ipc_channel_init(client, &mods_bpmp_ch); + if (err != OK) { + bpmp_iounmap(&mods_bpmp_ch); + return err; + } + + mods_bpmp_ch.is_init = true; + mods_debug_printk(DEBUG_TEGRADMA, "bpmp ipc init done\n"); + + return err; +} + +void mods_bpmpipc_cleanup(void) +{ + if (!mods_bpmp_ch.is_init) + return; + + bpmp_ipc_channel_uninit(&mods_bpmp_ch); + bpmp_iounmap(&mods_bpmp_ch); + mods_bpmp_ch.is_init = false; +} + +int esc_mods_bpmp_uphy_lane_eom_scan(struct mods_client *client, + struct MODS_BPMP_UPHY_LANE_EOM_SCAN_PARAMS *p) +{ + int err = OK; + + mutex_lock(&mods_bpmpipc_lock); + + err = mods_bpmpipc_init(client, + p->db_phys_addr, + p->req_phys_addr, + p->resp_phys_addr); + if (err != OK) + goto error; + + err = mrq_uphy_lane_eom_scan(client, + p->brick, + p->lane, + p->pcie_gen5, + &p->data); + + if (err != OK) + cl_error("mrq uphy lane eom scan failed with brick(%u), lane(%u), pcie_gen5(%u)\n", + p->brick, p->lane, p->pcie_gen5); + +error: + mutex_unlock(&mods_bpmpipc_lock); + return err; +} diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h index 76290b98..a93793d2 100644 --- a/drivers/misc/mods/mods_internal.h +++ b/drivers/misc/mods/mods_internal.h @@ -755,4 +755,14 @@ int mods_ffa_abi_register(void); void mods_ffa_abi_unregister(void); #endif +#if defined(CONFIG_TEGRA_IVC) +int esc_mods_bpmp_uphy_lane_eom_scan(struct mods_client *client, + struct MODS_BPMP_UPHY_LANE_EOM_SCAN_PARAMS *p); +int mods_bpmpipc_init(struct mods_client *client, + u64 db_phys_addr, + u64 req_phys_addr, + u64 resp_phys_addr); +void mods_bpmpipc_cleanup(void); +#endif + #endif /* _MODS_INTERNAL_H_ */ diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c index 0ca658f8..526cc8fd 100644 --- a/drivers/misc/mods/mods_krnl.c +++ b/drivers/misc/mods/mods_krnl.c @@ -1035,6 +1035,10 @@ static int mods_krnl_close(struct inode *ip, struct file *fp) final_err = err; #endif +#if defined(CONFIG_TEGRA_IVC) + mods_bpmpipc_cleanup(); +#endif + mods_disable_all_devices(client); { @@ -2714,6 +2718,14 @@ static long mods_krnl_ioctl(struct file *fp, esc_mods_get_driver_stats, MODS_GET_DRIVER_STATS); break; +#ifdef CONFIG_TEGRA_IVC + case MODS_ESC_BPMP_UPHY_LANE_EOM_SCAN: + MODS_IOCTL(MODS_ESC_BPMP_UPHY_LANE_EOM_SCAN, + esc_mods_bpmp_uphy_lane_eom_scan, + MODS_BPMP_UPHY_LANE_EOM_SCAN_PARAMS); + break; +#endif + default: cl_error( "unrecognized ioctl 0x%x, dir %u, type 0x%x, nr %u, size 0x%x\n", diff --git a/include/uapi/misc/mods.h b/include/uapi/misc/mods.h index dc140810..837ba93e 100644 --- a/include/uapi/misc/mods.h +++ b/include/uapi/misc/mods.h @@ -1927,6 +1927,26 @@ struct MODS_PROXIMITY_TO_NUMA_NODE { __s32 numa_node; }; +/* Used by MODS_ESC_BPMP_UPHY_LANE_EOM_SCAN + * + * Get EOM data of UPHY lane from bpmp + */ +struct MODS_BPMP_UPHY_LANE_EOM_SCAN_PARAMS { + /* IN */ + /* the address of bpmp db/req/resp is various for different + * chip/socket, the value of them will be passed from user. + */ + __u64 db_phys_addr; + __u64 req_phys_addr; + __u64 resp_phys_addr; + __u32 brick; + __u32 lane; + __u32 pcie_gen5; + + /* OUT */ + __u32 data; +}; + #pragma pack(pop) #define MODS_IOC_MAGIC 'x' @@ -2133,5 +2153,8 @@ struct MODS_PROXIMITY_TO_NUMA_NODE { #define MODS_ESC_PROXIMITY_TO_NUMA_NODE MODSIO(WR, 143, MODS_PROXIMITY_TO_NUMA_NODE) #define MODS_ESC_MODS_SEND_IPI MODSIO(W, 144, MODS_SEND_IPI) #define MODS_ESC_FFA_CMD MODSIO(WR, 145, MODS_FFA_PARAMS) +#define MODS_ESC_BPMP_UPHY_LANE_EOM_SCAN MODSIO(WR, 146, \ + MODS_BPMP_UPHY_LANE_EOM_SCAN_PARAMS) + #endif /* _UAPI_MODS_H_ */