158 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * MEN Chameleon Bus.4 *5 * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)6 * Author: Johannes Thumshirn <johannes.thumshirn@men.de>7 */8 9#include <linux/module.h>10#include <linux/pci.h>11#include <linux/mcb.h>12 13#include "mcb-internal.h"14 15struct priv {16 struct mcb_bus *bus;17 phys_addr_t mapbase;18 void __iomem *base;19};20 21static int mcb_pci_get_irq(struct mcb_device *mdev)22{23 struct mcb_bus *mbus = mdev->bus;24 struct device *dev = mbus->carrier;25 struct pci_dev *pdev = to_pci_dev(dev);26 27 return pdev->irq;28}29 30static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)31{32 struct resource *res;33 struct priv *priv;34 int ret, table_size;35 unsigned long flags;36 37 priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL);38 if (!priv)39 return -ENOMEM;40 41 ret = pci_enable_device(pdev);42 if (ret) {43 dev_err(&pdev->dev, "Failed to enable PCI device\n");44 return -ENODEV;45 }46 pci_set_master(pdev);47 48 flags = pci_resource_flags(pdev, 0);49 if (flags & IORESOURCE_IO) {50 ret = -ENOTSUPP;51 dev_err(&pdev->dev,52 "IO mapped PCI devices are not supported\n");53 goto out_disable;54 }55 56 priv->mapbase = pci_resource_start(pdev, 0);57 if (!priv->mapbase) {58 dev_err(&pdev->dev, "No PCI resource\n");59 ret = -ENODEV;60 goto out_disable;61 }62 63 res = devm_request_mem_region(&pdev->dev, priv->mapbase,64 CHAM_HEADER_SIZE,65 KBUILD_MODNAME);66 if (!res) {67 dev_err(&pdev->dev, "Failed to request PCI memory\n");68 ret = -EBUSY;69 goto out_disable;70 }71 72 priv->base = devm_ioremap(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE);73 if (!priv->base) {74 dev_err(&pdev->dev, "Cannot ioremap\n");75 ret = -ENOMEM;76 goto out_disable;77 }78 79 pci_set_drvdata(pdev, priv);80 81 priv->bus = mcb_alloc_bus(&pdev->dev);82 if (IS_ERR(priv->bus)) {83 ret = PTR_ERR(priv->bus);84 goto out_disable;85 }86 87 priv->bus->get_irq = mcb_pci_get_irq;88 89 ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base);90 if (ret < 0)91 goto out_mcb_bus;92 93 table_size = ret;94 95 if (table_size < CHAM_HEADER_SIZE) {96 /* Release the previous resources */97 devm_iounmap(&pdev->dev, priv->base);98 devm_release_mem_region(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE);99 100 /* Then, allocate it again with the actual chameleon table size */101 res = devm_request_mem_region(&pdev->dev, priv->mapbase,102 table_size,103 KBUILD_MODNAME);104 if (!res) {105 dev_err(&pdev->dev, "Failed to request PCI memory\n");106 ret = -EBUSY;107 goto out_mcb_bus;108 }109 110 priv->base = devm_ioremap(&pdev->dev, priv->mapbase, table_size);111 if (!priv->base) {112 dev_err(&pdev->dev, "Cannot ioremap\n");113 ret = -ENOMEM;114 goto out_mcb_bus;115 }116 }117 118 mcb_bus_add_devices(priv->bus);119 120 return 0;121 122out_mcb_bus:123 mcb_release_bus(priv->bus);124out_disable:125 pci_disable_device(pdev);126 return ret;127}128 129static void mcb_pci_remove(struct pci_dev *pdev)130{131 struct priv *priv = pci_get_drvdata(pdev);132 133 mcb_release_bus(priv->bus);134 135 pci_disable_device(pdev);136}137 138static const struct pci_device_id mcb_pci_tbl[] = {139 { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },140 { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) },141 { 0 },142};143MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);144 145static struct pci_driver mcb_pci_driver = {146 .name = "mcb-pci",147 .id_table = mcb_pci_tbl,148 .probe = mcb_pci_probe,149 .remove = mcb_pci_remove,150};151 152module_pci_driver(mcb_pci_driver);153 154MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");155MODULE_LICENSE("GPL");156MODULE_DESCRIPTION("MCB over PCI support");157MODULE_IMPORT_NS(MCB);158