From 0bda83c15b2ecfc45fac0656df15d4f4fa65afa9 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Wed, 18 Mar 2015 17:32:59 -0500 Subject: [PATCH 193/226] staging: fsl-mc: add device binding path 'driver_override' This patch is required for vfio-fsl-mc meta driver to successfully bind layerscape container devices for device passthrough. This patch adds a mechanism to allow a layerscape device to specify a driver rather than a layerscape driver provide a device match. This patch is based on following proposed patches for PCI and platform devices - https://lkml.org/lkml/2014/4/8/571 :- For Platform devices - http://lists-archives.com/linux-kernel/28030441-pci-introduce-new-device-binding-path-using-pci_dev-driver_override.html :- For PCI devices Example to allow a device (dprc.1) to specifically bind with driver (vfio-fsl-mc):- - echo vfio-fsl-mc > /sys/bus/fsl-mc/devices/dprc.1/driver_override - echo dprc.1 > /sys/bus/fsl-mc/drivers/fsl_mc_dprc/unbind - echo dprc.1 > /sys/bus/fsl-mc/drivers/vfio-fsl-mc/bind Signed-off-by: J. German Rivera (Stuart: resolved merge conflicts) Signed-off-by: Stuart Yoder --- drivers/staging/fsl-mc/bus/mc-bus.c | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) --- a/drivers/staging/fsl-mc/bus/mc-bus.c +++ b/drivers/staging/fsl-mc/bus/mc-bus.c @@ -42,6 +42,12 @@ static int fsl_mc_bus_match(struct devic if (WARN_ON(!fsl_mc_bus_exists())) goto out; + /* When driver_override is set, only bind to the matching driver */ + if (mc_dev->driver_override) { + found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); + goto out; + } + if (!mc_drv->match_id_table) goto out; @@ -96,6 +102,50 @@ static ssize_t modalias_show(struct devi } 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 fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + const char *driver_override, *old = mc_dev->driver_override; + char *cp; + + if (WARN_ON(dev->bus != &fsl_mc_bus_type)) + return -EINVAL; + + if (count > PATH_MAX) + 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)) { + mc_dev->driver_override = driver_override; + } else { + kfree(driver_override); + mc_dev->driver_override = NULL; + } + + kfree(old); + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + + return sprintf(buf, "%s\n", mc_dev->driver_override); +} + +static DEVICE_ATTR_RW(driver_override); + static ssize_t rescan_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -127,6 +177,7 @@ static DEVICE_ATTR_WO(rescan); static struct attribute *fsl_mc_dev_attrs[] = { &dev_attr_modalias.attr, &dev_attr_rescan.attr, + &dev_attr_driver_override.attr, NULL, }; @@ -677,6 +728,8 @@ void fsl_mc_device_remove(struct fsl_mc_ } } + kfree(mc_dev->driver_override); + mc_dev->driver_override = NULL; if (mc_bus) devm_kfree(mc_dev->dev.parent, mc_bus); else