123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- /*
- * MEN Chameleon Bus.
- *
- * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
- * Author: Johannes Thumshirn <johannes.thumshirn@men.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; version 2 of the License.
- */
- #include <linux/module.h>
- #include <linux/pci.h>
- #include <linux/mcb.h>
- #include "mcb-internal.h"
- struct priv {
- struct mcb_bus *bus;
- phys_addr_t mapbase;
- void __iomem *base;
- };
- static int mcb_pci_get_irq(struct mcb_device *mdev)
- {
- struct mcb_bus *mbus = mdev->bus;
- struct device *dev = mbus->carrier;
- struct pci_dev *pdev = to_pci_dev(dev);
- return pdev->irq;
- }
- static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
- {
- struct resource *res;
- struct priv *priv;
- int ret;
- int num_cells;
- unsigned long flags;
- priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- ret = pci_enable_device(pdev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to enable PCI device\n");
- return -ENODEV;
- }
- priv->mapbase = pci_resource_start(pdev, 0);
- if (!priv->mapbase) {
- dev_err(&pdev->dev, "No PCI resource\n");
- ret = -ENODEV;
- goto out_disable;
- }
- res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE,
- KBUILD_MODNAME);
- if (!res) {
- dev_err(&pdev->dev, "Failed to request PCI memory\n");
- ret = -EBUSY;
- goto out_disable;
- }
- priv->base = ioremap(priv->mapbase, CHAM_HEADER_SIZE);
- if (!priv->base) {
- dev_err(&pdev->dev, "Cannot ioremap\n");
- ret = -ENOMEM;
- goto out_release;
- }
- flags = pci_resource_flags(pdev, 0);
- if (flags & IORESOURCE_IO) {
- ret = -ENOTSUPP;
- dev_err(&pdev->dev,
- "IO mapped PCI devices are not supported\n");
- goto out_iounmap;
- }
- pci_set_drvdata(pdev, priv);
- priv->bus = mcb_alloc_bus(&pdev->dev);
- if (IS_ERR(priv->bus)) {
- ret = PTR_ERR(priv->bus);
- goto out_iounmap;
- }
- priv->bus->get_irq = mcb_pci_get_irq;
- ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base);
- if (ret < 0)
- goto out_mcb_bus;
- num_cells = ret;
- dev_dbg(&pdev->dev, "Found %d cells\n", num_cells);
- mcb_bus_add_devices(priv->bus);
- return 0;
- out_mcb_bus:
- mcb_release_bus(priv->bus);
- out_iounmap:
- iounmap(priv->base);
- out_release:
- pci_release_region(pdev, 0);
- out_disable:
- pci_disable_device(pdev);
- return ret;
- }
- static void mcb_pci_remove(struct pci_dev *pdev)
- {
- struct priv *priv = pci_get_drvdata(pdev);
- mcb_release_bus(priv->bus);
- iounmap(priv->base);
- release_region(priv->mapbase, CHAM_HEADER_SIZE);
- pci_disable_device(pdev);
- }
- static const struct pci_device_id mcb_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
- { 0 },
- };
- MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
- static struct pci_driver mcb_pci_driver = {
- .name = "mcb-pci",
- .id_table = mcb_pci_tbl,
- .probe = mcb_pci_probe,
- .remove = mcb_pci_remove,
- };
- module_pci_driver(mcb_pci_driver);
- MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("MCB over PCI support");
|