Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d713e0e authored by Kim Phillips's avatar Kim Phillips Committed by Greg Kroah-Hartman
Browse files

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: default avatarAlex Williamson <alex.williamson@redhat.com>
Reviewed-by: default avatarAlexander Graf <agraf@suse.de>
Reviewed-by: default avatarStuart Yoder <stuart.yoder@freescale.com>
Signed-off-by: default avatarKim Phillips <kim.phillips@freescale.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1cec24c5
Loading
Loading
Loading
Loading
+20 −0
Original line number Original line Diff line number Diff line
What:		/sys/bus/platform/devices/.../driver_override
Date:		April 2014
Contact:	Kim Phillips <kim.phillips@freescale.com>
Description:
		This file allows the driver for a device to be specified which
		will override standard OF, ACPI, ID table, and name matching.
		When specified, only a driver with a name matching the value
		written to driver_override will have an opportunity to bind
		to the device.  The override is specified by writing a string
		to the driver_override file (echo vfio-platform > \
		driver_override) and may be cleared with an empty string
		(echo > driver_override).  This returns the device to standard
		matching rules binding.  Writing to driver_override does not
		automatically unbind the device from its current driver or make
		any attempt to automatically load the specified driver.  If no
		driver with a matching name is currently loaded in the kernel,
		the device will not bind to any driver.  This also allows
		devices to opt-out of driver binding using a driver_override
		name such as "none".  Only a single driver may be specified in
		the override, there is no support for parsing delimiters.
+47 −0
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/pm_runtime.h>
#include <linux/pm_runtime.h>
#include <linux/idr.h>
#include <linux/idr.h>
#include <linux/acpi.h>
#include <linux/acpi.h>
#include <linux/limits.h>


#include "base.h"
#include "base.h"
#include "power/power.h"
#include "power/power.h"
@@ -191,6 +192,7 @@ static void platform_device_release(struct device *dev)
	kfree(pa->pdev.dev.platform_data);
	kfree(pa->pdev.dev.platform_data);
	kfree(pa->pdev.mfd_cell);
	kfree(pa->pdev.mfd_cell);
	kfree(pa->pdev.resource);
	kfree(pa->pdev.resource);
	kfree(pa->pdev.driver_override);
	kfree(pa);
	kfree(pa);
}
}


@@ -698,8 +700,49 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
}
}
static DEVICE_ATTR_RO(modalias);
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 platform_device *pdev = to_platform_device(dev);
	char *driver_override, *old = pdev->driver_override, *cp;

	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)) {
		pdev->driver_override = driver_override;
	} else {
		kfree(driver_override);
		pdev->driver_override = NULL;
	}

	kfree(old);

	return count;
}

static ssize_t driver_override_show(struct device *dev,
				    struct device_attribute *attr, char *buf)
{
	struct platform_device *pdev = to_platform_device(dev);

	return sprintf(buf, "%s\n", pdev->driver_override);
}
static DEVICE_ATTR_RW(driver_override);


static struct attribute *platform_dev_attrs[] = {
static struct attribute *platform_dev_attrs[] = {
	&dev_attr_modalias.attr,
	&dev_attr_modalias.attr,
	&dev_attr_driver_override.attr,
	NULL,
	NULL,
};
};
ATTRIBUTE_GROUPS(platform_dev);
ATTRIBUTE_GROUPS(platform_dev);
@@ -755,6 +798,10 @@ static int platform_match(struct device *dev, struct device_driver *drv)
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);
	struct platform_driver *pdrv = to_platform_driver(drv);


	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
	if (of_driver_match_device(dev, drv))
		return 1;
		return 1;
+1 −0
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ struct platform_device {
	struct resource	*resource;
	struct resource	*resource;


	const struct platform_device_id	*id_entry;
	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */


	/* MFD cell pointer */
	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;
	struct mfd_cell *mfd_cell;