[RFC PATCH v4 5/8] bus/cdx: add bus and device attributes

From: Nipun Gupta
Date: Fri Oct 14 2022 - 00:42:32 EST


This change adds te support for rescanning and reset
of the CDX buses, as well as option to reset any device
on the bus. It also enables sysfs entry for vendor id
and device id.

Sysfs entries are provided in CDX controller:
- rescan of the CDX controller.
- reset all the devices present on CDX buses.

Sysfs entry is provided in each of the platform device
detected by the CDX controller
- vendor id
- device id
- modalias
- reset of the device.

Signed-off-by: Puneet Gupta <puneet.gupta@xxxxxxx>
Signed-off-by: Nipun Gupta <nipun.gupta@xxxxxxx>
---
drivers/bus/cdx/cdx.c | 158 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 158 insertions(+)

diff --git a/drivers/bus/cdx/cdx.c b/drivers/bus/cdx/cdx.c
index b2a7e0b34df8..41c61bf6d5f0 100644
--- a/drivers/bus/cdx/cdx.c
+++ b/drivers/bus/cdx/cdx.c
@@ -107,6 +107,12 @@ static int cdx_unregister_device(struct device *dev,
return 0;
}

+static void cdx_unregister_devices(struct bus_type *bus)
+{
+ /* Reset all the devices attached to cdx bus */
+ bus_for_each_dev(bus, NULL, NULL, cdx_unregister_device);
+}
+
/**
* cdx_match_one_device - Tell if a CDX device structure has a matching
* CDX device id structure
@@ -196,11 +202,163 @@ static int cdx_dma_configure(struct device *dev)
return 0;
}

+static ssize_t vendor_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ return sprintf(buf, "0x%x\n", cdx_dev->vendor);
+}
+static DEVICE_ATTR_RO(vendor);
+
+static ssize_t device_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ return sprintf(buf, "0x%x\n", cdx_dev->device);
+}
+static DEVICE_ATTR_RO(device);
+
+static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = 0;
+ bool reset = count > 0 && *buf != '0';
+
+ if (!reset)
+ return count;
+
+ ret = reset_cdx_device(dev, NULL);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ return sprintf(buf, "cdx:v%08Xd%d\n", cdx_dev->vendor,
+ cdx_dev->device);
+}
+static DEVICE_ATTR_RO(modalias);
+
+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);
+ const char *old = cdx_dev->driver_override;
+ char *driver_override;
+ char *cp;
+
+ if (WARN_ON(dev->bus != &cdx_bus_type))
+ return -EINVAL;
+
+ if (count >= (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ driver_override = kstrndup(buf, count, GFP_KERNEL);
+ if (!driver_override)
+ return -ENOMEM;
+
+ cp = strchr(driver_override, '\n');
+ if (cp)
+ *cp = '\0';
+
+ if (strlen(driver_override)) {
+ cdx_dev->driver_override = driver_override;
+ } else {
+ kfree(driver_override);
+ cdx_dev->driver_override = NULL;
+ }
+
+ kfree(old);
+
+ 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 snprintf(buf, PAGE_SIZE, "%s\n", cdx_dev->driver_override);
+}
+static DEVICE_ATTR_RW(driver_override);
+
+static struct attribute *cdx_dev_attrs[] = {
+ &dev_attr_reset.attr,
+ &dev_attr_vendor.attr,
+ &dev_attr_device.attr,
+ &dev_attr_modalias.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)
+{
+ bool rescan = count > 0 && *buf != '0';
+ int ret = 0;
+
+ if (!rescan)
+ return count;
+
+ if (!cdx_controller)
+ return -EINVAL;
+
+ /* Unregister all the devices on the bus */
+ cdx_unregister_devices(&cdx_bus_type);
+
+ /* Rescan all the devices */
+ ret = cdx_controller->ops.scan(cdx_controller);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static BUS_ATTR_WO(rescan);
+
+static ssize_t reset_all_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ bool reset = count > 0 && *buf != '0';
+ int ret = 0;
+
+ if (!reset)
+ return count;
+
+ /* Reset all the devices attached to cdx bus */
+ ret = bus_for_each_dev(bus, NULL, NULL, reset_cdx_device);
+ if (ret) {
+ pr_err("error in CDX bus reset\n");
+ return 0;
+ }
+
+ return count;
+}
+static BUS_ATTR_WO(reset_all);
+
+static struct attribute *cdx_bus_attrs[] = {
+ &bus_attr_rescan.attr,
+ &bus_attr_reset_all.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(cdx_bus);
+
struct bus_type cdx_bus_type = {
.name = "cdx",
.match = cdx_bus_match,
.remove = cdx_remove,
.dma_configure = cdx_dma_configure,
+ .dev_groups = cdx_dev_groups,
+ .bus_groups = cdx_bus_groups,
};
EXPORT_SYMBOL_GPL(cdx_bus_type);

--
2.25.1