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