cdx: add device attributes
authorNipun Gupta <nipun.gupta@amd.com>
Mon, 13 Mar 2023 13:26:36 +0000 (18:56 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Mar 2023 10:26:32 +0000 (12:26 +0200)
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 <puneet.gupta@amd.com>
Signed-off-by: Nipun Gupta <nipun.gupta@amd.com>
Signed-off-by: Tarak Reddy <tarak.reddy@amd.com>
Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Tested-by: Nikhil Agarwal <nikhil.agarwal@amd.com>
Link: https://lore.kernel.org/r/20230313132636.31850-8-nipun.gupta@amd.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-bus-cdx
drivers/cdx/cdx.c
drivers/cdx/controller/cdx_controller.c
drivers/cdx/controller/mcdi_functions.c
drivers/cdx/controller/mcdi_functions.h
include/linux/cdx/cdx_bus.h

index 43b4e16..7af477f 100644 (file)
@@ -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
index 6b162bb..67c32cb 100644 (file)
 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
  * @data: This is always passed as NULL, and is not used in this API,
@@ -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);
 
index 0d18269..dc52f95 100644 (file)
@@ -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)
index 012b528..0158f26 100644 (file)
@@ -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;
+}
index 6bf5a4a..7440ace 100644 (file)
@@ -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 */
index d134e01..35ef41d 100644 (file)
 /* 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_ */