On Fri, Jul 23, 2010 at 2:57 PM, Stephen Neuendorffer
<[email protected]> wrote:
> This device has an internal bus which contains multiple devices, which
> are described in a device tree.
>
> Signed-off-by: Stephen Neuendorffer <[email protected]>

You need to cc: the pci mailing list on the next submission.

>
> ----
>
> This is probably still preliminary, although it actually works now.
> 1) It should probably correctly deallocate and free all the contained
> devices.
> 2) It should probably handle different device trees for multiple boards
> in the same system.  How to do this?
> ---
>  drivers/pci/Kconfig        |    8 ++
>  drivers/pci/Makefile       |    2 +
>  drivers/pci/xilinx_pcipr.c |  197 
> ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 207 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/pci/xilinx_pcipr.c
>
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index 34ef70d..69616b6 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -65,3 +65,11 @@ config PCI_IOAPIC
>        depends on ACPI
>        depends on HOTPLUG
>        default y
> +
> +config XILINX_PCIPR
> +       tristate "Xilinx OF-based PCI endpoint"
> +       depends on PCI
> +       select OF
> +       select OF_FLATTREE
> +       help
> +         Enable support for Xilinx PCIPR endpoint
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index 0b51857..ec44c21 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -62,6 +62,8 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o
>
>  obj-$(CONFIG_PCI_STUB) += pci-stub.o
>
> +obj-$(CONFIG_XILINX_PCIPR) += xilinx_pcipr.o
> +
>  ifeq ($(CONFIG_PCI_DEBUG),y)
>  EXTRA_CFLAGS += -DDEBUG
>  endif
> diff --git a/drivers/pci/xilinx_pcipr.c b/drivers/pci/xilinx_pcipr.c
> new file mode 100644
> index 0000000..80f8f1a
> --- /dev/null
> +++ b/drivers/pci/xilinx_pcipr.c
> @@ -0,0 +1,197 @@
> +/*

Always good practice to include a one line description of what this
file is for in the header block.

> + *  Copyright 2010 Xilinx, Inc.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/ide.h>
> +#include <linux/init.h>
> +#include <linux/dmi.h>
> +#include <linux/firmware.h>
> +
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/of_platform.h>
> +
> +#include <linux/io.h>
> +
> +#define DRV_NAME "xilinx_pcipr"

unused

> +
> +struct xilinx_pcipr_drvdata {
> +       struct device_node *child_nodes;
> +       struct firmware *fw_entry;
> +};
> +
> +/**
> + *     xilinx_pcipr_probe - Setup the endpoint
> + *     @dev: PCI device to set up
> + */
> +static int __devinit xilinx_pcipr_probe(struct pci_dev *pdev,
> +                                       const struct pci_device_id *id)
> +{
> +       int ret;
> +       struct xilinx_pcipr_drvdata *drvdata;
> +       resource_size_t start, len;
> +       struct property *ranges_prop;
> +       unsigned long *value;
> +       struct device_node *bus_node;
> +
> +       dev_dbg(&pdev->dev, "xilinx_pcipr_probe\n");
> +
> +       drvdata = kzalloc(sizeof(struct xilinx_pcipr_drvdata), GFP_KERNEL);
> +       if (!drvdata) {
> +               dev_err(&pdev->dev,
> +                       "Couldn't allocate device private record\n");
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       dev_set_drvdata(&pdev->dev, (void *)drvdata);
> +
> +       /*
> +        * Do some basic sanity checking..
> +        */
> +       if (pci_enable_device(pdev)) {
> +               ret = -EBUSY;
> +               goto free;
> +       }
> +
> +       /* Enable the board to bus master. */
> +       pci_set_master(pdev);
> +
> +       if (request_firmware(&drvdata->fw_entry,
> +                           "xilinx_pcipr.dtb", &pdev->dev)) {
> +               dev_err(&pdev->dev, "Couldn't get dtb\n");
> +               ret = -ENODEV;
> +               goto disable;
> +       }
> +
> +
> +       ret = pci_request_regions(pdev, "xilinx_pcipr");
> +       if (ret)
> +               goto release_dtb;
> +
> +       if (!pci_resource_start(pdev, 0)) {
> +               dev_printk(KERN_ERR, &pdev->dev, "No cardbus resource!\n");
> +               ret = -ENODEV;
> +               goto release;
> +       }
> +
> +
> +       /*
> +        * report the subsystem vendor and device for help debugging
> +        * the irq stuff...
> +        */
> +       dev_printk(KERN_INFO, &pdev->dev,
> +                  "Xilinx PCIPR bridge found [%04x:%04x]\n",
> +                  pdev->subsystem_vendor, pdev->subsystem_device);
> +
> +       /* Stuff the ranges property into the toplevel bus of the device
> +          tree, according to how the BARs are programmed */
> +       start = pci_resource_start(pdev, 0);
> +       len = pci_resource_len(pdev, 0);
> +
> +       dev_printk(KERN_INFO, &pdev->dev,
> +                  "Xilinx PCIPR bridge range %llx %llx",
> +                  (unsigned long long) start, (unsigned long long) len);

dev_info()

> +
> +       ranges_prop = kzalloc(sizeof(struct property), GFP_KERNEL);

if (!ranges_prop) ...

> +       ranges_prop->value = kzalloc(sizeof(unsigned long) * 3, GFP_KERNEL);

if (!ranges_prop->value) ...

> +       ranges_prop->name = "ranges";
> +       ranges_prop->length = sizeof(unsigned long) * 3;
> +       value = (unsigned long *)ranges_prop->value;
> +       /* FIXME: gotta get this from the bridge */
> +       value[0] = cpu_to_be32(0x80000000);
> +       value[1] = cpu_to_be32(start);
> +       value[2] = cpu_to_be32(len);

This won't work if the PCI BARs gets mapped above 4GiB.  It will need
to handle the case where #address-cells and #size-cells on the root
are 2.

> +
> +       unflatten_partial_device_tree((unsigned long 
> *)drvdata->fw_entry->data,
> +                                     &drvdata->child_nodes);
> +       of_node_get(drvdata->child_nodes);
> +       bus_node = of_find_node_by_name(drvdata->child_nodes, "plb");
> +       prom_add_property(bus_node, ranges_prop);

Heh; clever hack.  Not exactly pretty, but it eliminates the need to
have a translation hook and it doesn't look actively dangerous so I'm
okay with it for the time being.  It would be nice to have something
cleaner though.

> +
> +       /* Generate child devices from the device tree */
> +       of_platform_bus_probe(drvdata->child_nodes, NULL, &pdev->dev);
> +
> +       pci_release_regions(pdev);
> +
> +       goto out; /* Success */
> +
> + release:
> +       pci_release_regions(pdev);
> +
> + release_dtb:
> +       release_firmware(drvdata->fw_entry);
> +
> + disable:
> +       pci_disable_device(pdev);
> + free:
> +       kfree(drvdata);
> +       dev_set_drvdata(&pdev->dev, NULL);

set the drvdata to NULL before freeing the pointer.  (probably will
never cause a problem, but it doesn't hurt to be strict).

> + out:
> +       return ret;
> +}
> +
> +static int __devexit xilinx_pcipr_remove(struct pci_dev *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct xilinx_pcipr_drvdata *drvdata = dev_get_drvdata(dev);
> +
> +       /* FIXME: Release the contained devices. There should probably
> +be a device-tree method to free a whole tree of devices, essentially
> +the inverse of of_platform_bus_probe
> +       */

The driver model tracks this for you.  It isn't even device tree
specific.  The solution to this is to walk the children of
pci_dev->dev.  (Actually, not entirely true; as you still need to call
of_put on the device_node before freeing the child device.

> +
> +       release_firmware(drvdata->fw_entry);
> +       pci_disable_device(pdev);
> +       kfree(drvdata);
> +       dev_set_drvdata(dev, NULL);
> +       return 0;
> +}
> +
> +
> +static const struct pci_device_id xilinx_pcipr_ids[] = {
> +       { PCI_VDEVICE(XILINX, 0x0505), 0 },
> +       { 0, },
> +};
> +MODULE_DEVICE_TABLE(pci, xilinx_pcipr_ids);
> +
> +pci_ers_result_t *xilinx_pcipr_error_detected(struct pci_dev *pdev,
> +                                             enum pci_channel_state error) {
> +       dev_printk(KERN_INFO, &pdev->dev, "Error detected!\n");
> +       return PCI_ERS_RESULT_NEED_RESET;
> +}
> +
> +static struct pci_error_handlers xilinx_pcipr_error_handlers = {
> +       /* PCI bus error detected on this device */
> +       .error_detected = xilinx_pcipr_error_detected,
> +};
> +
> +static struct pci_driver  xilinx_pcipr_driver = {
> +       .name           = "XILINX_PCIPR",
> +       .id_table       = xilinx_pcipr_ids,
> +       .probe          = xilinx_pcipr_probe,
> +       .remove         = __devexit_p(xilinx_pcipr_remove),
> +       .err_handler    = &xilinx_pcipr_error_handlers,
> +};
> +
> +static int __init xilinx_pcipr_init(void)
> +{
> +       return ide_pci_register_driver(&xilinx_pcipr_driver);
> +}
> +
> +static void __exit xilinx_pcipr_exit(void)
> +{
> +       pci_unregister_driver(&xilinx_pcipr_driver);
> +}
> +
> +module_init(xilinx_pcipr_init);

module_init should appear immediately after xilinx_pcipr_init()

> +module_exit(xilinx_pcipr_exit);
> +
> +MODULE_AUTHOR("Xilinx Research Labs");
> +MODULE_DESCRIPTION("PCI driver for PCI reconfigurable endpoint");
> +MODULE_LICENSE("GPL");
> --
> 1.5.6.6
>
>
>
> This email and any attachments are intended for the sole use of the named 
> recipient(s) and contain(s) confidential information that may be proprietary, 
> privileged or copyrighted under applicable law. If you are not the intended 
> recipient, do not read, copy, or forward this email message or any 
> attachments. Delete this email message and any attachments immediately.
>
>
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
_______________________________________________
devicetree-discuss mailing list
[email protected]
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to