Received: by 2002:a05:6a10:a0d1:0:0:0:0 with SMTP id j17csp1114951pxa; Thu, 13 Aug 2020 00:25:20 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzYjSIvLiJasO91beFh6PBrO7IBsC6oKarTZrQADqzwfhvMgkvSAOiZZ8NrRGGx1gT44rqr X-Received: by 2002:a17:906:e87:: with SMTP id p7mr3408968ejf.547.1597303519807; Thu, 13 Aug 2020 00:25:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1597303519; cv=none; d=google.com; s=arc-20160816; b=B+fd5AY54wlAHzVv7182Z58RK5Dv8y8QD1EcuzAWyQ2SxiOAGmJzXdCXSC3MMIesrf N/rfmu/1q2MEkO3oL7qhYpRJhtJUNiEOveMO9Hp/KW2g0pzSWkGm0RkMxCPx3NjMMX7O N22VCU06fKJ1ifNUoX2Qg8FlU2NJ4Jy8QqWyivmxpIkekSsF5aheqni0y+NevAHLi8W0 k1JqzeyzqqvJJ9cCJ03q7xAV+uSDBD+jNCk+2sUeMezqPbpoB+Q09D1z5Y/rXnEqALKA fjbgeLuj6bxFw0MPD3Q0AumTFshSxzjIlUcJtCq3fb0J+S45+AeX0PIdKKtpvFA0HriO +0gw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:ironport-sdr:ironport-sdr; bh=7jYjNe/uw5ceSkUqKjr/c8fCLPTnihkIqeX39Gb1SMk=; b=h0fcmrX6gGnVGW2CkvJ61I/15z7+dHPfGNoVTT+D+r8nbwGMhYY6J+KvxLXka/P2l9 oYYCgbhdeKPMqZWzmIvBSmn4ss3PmHTCp8xsJwqX6paTEn00lHqyUyNchPGeB/6966fQ vE3YUIT3iL5CcXGx6Dt92yvj+cP6RM36kyqSXxWgqnopXU2kQLyLGDaStNySHDPSGCS1 vL2XNYDYNcjIbVwCFFylSBq+UhNmWaJTEO6NS9ahNt62tnhxzkxhojpwtIjrvgwgiJIk nKV0MM5Ebkva4z0nlD9evpFKxJLj5m8piT+sc5+hpAEjsz1vnojKMpQS59bL3IHY4r3p lcLg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id r26si2735811edy.137.2020.08.13.00.24.57; Thu, 13 Aug 2020 00:25:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726700AbgHMHWF (ORCPT + 99 others); Thu, 13 Aug 2020 03:22:05 -0400 Received: from mga05.intel.com ([192.55.52.43]:3421 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726048AbgHMHWF (ORCPT ); Thu, 13 Aug 2020 03:22:05 -0400 IronPort-SDR: P/UlSy0GaYbvmd6UIQLPL2flTndJsYn3E0wUHRi4MzyQHjxn8WWH1JFYplV6d3V8LrBhvnfll6 MZNV01b7Ep1g== X-IronPort-AV: E=McAfee;i="6000,8403,9711"; a="239010528" X-IronPort-AV: E=Sophos;i="5.76,307,1592895600"; d="scan'208";a="239010528" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga004.jf.intel.com ([10.7.209.38]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Aug 2020 00:21:49 -0700 IronPort-SDR: X/54ItYypI0K9IMFlbIGy88IU0h99by8FEHBMmd+dSplZSsX99L6fB2XERM6PNXhtTHw7Z7p6J dkB/48iR7E8A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.76,307,1592895600"; d="scan'208";a="439679754" Received: from yilunxu-optiplex-7050.sh.intel.com ([10.239.159.141]) by orsmga004.jf.intel.com with ESMTP; 13 Aug 2020 00:21:46 -0700 From: Xu Yilun To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, Wu Hao , Matthew Gerlach , Russ Weight Subject: [PATCH v5 2/3] fpga: dfl: create a dfl bus type to support DFL devices Date: Thu, 13 Aug 2020 15:17:59 +0800 Message-Id: <1597303080-30640-3-git-send-email-yilun.xu@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1597303080-30640-1-git-send-email-yilun.xu@intel.com> References: <1597303080-30640-1-git-send-email-yilun.xu@intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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. Signed-off-by: Xu Yilun Signed-off-by: Wu Hao Signed-off-by: Matthew Gerlach Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v2: change the bus uevent format. change the dfl device's sysfs name format. refactor dfl_dev_add(). minor fixes for comments from Hao and Tom. v3: no change. v4: improve the uevent format, 4 bits for type & 12 bits for id. change dfl_device->type to u8. A dedicate field in struct dfl_feature for dfl device instance. error out if dfl_device already exist on dfl_devs_init(). v5: minor fixes for Hao's comments --- Documentation/ABI/testing/sysfs-bus-dfl | 15 ++ drivers/fpga/dfl.c | 263 +++++++++++++++++++++++++++++++- drivers/fpga/dfl.h | 86 +++++++++++ 3 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-dfl diff --git a/Documentation/ABI/testing/sysfs-bus-dfl b/Documentation/ABI/testing/sysfs-bus-dfl new file mode 100644 index 0000000..23543be --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-dfl @@ -0,0 +1,15 @@ +What: /sys/bus/dfl/devices/dfl_dev.X/type +Date: Aug 2020 +KernelVersion: 5.10 +Contact: Xu Yilun +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 +Description: Read-only. It returns feature identifier local to its DFL FIU + type. + Format: 0x%x diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 52cafa2..1a9ad7e 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -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,245 @@ 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 = 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_device *ddev = to_dfl_dev(dev); + struct dfl_driver *ddrv = to_dfl_drv(dev->driver); + + return ddrv->probe(ddev); +} + +static int dfl_bus_remove(struct device *dev) +{ + struct dfl_device *ddev = to_dfl_dev(dev); + struct dfl_driver *ddrv = to_dfl_drv(dev->driver); + + 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); +} + +/* show dfl info fields */ +#define dfl_info_attr(field, format_string) \ +static ssize_t \ +field##_show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct dfl_device *ddev = to_dfl_dev(dev); \ + \ + return sprintf(buf, format_string, ddev->field); \ +} \ +static DEVICE_ATTR_RO(field) + +dfl_info_attr(type, "0x%x\n"); +dfl_info_attr(feature_id, "0x%x\n"); + +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_info(&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 platform_device *pdev) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + 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 +494,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 +583,10 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev, drv++; } + ret = dfl_devs_add(pdev); + if (ret) + goto exit; + return 0; exit: dfl_fpga_dev_feature_uinit(pdev); @@ -1284,11 +1524,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 +1872,7 @@ static void __exit dfl_fpga_exit(void) { dfl_chardev_uinit(); dfl_ids_destroy(); + bus_unregister(&dfl_bus_type); } module_init(dfl_fpga_init); diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 5973769..fd93f08 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -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 */ -- 2.7.4