From 8a7923df35d3a0fa44985148cee5b7fde4a370ba Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:34 +0530 Subject: cdx: add cdx controller CDX controller uses MCDI interface as a protocol to communicate with the RPU firmware and registers the detected CDX devices on the CDX bus. It also uses RPMsg as the communication channel with the Firmware. Signed-off-by: Nipun Gupta Signed-off-by: Puneet Gupta Signed-off-by: Abhijit Gangurde Signed-off-by: Nikhil Agarwal Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-6-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/controller/cdx_controller.c | 188 ++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 drivers/cdx/controller/cdx_controller.c (limited to 'drivers/cdx/controller/cdx_controller.c') diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c new file mode 100644 index 000000000000..fbebc8cdcbf8 --- /dev/null +++ b/drivers/cdx/controller/cdx_controller.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CDX host controller driver for AMD versal-net platform. + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "../cdx.h" +#include "mcdi_functions.h" +#include "mcdi.h" + +static unsigned int cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd) +{ + return MCDI_RPC_TIMEOUT; +} + +static void cdx_mcdi_request(struct cdx_mcdi *cdx, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len) +{ + /* + * This will get updated by rpmsg APIs, with RPMSG introduction + * in CDX controller as a transport layer. + */ +} + +static const struct cdx_mcdi_ops mcdi_ops = { + .mcdi_rpc_timeout = cdx_mcdi_rpc_timeout, + .mcdi_request = cdx_mcdi_request, +}; + +static int cdx_scan_devices(struct cdx_controller *cdx) +{ + struct cdx_mcdi *cdx_mcdi = cdx->priv; + u8 bus_num, dev_num, num_cdx_bus; + int ret; + + /* MCDI FW Read: Fetch the number of CDX buses on this controller */ + ret = cdx_mcdi_get_num_buses(cdx_mcdi); + if (ret < 0) { + dev_err(cdx->dev, + "Get number of CDX buses failed: %d\n", ret); + return ret; + } + num_cdx_bus = (u8)ret; + + for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) { + u8 num_cdx_dev; + + /* MCDI FW Read: Fetch the number of devices present */ + ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num); + if (ret < 0) { + dev_err(cdx->dev, + "Get devices on CDX bus %d failed: %d\n", bus_num, ret); + continue; + } + num_cdx_dev = (u8)ret; + + for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) { + struct cdx_dev_params dev_params; + + /* MCDI FW: Get the device config */ + ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num, + dev_num, &dev_params); + if (ret) { + dev_err(cdx->dev, + "CDX device config get failed for %d(bus):%d(dev), %d\n", + bus_num, dev_num, ret); + continue; + } + dev_params.cdx = cdx; + + /* Add the device to the cdx bus */ + ret = cdx_device_add(&dev_params); + if (ret) { + dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n", + dev_num, ret); + continue; + } + + dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n", + dev_num, bus_num); + } + } + + return 0; +} + +static struct cdx_ops cdx_ops = { + .scan = cdx_scan_devices, +}; + +static int xlnx_cdx_probe(struct platform_device *pdev) +{ + struct cdx_controller *cdx; + struct cdx_mcdi *cdx_mcdi; + int ret; + + cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL); + if (!cdx_mcdi) + return -ENOMEM; + + /* Store the MCDI ops */ + cdx_mcdi->mcdi_ops = &mcdi_ops; + /* MCDI FW: Initialize the FW path */ + ret = cdx_mcdi_init(cdx_mcdi); + if (ret) { + dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n"); + goto mcdi_init_fail; + } + + cdx = kzalloc(sizeof(*cdx), GFP_KERNEL); + if (!cdx) { + ret = -ENOMEM; + goto cdx_alloc_fail; + } + platform_set_drvdata(pdev, cdx); + + cdx->dev = &pdev->dev; + cdx->priv = cdx_mcdi; + cdx->ops = &cdx_ops; + + return 0; + +cdx_alloc_fail: + cdx_mcdi_finish(cdx_mcdi); +mcdi_init_fail: + kfree(cdx_mcdi); + + return ret; +} + +static int xlnx_cdx_remove(struct platform_device *pdev) +{ + struct cdx_controller *cdx = platform_get_drvdata(pdev); + struct cdx_mcdi *cdx_mcdi = cdx->priv; + + kfree(cdx); + + cdx_mcdi_finish(cdx_mcdi); + kfree(cdx_mcdi); + + return 0; +} + +static const struct of_device_id cdx_match_table[] = { + {.compatible = "xlnx,versal-net-cdx",}, + { }, +}; + +MODULE_DEVICE_TABLE(of, cdx_match_table); + +static struct platform_driver cdx_pdriver = { + .driver = { + .name = "cdx-controller", + .pm = NULL, + .of_match_table = cdx_match_table, + }, + .probe = xlnx_cdx_probe, + .remove = xlnx_cdx_remove, +}; + +static int __init cdx_controller_init(void) +{ + int ret; + + ret = platform_driver_register(&cdx_pdriver); + if (ret) + pr_err("platform_driver_register() failed: %d\n", ret); + + return ret; +} + +static void __exit cdx_controller_exit(void) +{ + platform_driver_unregister(&cdx_pdriver); +} + +module_init(cdx_controller_init); +module_exit(cdx_controller_exit); + +MODULE_AUTHOR("AMD Inc."); +MODULE_DESCRIPTION("CDX controller for AMD devices"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2a226927d9b836ddec674aebbcea311727d04240 Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:35 +0530 Subject: cdx: add rpmsg communication channel for CDX RPMsg is used as a transport communication channel. This change introduces RPMsg driver and integrates it with the CDX controller. Signed-off-by: Abhijit Gangurde Signed-off-by: Nipun Gupta Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-7-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/controller/Kconfig | 2 + drivers/cdx/controller/Makefile | 2 +- drivers/cdx/controller/cdx_controller.c | 32 ++++- drivers/cdx/controller/cdx_controller.h | 30 +++++ drivers/cdx/controller/cdx_rpmsg.c | 202 ++++++++++++++++++++++++++++++++ drivers/cdx/controller/mcdi.h | 10 ++ 6 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 drivers/cdx/controller/cdx_controller.h create mode 100644 drivers/cdx/controller/cdx_rpmsg.c (limited to 'drivers/cdx/controller/cdx_controller.c') diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig index 17f9c6be2fe1..c3e3b9ff8dfe 100644 --- a/drivers/cdx/controller/Kconfig +++ b/drivers/cdx/controller/Kconfig @@ -9,6 +9,8 @@ if CDX_BUS config CDX_CONTROLLER tristate "CDX bus controller" + select REMOTEPROC + select RPMSG help CDX controller drives the CDX bus. It interacts with firmware to get the hardware devices and registers with diff --git a/drivers/cdx/controller/Makefile b/drivers/cdx/controller/Makefile index f7437c882cc9..f071be411d96 100644 --- a/drivers/cdx/controller/Makefile +++ b/drivers/cdx/controller/Makefile @@ -6,4 +6,4 @@ # obj-$(CONFIG_CDX_CONTROLLER) += cdx-controller.o -cdx-controller-objs := cdx_controller.o mcdi.o mcdi_functions.o +cdx-controller-objs := cdx_controller.o cdx_rpmsg.o mcdi.o mcdi_functions.o diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index fbebc8cdcbf8..0d1826980935 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -9,6 +9,7 @@ #include #include +#include "cdx_controller.h" #include "../cdx.h" #include "mcdi_functions.h" #include "mcdi.h" @@ -22,10 +23,8 @@ static void cdx_mcdi_request(struct cdx_mcdi *cdx, const struct cdx_dword *hdr, size_t hdr_len, const struct cdx_dword *sdu, size_t sdu_len) { - /* - * This will get updated by rpmsg APIs, with RPMSG introduction - * in CDX controller as a transport layer. - */ + if (cdx_rpmsg_send(cdx, hdr, hdr_len, sdu, sdu_len)) + dev_err(&cdx->rpdev->dev, "Failed to send rpmsg data\n"); } static const struct cdx_mcdi_ops mcdi_ops = { @@ -33,6 +32,19 @@ static const struct cdx_mcdi_ops mcdi_ops = { .mcdi_request = cdx_mcdi_request, }; +void cdx_rpmsg_post_probe(struct cdx_controller *cdx) +{ + /* Register CDX controller with CDX bus driver */ + if (cdx_register_controller(cdx)) + dev_err(cdx->dev, "Failed to register CDX controller\n"); +} + +void cdx_rpmsg_pre_remove(struct cdx_controller *cdx) +{ + cdx_unregister_controller(cdx); + cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT); +} + static int cdx_scan_devices(struct cdx_controller *cdx) { struct cdx_mcdi *cdx_mcdi = cdx->priv; @@ -124,8 +136,18 @@ static int xlnx_cdx_probe(struct platform_device *pdev) cdx->priv = cdx_mcdi; cdx->ops = &cdx_ops; + ret = cdx_setup_rpmsg(pdev); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n"); + goto cdx_rpmsg_fail; + } + + dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n"); return 0; +cdx_rpmsg_fail: + kfree(cdx); cdx_alloc_fail: cdx_mcdi_finish(cdx_mcdi); mcdi_init_fail: @@ -139,6 +161,8 @@ static int xlnx_cdx_remove(struct platform_device *pdev) struct cdx_controller *cdx = platform_get_drvdata(pdev); struct cdx_mcdi *cdx_mcdi = cdx->priv; + cdx_destroy_rpmsg(pdev); + kfree(cdx); cdx_mcdi_finish(cdx_mcdi); diff --git a/drivers/cdx/controller/cdx_controller.h b/drivers/cdx/controller/cdx_controller.h new file mode 100644 index 000000000000..43b7c742df87 --- /dev/null +++ b/drivers/cdx/controller/cdx_controller.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Header file for the CDX Controller + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef _CDX_CONTROLLER_H_ +#define _CDX_CONTROLLER_H_ + +#include +#include "mcdi_functions.h" + +void cdx_rpmsg_post_probe(struct cdx_controller *cdx); + +void cdx_rpmsg_pre_remove(struct cdx_controller *cdx); + +int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len); + +void cdx_rpmsg_read_resp(struct cdx_mcdi *cdx_mcdi, + struct cdx_dword *outbuf, size_t offset, + size_t outlen); + +int cdx_setup_rpmsg(struct platform_device *pdev); + +void cdx_destroy_rpmsg(struct platform_device *pdev); + +#endif /* _CDX_CONT_PRIV_H_ */ diff --git a/drivers/cdx/controller/cdx_rpmsg.c b/drivers/cdx/controller/cdx_rpmsg.c new file mode 100644 index 000000000000..f37e639d6ce3 --- /dev/null +++ b/drivers/cdx/controller/cdx_rpmsg.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Platform driver for CDX bus. + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include + +#include "../cdx.h" +#include "cdx_controller.h" +#include "mcdi_functions.h" +#include "mcdi.h" + +static struct rpmsg_device_id cdx_rpmsg_id_table[] = { + { .name = "mcdi_ipc" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, cdx_rpmsg_id_table); + +int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len) +{ + unsigned char *send_buf; + int ret; + + send_buf = kzalloc(hdr_len + sdu_len, GFP_KERNEL); + if (!send_buf) + return -ENOMEM; + + memcpy(send_buf, hdr, hdr_len); + memcpy(send_buf + hdr_len, sdu, sdu_len); + + ret = rpmsg_send(cdx_mcdi->ept, send_buf, hdr_len + sdu_len); + kfree(send_buf); + + return ret; +} + +static int cdx_attach_to_rproc(struct platform_device *pdev) +{ + struct device_node *r5_core_node; + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + struct device *dev; + struct rproc *rp; + int ret; + + dev = &pdev->dev; + cdx_c = platform_get_drvdata(pdev); + cdx_mcdi = cdx_c->priv; + + r5_core_node = of_parse_phandle(dev->of_node, "xlnx,rproc", 0); + if (!r5_core_node) { + dev_err(&pdev->dev, "xlnx,rproc: invalid phandle\n"); + return -EINVAL; + } + + rp = rproc_get_by_phandle(r5_core_node->phandle); + if (!rp) { + ret = -EPROBE_DEFER; + goto pdev_err; + } + + /* Attach to remote processor */ + ret = rproc_boot(rp); + if (ret) { + dev_err(&pdev->dev, "Failed to attach to remote processor\n"); + rproc_put(rp); + goto pdev_err; + } + + cdx_mcdi->r5_rproc = rp; +pdev_err: + of_node_put(r5_core_node); + return ret; +} + +static void cdx_detach_to_r5(struct platform_device *pdev) +{ + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + + cdx_c = platform_get_drvdata(pdev); + cdx_mcdi = cdx_c->priv; + + rproc_detach(cdx_mcdi->r5_rproc); + rproc_put(cdx_mcdi->r5_rproc); +} + +static int cdx_rpmsg_cb(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 src) +{ + struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev); + struct cdx_mcdi *cdx_mcdi = cdx_c->priv; + + if (len > MCDI_BUF_LEN) + return -EINVAL; + + cdx_mcdi_process_cmd(cdx_mcdi, (struct cdx_dword *)data, len); + + return 0; +} + +static void cdx_rpmsg_post_probe_work(struct work_struct *work) +{ + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + + cdx_mcdi = container_of(work, struct cdx_mcdi, work); + cdx_c = dev_get_drvdata(&cdx_mcdi->rpdev->dev); + cdx_rpmsg_post_probe(cdx_c); +} + +static int cdx_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_channel_info chinfo = {0}; + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + + cdx_c = (struct cdx_controller *)cdx_rpmsg_id_table[0].driver_data; + cdx_mcdi = cdx_c->priv; + + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = rpdev->dst; + strscpy(chinfo.name, cdx_rpmsg_id_table[0].name, + strlen(cdx_rpmsg_id_table[0].name)); + + cdx_mcdi->ept = rpmsg_create_ept(rpdev, cdx_rpmsg_cb, NULL, chinfo); + if (!cdx_mcdi->ept) { + dev_err_probe(&rpdev->dev, -ENXIO, + "Failed to create ept for channel %s\n", + chinfo.name); + return -EINVAL; + } + + cdx_mcdi->rpdev = rpdev; + dev_set_drvdata(&rpdev->dev, cdx_c); + + schedule_work(&cdx_mcdi->work); + return 0; +} + +static void cdx_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev); + struct cdx_mcdi *cdx_mcdi = cdx_c->priv; + + flush_work(&cdx_mcdi->work); + cdx_rpmsg_pre_remove(cdx_c); + + rpmsg_destroy_ept(cdx_mcdi->ept); + dev_set_drvdata(&rpdev->dev, NULL); +} + +static struct rpmsg_driver cdx_rpmsg_driver = { + .drv.name = KBUILD_MODNAME, + .id_table = cdx_rpmsg_id_table, + .probe = cdx_rpmsg_probe, + .remove = cdx_rpmsg_remove, + .callback = cdx_rpmsg_cb, +}; + +int cdx_setup_rpmsg(struct platform_device *pdev) +{ + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + int ret; + + /* Attach to remote processor */ + ret = cdx_attach_to_rproc(pdev); + if (ret) + return ret; + + cdx_c = platform_get_drvdata(pdev); + cdx_mcdi = cdx_c->priv; + + /* Register RPMsg driver */ + cdx_rpmsg_id_table[0].driver_data = (kernel_ulong_t)cdx_c; + + INIT_WORK(&cdx_mcdi->work, cdx_rpmsg_post_probe_work); + ret = register_rpmsg_driver(&cdx_rpmsg_driver); + if (ret) { + dev_err(&pdev->dev, + "Failed to register cdx RPMsg driver: %d\n", ret); + cdx_detach_to_r5(pdev); + } + + return ret; +} + +void cdx_destroy_rpmsg(struct platform_device *pdev) +{ + unregister_rpmsg_driver(&cdx_rpmsg_driver); + + cdx_detach_to_r5(pdev); +} diff --git a/drivers/cdx/controller/mcdi.h b/drivers/cdx/controller/mcdi.h index 63933ede33ed..0bfbeab04e43 100644 --- a/drivers/cdx/controller/mcdi.h +++ b/drivers/cdx/controller/mcdi.h @@ -9,6 +9,7 @@ #include #include +#include #include "bitfield.h" #include "mc_cdx_pcol.h" @@ -62,11 +63,20 @@ enum cdx_mcdi_cmd_state { * with CDX controller. * @mcdi: MCDI interface * @mcdi_ops: MCDI operations + * @r5_rproc : R5 Remoteproc device handle + * @rpdev: RPMsg device + * @ept: RPMsg endpoint + * @work: Post probe work */ struct cdx_mcdi { /* MCDI interface */ struct cdx_mcdi_data *mcdi; const struct cdx_mcdi_ops *mcdi_ops; + + struct rproc *r5_rproc; + struct rpmsg_device *rpdev; + struct rpmsg_endpoint *ept; + struct work_struct work; }; struct cdx_mcdi_ops { -- cgit v1.2.3 From 48a6c7bced2a781c911497f073347bec5ab4d7a5 Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:36 +0530 Subject: cdx: add device attributes Create sysfs entry for CDX devices. Sysfs entries provided in each of the CDX device detected by the CDX controller - vendor id - device id - remove - reset of the device. - driver override Signed-off-by: Puneet Gupta Signed-off-by: Nipun Gupta Signed-off-by: Tarak Reddy Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-8-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-cdx | 44 +++++++++++ drivers/cdx/cdx.c | 127 ++++++++++++++++++++++++++++++++ drivers/cdx/controller/cdx_controller.c | 18 +++++ drivers/cdx/controller/mcdi_functions.c | 14 ++++ drivers/cdx/controller/mcdi_functions.h | 11 +++ include/linux/cdx/cdx_bus.h | 27 +++++++ 6 files changed, 241 insertions(+) (limited to 'drivers/cdx/controller/cdx_controller.c') diff --git a/Documentation/ABI/testing/sysfs-bus-cdx b/Documentation/ABI/testing/sysfs-bus-cdx index 43b4e161f226..7af477f49998 100644 --- a/Documentation/ABI/testing/sysfs-bus-cdx +++ b/Documentation/ABI/testing/sysfs-bus-cdx @@ -10,3 +10,47 @@ Description: For example:: # echo 1 > /sys/bus/cdx/rescan + +What: /sys/bus/cdx/devices/.../vendor +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Vendor ID for this CDX device, in hexadecimal. Vendor ID is + 16 bit identifier which is specific to the device manufacturer. + Combination of Vendor ID and Device ID identifies a device. + +What: /sys/bus/cdx/devices/.../device +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Device ID for this CDX device, in hexadecimal. Device ID is + 16 bit identifier to identify a device type within the range + of a device manufacturer. + Combination of Vendor ID and Device ID identifies a device. + +What: /sys/bus/cdx/devices/.../reset +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Writing y/1/on to this file resets the CDX device. + On resetting the device, the corresponding driver is notified + twice, once before the device is being reset, and again after + the reset has been complete. + + For example:: + + # echo 1 > /sys/bus/cdx/.../reset + +What: /sys/bus/cdx/devices/.../remove +Date: March 2023 +Contact: tarak.reddy@amd.com +Description: + Writing y/1/on to this file removes the corresponding + device from the CDX bus. If the device is to be reconfigured + reconfigured in the Hardware, the device can be removed, so + that the device driver does not access the device while it is + being reconfigured. + + For example:: + + # echo 1 > /sys/bus/cdx/devices/.../remove diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 6b162bbb9b25..67c32cb2c006 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -71,6 +71,39 @@ /* CDX controllers registered with the CDX bus */ static DEFINE_XARRAY_ALLOC(cdx_controllers); +/** + * cdx_dev_reset - Reset a CDX device + * @dev: CDX device + * + * Return: -errno on failure, 0 on success. + */ +int cdx_dev_reset(struct device *dev) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + struct cdx_controller *cdx = cdx_dev->cdx; + struct cdx_device_config dev_config = {0}; + struct cdx_driver *cdx_drv; + int ret; + + cdx_drv = to_cdx_driver(dev->driver); + /* Notify driver that device is being reset */ + if (cdx_drv && cdx_drv->reset_prepare) + cdx_drv->reset_prepare(cdx_dev); + + dev_config.type = CDX_DEV_RESET_CONF; + ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num, + cdx_dev->dev_num, &dev_config); + if (ret) + dev_err(dev, "cdx device reset failed\n"); + + /* Notify driver that device reset is complete */ + if (cdx_drv && cdx_drv->reset_done) + cdx_drv->reset_done(cdx_dev); + + return ret; +} +EXPORT_SYMBOL_GPL(cdx_dev_reset); + /** * cdx_unregister_device - Unregister a CDX device * @dev: CDX device @@ -237,6 +270,99 @@ static int cdx_dma_configure(struct device *dev) return 0; } +/* show configuration fields */ +#define cdx_config_attr(field, format_string) \ +static ssize_t \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct cdx_device *cdx_dev = to_cdx_device(dev); \ + return sysfs_emit(buf, format_string, cdx_dev->field); \ +} \ +static DEVICE_ATTR_RO(field) + +cdx_config_attr(vendor, "0x%04x\n"); +cdx_config_attr(device, "0x%04x\n"); + +static ssize_t remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool val; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + if (!val) + return -EINVAL; + + if (device_remove_file_self(dev, attr)) { + int ret; + + ret = cdx_unregister_device(dev, NULL); + if (ret) + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(remove); + +static ssize_t reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + bool val; + int ret; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + if (!val) + return -EINVAL; + + ret = cdx_dev_reset(dev); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + int ret; + + if (WARN_ON(dev->bus != &cdx_bus_type)) + return -EINVAL; + + ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count); + if (ret) + return ret; + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + + return sysfs_emit(buf, "%s\n", cdx_dev->driver_override); +} +static DEVICE_ATTR_RW(driver_override); + +static struct attribute *cdx_dev_attrs[] = { + &dev_attr_remove.attr, + &dev_attr_reset.attr, + &dev_attr_vendor.attr, + &dev_attr_device.attr, + &dev_attr_driver_override.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cdx_dev); + static ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count) { @@ -280,6 +406,7 @@ struct bus_type cdx_bus_type = { .shutdown = cdx_shutdown, .dma_configure = cdx_dma_configure, .bus_groups = cdx_bus_groups, + .dev_groups = cdx_dev_groups, }; EXPORT_SYMBOL_GPL(cdx_bus_type); diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index 0d1826980935..dc52f95f8978 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -45,6 +45,23 @@ void cdx_rpmsg_pre_remove(struct cdx_controller *cdx) cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT); } +static int cdx_configure_device(struct cdx_controller *cdx, + u8 bus_num, u8 dev_num, + struct cdx_device_config *dev_config) +{ + int ret = 0; + + switch (dev_config->type) { + case CDX_DEV_RESET_CONF: + ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num); + break; + default: + ret = -EINVAL; + } + + return ret; +} + static int cdx_scan_devices(struct cdx_controller *cdx) { struct cdx_mcdi *cdx_mcdi = cdx->priv; @@ -104,6 +121,7 @@ static int cdx_scan_devices(struct cdx_controller *cdx) static struct cdx_ops cdx_ops = { .scan = cdx_scan_devices, + .dev_configure = cdx_configure_device, }; static int xlnx_cdx_probe(struct platform_device *pdev) diff --git a/drivers/cdx/controller/mcdi_functions.c b/drivers/cdx/controller/mcdi_functions.c index 012b52881dd5..0158f26533dd 100644 --- a/drivers/cdx/controller/mcdi_functions.c +++ b/drivers/cdx/controller/mcdi_functions.c @@ -123,3 +123,17 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, return 0; } + +int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN); + int ret; + + MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_BUS, bus_num); + MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_DEVICE, dev_num); + + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_RESET, inbuf, sizeof(inbuf), + NULL, 0, NULL); + + return ret; +} diff --git a/drivers/cdx/controller/mcdi_functions.h b/drivers/cdx/controller/mcdi_functions.h index 6bf5a4a0778f..7440ace5539a 100644 --- a/drivers/cdx/controller/mcdi_functions.h +++ b/drivers/cdx/controller/mcdi_functions.h @@ -47,4 +47,15 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, struct cdx_dev_params *dev_params); +/** + * cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num + * @cdx: pointer to MCDI interface. + * @bus_num: Bus number. + * @dev_num: Device number. + * + * Return: 0 on success, <0 on failure + */ +int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, + u8 bus_num, u8 dev_num); + #endif /* CDX_MCDI_FUNCTIONS_H */ diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h index d134e0104724..35ef41d8a61a 100644 --- a/include/linux/cdx/cdx_bus.h +++ b/include/linux/cdx/cdx_bus.h @@ -21,8 +21,20 @@ /* Forward declaration for CDX controller */ struct cdx_controller; +enum { + CDX_DEV_RESET_CONF, +}; + +struct cdx_device_config { + u8 type; +}; + typedef int (*cdx_scan_cb)(struct cdx_controller *cdx); +typedef int (*cdx_dev_configure_cb)(struct cdx_controller *cdx, + u8 bus_num, u8 dev_num, + struct cdx_device_config *dev_config); + /** * CDX_DEVICE_DRIVER_OVERRIDE - macro used to describe a CDX device with * override_only flags. @@ -39,9 +51,12 @@ typedef int (*cdx_scan_cb)(struct cdx_controller *cdx); /** * struct cdx_ops - Callbacks supported by CDX controller. * @scan: scan the devices on the controller + * @dev_configure: configuration like reset, master_enable, + * msi_config etc for a CDX device */ struct cdx_ops { cdx_scan_cb scan; + cdx_dev_configure_cb dev_configure; }; /** @@ -101,6 +116,8 @@ struct cdx_device { * @probe: Function called when a device is added * @remove: Function called when a device is removed * @shutdown: Function called at shutdown time to quiesce the device + * @reset_prepare: Function called before is reset to notify driver + * @reset_done: Function called after reset is complete to notify driver * @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA. * For most device drivers, no need to care about this flag * as long as all DMAs are handled through the kernel DMA API. @@ -115,6 +132,8 @@ struct cdx_driver { int (*probe)(struct cdx_device *dev); int (*remove)(struct cdx_device *dev); void (*shutdown)(struct cdx_device *dev); + void (*reset_prepare)(struct cdx_device *dev); + void (*reset_done)(struct cdx_device *dev); bool driver_managed_dma; }; @@ -144,4 +163,12 @@ void cdx_driver_unregister(struct cdx_driver *cdx_driver); extern struct bus_type cdx_bus_type; +/** + * cdx_dev_reset - Reset CDX device + * @dev: device pointer + * + * Return: 0 for success, -errno on failure + */ +int cdx_dev_reset(struct device *dev); + #endif /* _CDX_BUS_H_ */ -- cgit v1.2.3