mirror of https://gitee.com/openkylin/linux.git
fpga: dfl: create a dfl bus type to support DFL devices
A new bus type "dfl" is introduced for private features which are not initialized by DFL feature drivers (dfl-fme & dfl-afu drivers). So these private features could be handled by separate driver modules. DFL feature drivers (dfl-fme, dfl-port) will create DFL devices on enumeration. DFL drivers could be registered on this bus to match these DFL devices. They are matched by dfl type & feature_id. [mdf@kernel.org: Add missing Documentation part to MAINTAINERS file] Signed-off-by: Xu Yilun <yilun.xu@intel.com> Signed-off-by: Wu Hao <hao.wu@intel.com> Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com> Signed-off-by: Russ Weight <russell.h.weight@intel.com> Reviewed-by: Tom Rix <trix@redhat.com> Acked-by: Wu Hao <hao.wu@intel.com> Signed-off-by: Moritz Fischer <mdf@kernel.org>
This commit is contained in:
parent
c71e805083
commit
9ba3a0aa09
|
@ -0,0 +1,15 @@
|
|||
What: /sys/bus/dfl/devices/dfl_dev.X/type
|
||||
Date: Aug 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: Xu Yilun <yilun.xu@intel.com>
|
||||
Description: Read-only. It returns type of DFL FIU of the device. Now DFL
|
||||
supports 2 FIU types, 0 for FME, 1 for PORT.
|
||||
Format: 0x%x
|
||||
|
||||
What: /sys/bus/dfl/devices/dfl_dev.X/feature_id
|
||||
Date: Aug 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: Xu Yilun <yilun.xu@intel.com>
|
||||
Description: Read-only. It returns feature identifier local to its DFL FIU
|
||||
type.
|
||||
Format: 0x%x
|
|
@ -6814,6 +6814,7 @@ M: Wu Hao <hao.wu@intel.com>
|
|||
R: Tom Rix <trix@redhat.com>
|
||||
L: linux-fpga@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-dfl
|
||||
F: Documentation/fpga/dfl.rst
|
||||
F: drivers/fpga/dfl*
|
||||
F: include/uapi/linux/fpga-dfl.h
|
||||
|
|
|
@ -30,12 +30,6 @@ static DEFINE_MUTEX(dfl_id_mutex);
|
|||
* index to dfl_chardevs table. If no chardev support just set devt_type
|
||||
* as one invalid index (DFL_FPGA_DEVT_MAX).
|
||||
*/
|
||||
enum dfl_id_type {
|
||||
FME_ID, /* fme id allocation and mapping */
|
||||
PORT_ID, /* port id allocation and mapping */
|
||||
DFL_ID_MAX,
|
||||
};
|
||||
|
||||
enum dfl_fpga_devt_type {
|
||||
DFL_FPGA_DEVT_FME,
|
||||
DFL_FPGA_DEVT_PORT,
|
||||
|
@ -250,6 +244,247 @@ int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id);
|
||||
|
||||
static DEFINE_IDA(dfl_device_ida);
|
||||
|
||||
static const struct dfl_device_id *
|
||||
dfl_match_one_device(const struct dfl_device_id *id, struct dfl_device *ddev)
|
||||
{
|
||||
if (id->type == ddev->type && id->feature_id == ddev->feature_id)
|
||||
return id;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dfl_bus_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
struct dfl_driver *ddrv = to_dfl_drv(drv);
|
||||
const struct dfl_device_id *id_entry;
|
||||
|
||||
id_entry = ddrv->id_table;
|
||||
if (id_entry) {
|
||||
while (id_entry->feature_id) {
|
||||
if (dfl_match_one_device(id_entry, ddev)) {
|
||||
ddev->id_entry = id_entry;
|
||||
return 1;
|
||||
}
|
||||
id_entry++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dfl_bus_probe(struct device *dev)
|
||||
{
|
||||
struct dfl_driver *ddrv = to_dfl_drv(dev->driver);
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
|
||||
return ddrv->probe(ddev);
|
||||
}
|
||||
|
||||
static int dfl_bus_remove(struct device *dev)
|
||||
{
|
||||
struct dfl_driver *ddrv = to_dfl_drv(dev->driver);
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
|
||||
if (ddrv->remove)
|
||||
ddrv->remove(ddev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dfl_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
|
||||
/* The type has 4 valid bits and feature_id has 12 valid bits */
|
||||
return add_uevent_var(env, "MODALIAS=dfl:t%01Xf%03X",
|
||||
ddev->type, ddev->feature_id);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
type_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
|
||||
return sprintf(buf, "0x%x\n", ddev->type);
|
||||
}
|
||||
static DEVICE_ATTR_RO(type);
|
||||
|
||||
static ssize_t
|
||||
feature_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
|
||||
return sprintf(buf, "0x%x\n", ddev->feature_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(feature_id);
|
||||
|
||||
static struct attribute *dfl_dev_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_feature_id.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(dfl_dev);
|
||||
|
||||
static struct bus_type dfl_bus_type = {
|
||||
.name = "dfl",
|
||||
.match = dfl_bus_match,
|
||||
.probe = dfl_bus_probe,
|
||||
.remove = dfl_bus_remove,
|
||||
.uevent = dfl_bus_uevent,
|
||||
.dev_groups = dfl_dev_groups,
|
||||
};
|
||||
|
||||
static void release_dfl_dev(struct device *dev)
|
||||
{
|
||||
struct dfl_device *ddev = to_dfl_dev(dev);
|
||||
|
||||
if (ddev->mmio_res.parent)
|
||||
release_resource(&ddev->mmio_res);
|
||||
|
||||
ida_simple_remove(&dfl_device_ida, ddev->id);
|
||||
kfree(ddev->irqs);
|
||||
kfree(ddev);
|
||||
}
|
||||
|
||||
static struct dfl_device *
|
||||
dfl_dev_add(struct dfl_feature_platform_data *pdata,
|
||||
struct dfl_feature *feature)
|
||||
{
|
||||
struct platform_device *pdev = pdata->dev;
|
||||
struct resource *parent_res;
|
||||
struct dfl_device *ddev;
|
||||
int id, i, ret;
|
||||
|
||||
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
|
||||
if (!ddev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
id = ida_simple_get(&dfl_device_ida, 0, 0, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
dev_err(&pdev->dev, "unable to get id\n");
|
||||
kfree(ddev);
|
||||
return ERR_PTR(id);
|
||||
}
|
||||
|
||||
/* freeing resources by put_device() after device_initialize() */
|
||||
device_initialize(&ddev->dev);
|
||||
ddev->dev.parent = &pdev->dev;
|
||||
ddev->dev.bus = &dfl_bus_type;
|
||||
ddev->dev.release = release_dfl_dev;
|
||||
ddev->id = id;
|
||||
ret = dev_set_name(&ddev->dev, "dfl_dev.%d", id);
|
||||
if (ret)
|
||||
goto put_dev;
|
||||
|
||||
ddev->type = feature_dev_id_type(pdev);
|
||||
ddev->feature_id = feature->id;
|
||||
ddev->cdev = pdata->dfl_cdev;
|
||||
|
||||
/* add mmio resource */
|
||||
parent_res = &pdev->resource[feature->resource_index];
|
||||
ddev->mmio_res.flags = IORESOURCE_MEM;
|
||||
ddev->mmio_res.start = parent_res->start;
|
||||
ddev->mmio_res.end = parent_res->end;
|
||||
ddev->mmio_res.name = dev_name(&ddev->dev);
|
||||
ret = insert_resource(parent_res, &ddev->mmio_res);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s failed to claim resource: %pR\n",
|
||||
dev_name(&ddev->dev), &ddev->mmio_res);
|
||||
goto put_dev;
|
||||
}
|
||||
|
||||
/* then add irq resource */
|
||||
if (feature->nr_irqs) {
|
||||
ddev->irqs = kcalloc(feature->nr_irqs,
|
||||
sizeof(*ddev->irqs), GFP_KERNEL);
|
||||
if (!ddev->irqs) {
|
||||
ret = -ENOMEM;
|
||||
goto put_dev;
|
||||
}
|
||||
|
||||
for (i = 0; i < feature->nr_irqs; i++)
|
||||
ddev->irqs[i] = feature->irq_ctx[i].irq;
|
||||
|
||||
ddev->num_irqs = feature->nr_irqs;
|
||||
}
|
||||
|
||||
ret = device_add(&ddev->dev);
|
||||
if (ret)
|
||||
goto put_dev;
|
||||
|
||||
dev_dbg(&pdev->dev, "add dfl_dev: %s\n", dev_name(&ddev->dev));
|
||||
return ddev;
|
||||
|
||||
put_dev:
|
||||
/* calls release_dfl_dev() which does the clean up */
|
||||
put_device(&ddev->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void dfl_devs_remove(struct dfl_feature_platform_data *pdata)
|
||||
{
|
||||
struct dfl_feature *feature;
|
||||
|
||||
dfl_fpga_dev_for_each_feature(pdata, feature) {
|
||||
if (feature->ddev) {
|
||||
device_unregister(&feature->ddev->dev);
|
||||
feature->ddev = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dfl_devs_add(struct dfl_feature_platform_data *pdata)
|
||||
{
|
||||
struct dfl_feature *feature;
|
||||
struct dfl_device *ddev;
|
||||
int ret;
|
||||
|
||||
dfl_fpga_dev_for_each_feature(pdata, feature) {
|
||||
if (feature->ioaddr)
|
||||
continue;
|
||||
|
||||
if (feature->ddev) {
|
||||
ret = -EEXIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ddev = dfl_dev_add(pdata, feature);
|
||||
if (IS_ERR(ddev)) {
|
||||
ret = PTR_ERR(ddev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
feature->ddev = ddev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dfl_devs_remove(pdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner)
|
||||
{
|
||||
if (!dfl_drv || !dfl_drv->probe || !dfl_drv->id_table)
|
||||
return -EINVAL;
|
||||
|
||||
dfl_drv->drv.owner = owner;
|
||||
dfl_drv->drv.bus = &dfl_bus_type;
|
||||
|
||||
return driver_register(&dfl_drv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL(__dfl_driver_register);
|
||||
|
||||
void dfl_driver_unregister(struct dfl_driver *dfl_drv)
|
||||
{
|
||||
driver_unregister(&dfl_drv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL(dfl_driver_unregister);
|
||||
|
||||
#define is_header_feature(feature) ((feature)->id == FEATURE_ID_FIU_HEADER)
|
||||
|
||||
/**
|
||||
|
@ -261,12 +496,15 @@ void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
|
|||
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct dfl_feature *feature;
|
||||
|
||||
dfl_fpga_dev_for_each_feature(pdata, feature)
|
||||
dfl_devs_remove(pdata);
|
||||
|
||||
dfl_fpga_dev_for_each_feature(pdata, feature) {
|
||||
if (feature->ops) {
|
||||
if (feature->ops->uinit)
|
||||
feature->ops->uinit(pdev, feature);
|
||||
feature->ops = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit);
|
||||
|
||||
|
@ -347,6 +585,10 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev,
|
|||
drv++;
|
||||
}
|
||||
|
||||
ret = dfl_devs_add(pdata);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
dfl_fpga_dev_feature_uinit(pdev);
|
||||
|
@ -1284,11 +1526,17 @@ static int __init dfl_fpga_init(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = bus_register(&dfl_bus_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dfl_ids_init();
|
||||
|
||||
ret = dfl_chardev_init();
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dfl_ids_destroy();
|
||||
bus_unregister(&dfl_bus_type);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1626,6 +1874,7 @@ static void __exit dfl_fpga_exit(void)
|
|||
{
|
||||
dfl_chardev_uinit();
|
||||
dfl_ids_destroy();
|
||||
bus_unregister(&dfl_bus_type);
|
||||
}
|
||||
|
||||
module_init(dfl_fpga_init);
|
||||
|
|
|
@ -236,6 +236,7 @@ struct dfl_feature_irq_ctx {
|
|||
* @irq_ctx: interrupt context list.
|
||||
* @nr_irqs: number of interrupt contexts.
|
||||
* @ops: ops of this sub feature.
|
||||
* @ddev: ptr to the dfl device of this sub feature.
|
||||
* @priv: priv data of this feature.
|
||||
*/
|
||||
struct dfl_feature {
|
||||
|
@ -246,6 +247,7 @@ struct dfl_feature {
|
|||
struct dfl_feature_irq_ctx *irq_ctx;
|
||||
unsigned int nr_irqs;
|
||||
const struct dfl_feature_ops *ops;
|
||||
struct dfl_device *ddev;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
|
@ -514,4 +516,88 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
|
|||
struct dfl_feature *feature,
|
||||
unsigned long arg);
|
||||
|
||||
/**
|
||||
* enum dfl_id_type - define the DFL FIU types
|
||||
*/
|
||||
enum dfl_id_type {
|
||||
FME_ID,
|
||||
PORT_ID,
|
||||
DFL_ID_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dfl_device_id - dfl device identifier
|
||||
* @type: contains 4 bits DFL FIU type of the device. See enum dfl_id_type.
|
||||
* @feature_id: contains 12 bits feature identifier local to its DFL FIU type.
|
||||
* @driver_data: driver specific data.
|
||||
*/
|
||||
struct dfl_device_id {
|
||||
u8 type;
|
||||
u16 feature_id;
|
||||
unsigned long driver_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dfl_device - represent an dfl device on dfl bus
|
||||
*
|
||||
* @dev: generic device interface.
|
||||
* @id: id of the dfl device.
|
||||
* @type: type of DFL FIU of the device. See enum dfl_id_type.
|
||||
* @feature_id: 16 bits feature identifier local to its DFL FIU type.
|
||||
* @mmio_res: mmio resource of this dfl device.
|
||||
* @irqs: list of Linux IRQ numbers of this dfl device.
|
||||
* @num_irqs: number of IRQs supported by this dfl device.
|
||||
* @cdev: pointer to DFL FPGA container device this dfl device belongs to.
|
||||
* @id_entry: matched id entry in dfl driver's id table.
|
||||
*/
|
||||
struct dfl_device {
|
||||
struct device dev;
|
||||
int id;
|
||||
u8 type;
|
||||
u16 feature_id;
|
||||
struct resource mmio_res;
|
||||
int *irqs;
|
||||
unsigned int num_irqs;
|
||||
struct dfl_fpga_cdev *cdev;
|
||||
const struct dfl_device_id *id_entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dfl_driver - represent an dfl device driver
|
||||
*
|
||||
* @drv: driver model structure.
|
||||
* @id_table: pointer to table of device IDs the driver is interested in.
|
||||
* { } member terminated.
|
||||
* @probe: mandatory callback for device binding.
|
||||
* @remove: callback for device unbinding.
|
||||
*/
|
||||
struct dfl_driver {
|
||||
struct device_driver drv;
|
||||
const struct dfl_device_id *id_table;
|
||||
|
||||
int (*probe)(struct dfl_device *dfl_dev);
|
||||
void (*remove)(struct dfl_device *dfl_dev);
|
||||
};
|
||||
|
||||
#define to_dfl_dev(d) container_of(d, struct dfl_device, dev)
|
||||
#define to_dfl_drv(d) container_of(d, struct dfl_driver, drv)
|
||||
|
||||
/*
|
||||
* use a macro to avoid include chaining to get THIS_MODULE.
|
||||
*/
|
||||
#define dfl_driver_register(drv) \
|
||||
__dfl_driver_register(drv, THIS_MODULE)
|
||||
int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner);
|
||||
void dfl_driver_unregister(struct dfl_driver *dfl_drv);
|
||||
|
||||
/*
|
||||
* module_dfl_driver() - Helper macro for drivers that don't do
|
||||
* anything special in module init/exit. This eliminates a lot of
|
||||
* boilerplate. Each module may only use this macro once, and
|
||||
* calling it replaces module_init() and module_exit().
|
||||
*/
|
||||
#define module_dfl_driver(__dfl_driver) \
|
||||
module_driver(__dfl_driver, dfl_driver_register, \
|
||||
dfl_driver_unregister)
|
||||
|
||||
#endif /* __FPGA_DFL_H */
|
||||
|
|
Loading…
Reference in New Issue