diff options
author | Kim Phillips <kim.phillips@freescale.com> | 2014-06-02 20:42:58 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-08 18:31:26 -0400 |
commit | 3d713e0e382e6fcfb4bba1501645b66c129ad60b (patch) | |
tree | 9b47c38f09df44537b163ca18eabc8c979aa1f04 /drivers/base/platform.c | |
parent | 1cec24c59b4a133fc9c83912996168f511075485 (diff) |
driver core: platform: add device binding path 'driver_override'
Needed by platform device drivers, such as the upcoming
vfio-platform driver, in order to bypass the existing OF, ACPI,
id_table and name string matches, and successfully be able to be
bound to any device, like so:
echo vfio-platform > /sys/bus/platform/devices/fff51000.ethernet/driver_override
echo fff51000.ethernet > /sys/bus/platform/devices/fff51000.ethernet/driver/unbind
echo fff51000.ethernet > /sys/bus/platform/drivers_probe
This mimics "PCI: Introduce new device binding path using
pci_dev.driver_override", which is an interface enhancement
for more deterministic PCI device binding, e.g., when in the
presence of hotplug.
Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
Reviewed-by: Alexander Graf <agraf@suse.de>
Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com>
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/platform.c')
-rw-r--r-- | drivers/base/platform.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c48c4acb9b87..148f66a1d49a 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/pm_runtime.h> | 23 | #include <linux/pm_runtime.h> |
24 | #include <linux/idr.h> | 24 | #include <linux/idr.h> |
25 | #include <linux/acpi.h> | 25 | #include <linux/acpi.h> |
26 | #include <linux/limits.h> | ||
26 | 27 | ||
27 | #include "base.h" | 28 | #include "base.h" |
28 | #include "power/power.h" | 29 | #include "power/power.h" |
@@ -191,6 +192,7 @@ static void platform_device_release(struct device *dev) | |||
191 | kfree(pa->pdev.dev.platform_data); | 192 | kfree(pa->pdev.dev.platform_data); |
192 | kfree(pa->pdev.mfd_cell); | 193 | kfree(pa->pdev.mfd_cell); |
193 | kfree(pa->pdev.resource); | 194 | kfree(pa->pdev.resource); |
195 | kfree(pa->pdev.driver_override); | ||
194 | kfree(pa); | 196 | kfree(pa); |
195 | } | 197 | } |
196 | 198 | ||
@@ -698,8 +700,49 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | |||
698 | } | 700 | } |
699 | static DEVICE_ATTR_RO(modalias); | 701 | static DEVICE_ATTR_RO(modalias); |
700 | 702 | ||
703 | static ssize_t driver_override_store(struct device *dev, | ||
704 | struct device_attribute *attr, | ||
705 | const char *buf, size_t count) | ||
706 | { | ||
707 | struct platform_device *pdev = to_platform_device(dev); | ||
708 | char *driver_override, *old = pdev->driver_override, *cp; | ||
709 | |||
710 | if (count > PATH_MAX) | ||
711 | return -EINVAL; | ||
712 | |||
713 | driver_override = kstrndup(buf, count, GFP_KERNEL); | ||
714 | if (!driver_override) | ||
715 | return -ENOMEM; | ||
716 | |||
717 | cp = strchr(driver_override, '\n'); | ||
718 | if (cp) | ||
719 | *cp = '\0'; | ||
720 | |||
721 | if (strlen(driver_override)) { | ||
722 | pdev->driver_override = driver_override; | ||
723 | } else { | ||
724 | kfree(driver_override); | ||
725 | pdev->driver_override = NULL; | ||
726 | } | ||
727 | |||
728 | kfree(old); | ||
729 | |||
730 | return count; | ||
731 | } | ||
732 | |||
733 | static ssize_t driver_override_show(struct device *dev, | ||
734 | struct device_attribute *attr, char *buf) | ||
735 | { | ||
736 | struct platform_device *pdev = to_platform_device(dev); | ||
737 | |||
738 | return sprintf(buf, "%s\n", pdev->driver_override); | ||
739 | } | ||
740 | static DEVICE_ATTR_RW(driver_override); | ||
741 | |||
742 | |||
701 | static struct attribute *platform_dev_attrs[] = { | 743 | static struct attribute *platform_dev_attrs[] = { |
702 | &dev_attr_modalias.attr, | 744 | &dev_attr_modalias.attr, |
745 | &dev_attr_driver_override.attr, | ||
703 | NULL, | 746 | NULL, |
704 | }; | 747 | }; |
705 | ATTRIBUTE_GROUPS(platform_dev); | 748 | ATTRIBUTE_GROUPS(platform_dev); |
@@ -755,6 +798,10 @@ static int platform_match(struct device *dev, struct device_driver *drv) | |||
755 | struct platform_device *pdev = to_platform_device(dev); | 798 | struct platform_device *pdev = to_platform_device(dev); |
756 | struct platform_driver *pdrv = to_platform_driver(drv); | 799 | struct platform_driver *pdrv = to_platform_driver(drv); |
757 | 800 | ||
801 | /* When driver_override is set, only bind to the matching driver */ | ||
802 | if (pdev->driver_override) | ||
803 | return !strcmp(pdev->driver_override, drv->name); | ||
804 | |||
758 | /* Attempt an OF style match first */ | 805 | /* Attempt an OF style match first */ |
759 | if (of_driver_match_device(dev, drv)) | 806 | if (of_driver_match_device(dev, drv)) |
760 | return 1; | 807 | return 1; |