ARM: 8256/1: driver coamba: add device binding path 'driver_override'
As already demonstrated with PCI [1] and the platform bus [2], a driver_override property in sysfs can be used to bypass the id matching of a device to a AMBA driver. This can be used by VFIO to bind to any AMBA device requested by the user. [1] http://lists-archives.com/linux-kernel/28030441-pci-introduce-new-device-binding-path-using-pci_dev-driver_override.html [2] https://www.redhat.com/archives/libvir-list/2014-April/msg00382.html Signed-off-by: Antonios Motakis <a.motakis@virtualopensystems.com> Reviewed-by: Kim Phillips <kim.phillips@freescale.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
8684014d71
commit
3cf3857134
|
@ -0,0 +1,20 @@
|
||||||
|
What: /sys/bus/amba/devices/.../driver_override
|
||||||
|
Date: September 2014
|
||||||
|
Contact: Antonios Motakis <a.motakis@virtualopensystems.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-amba > 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.
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/amba/bus.h>
|
#include <linux/amba/bus.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
|
||||||
|
@ -43,6 +44,10 @@ static int amba_match(struct device *dev, struct device_driver *drv)
|
||||||
struct amba_device *pcdev = to_amba_device(dev);
|
struct amba_device *pcdev = to_amba_device(dev);
|
||||||
struct amba_driver *pcdrv = to_amba_driver(drv);
|
struct amba_driver *pcdrv = to_amba_driver(drv);
|
||||||
|
|
||||||
|
/* When driver_override is set, only bind to the matching driver */
|
||||||
|
if (pcdev->driver_override)
|
||||||
|
return !strcmp(pcdev->driver_override, drv->name);
|
||||||
|
|
||||||
return amba_lookup(pcdrv->id_table, pcdev) != NULL;
|
return amba_lookup(pcdrv->id_table, pcdev) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +64,47 @@ static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t driver_override_show(struct device *_dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct amba_device *dev = to_amba_device(_dev);
|
||||||
|
|
||||||
|
if (!dev->driver_override)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return sprintf(buf, "%s\n", dev->driver_override);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t driver_override_store(struct device *_dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct amba_device *dev = to_amba_device(_dev);
|
||||||
|
char *driver_override, *old = dev->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)) {
|
||||||
|
dev->driver_override = driver_override;
|
||||||
|
} else {
|
||||||
|
kfree(driver_override);
|
||||||
|
dev->driver_override = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(old);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
#define amba_attr_func(name,fmt,arg...) \
|
#define amba_attr_func(name,fmt,arg...) \
|
||||||
static ssize_t name##_show(struct device *_dev, \
|
static ssize_t name##_show(struct device *_dev, \
|
||||||
struct device_attribute *attr, char *buf) \
|
struct device_attribute *attr, char *buf) \
|
||||||
|
@ -81,6 +127,7 @@ amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n",
|
||||||
static struct device_attribute amba_dev_attrs[] = {
|
static struct device_attribute amba_dev_attrs[] = {
|
||||||
__ATTR_RO(id),
|
__ATTR_RO(id),
|
||||||
__ATTR_RO(resource),
|
__ATTR_RO(resource),
|
||||||
|
__ATTR_RW(driver_override),
|
||||||
__ATTR_NULL,
|
__ATTR_NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ struct amba_device {
|
||||||
struct clk *pclk;
|
struct clk *pclk;
|
||||||
unsigned int periphid;
|
unsigned int periphid;
|
||||||
unsigned int irq[AMBA_NR_IRQS];
|
unsigned int irq[AMBA_NR_IRQS];
|
||||||
|
char *driver_override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct amba_driver {
|
struct amba_driver {
|
||||||
|
|
Loading…
Reference in New Issue