162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MEN Chameleon Bus. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) 662306a36Sopenharmony_ci * Author: Johannes Thumshirn <johannes.thumshirn@men.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/mcb.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "mcb-internal.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct priv { 1662306a36Sopenharmony_ci struct mcb_bus *bus; 1762306a36Sopenharmony_ci phys_addr_t mapbase; 1862306a36Sopenharmony_ci void __iomem *base; 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int mcb_pci_get_irq(struct mcb_device *mdev) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct mcb_bus *mbus = mdev->bus; 2462306a36Sopenharmony_ci struct device *dev = mbus->carrier; 2562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return pdev->irq; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct resource *res; 3362306a36Sopenharmony_ci struct priv *priv; 3462306a36Sopenharmony_ci int ret, table_size; 3562306a36Sopenharmony_ci unsigned long flags; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL); 3862306a36Sopenharmony_ci if (!priv) 3962306a36Sopenharmony_ci return -ENOMEM; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci ret = pci_enable_device(pdev); 4262306a36Sopenharmony_ci if (ret) { 4362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PCI device\n"); 4462306a36Sopenharmony_ci return -ENODEV; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci pci_set_master(pdev); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci priv->mapbase = pci_resource_start(pdev, 0); 4962306a36Sopenharmony_ci if (!priv->mapbase) { 5062306a36Sopenharmony_ci dev_err(&pdev->dev, "No PCI resource\n"); 5162306a36Sopenharmony_ci ret = -ENODEV; 5262306a36Sopenharmony_ci goto out_disable; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci res = devm_request_mem_region(&pdev->dev, priv->mapbase, 5662306a36Sopenharmony_ci CHAM_HEADER_SIZE, 5762306a36Sopenharmony_ci KBUILD_MODNAME); 5862306a36Sopenharmony_ci if (!res) { 5962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request PCI memory\n"); 6062306a36Sopenharmony_ci ret = -EBUSY; 6162306a36Sopenharmony_ci goto out_disable; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci priv->base = devm_ioremap(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE); 6562306a36Sopenharmony_ci if (!priv->base) { 6662306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap\n"); 6762306a36Sopenharmony_ci ret = -ENOMEM; 6862306a36Sopenharmony_ci goto out_disable; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci flags = pci_resource_flags(pdev, 0); 7262306a36Sopenharmony_ci if (flags & IORESOURCE_IO) { 7362306a36Sopenharmony_ci ret = -ENOTSUPP; 7462306a36Sopenharmony_ci dev_err(&pdev->dev, 7562306a36Sopenharmony_ci "IO mapped PCI devices are not supported\n"); 7662306a36Sopenharmony_ci goto out_disable; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pci_set_drvdata(pdev, priv); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci priv->bus = mcb_alloc_bus(&pdev->dev); 8262306a36Sopenharmony_ci if (IS_ERR(priv->bus)) { 8362306a36Sopenharmony_ci ret = PTR_ERR(priv->bus); 8462306a36Sopenharmony_ci goto out_disable; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci priv->bus->get_irq = mcb_pci_get_irq; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base); 9062306a36Sopenharmony_ci if (ret < 0) 9162306a36Sopenharmony_ci goto out_mcb_bus; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci table_size = ret; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (table_size < CHAM_HEADER_SIZE) { 9662306a36Sopenharmony_ci /* Release the previous resources */ 9762306a36Sopenharmony_ci devm_iounmap(&pdev->dev, priv->base); 9862306a36Sopenharmony_ci devm_release_mem_region(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Then, allocate it again with the actual chameleon table size */ 10162306a36Sopenharmony_ci res = devm_request_mem_region(&pdev->dev, priv->mapbase, 10262306a36Sopenharmony_ci table_size, 10362306a36Sopenharmony_ci KBUILD_MODNAME); 10462306a36Sopenharmony_ci if (!res) { 10562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request PCI memory\n"); 10662306a36Sopenharmony_ci ret = -EBUSY; 10762306a36Sopenharmony_ci goto out_mcb_bus; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci priv->base = devm_ioremap(&pdev->dev, priv->mapbase, table_size); 11162306a36Sopenharmony_ci if (!priv->base) { 11262306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap\n"); 11362306a36Sopenharmony_ci ret = -ENOMEM; 11462306a36Sopenharmony_ci goto out_mcb_bus; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mcb_bus_add_devices(priv->bus); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciout_mcb_bus: 12362306a36Sopenharmony_ci mcb_release_bus(priv->bus); 12462306a36Sopenharmony_ciout_disable: 12562306a36Sopenharmony_ci pci_disable_device(pdev); 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void mcb_pci_remove(struct pci_dev *pdev) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct priv *priv = pci_get_drvdata(pdev); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci mcb_release_bus(priv->bus); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci pci_disable_device(pdev); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct pci_device_id mcb_pci_tbl[] = { 13962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) }, 14062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) }, 14162306a36Sopenharmony_ci { 0 }, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mcb_pci_tbl); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic struct pci_driver mcb_pci_driver = { 14662306a36Sopenharmony_ci .name = "mcb-pci", 14762306a36Sopenharmony_ci .id_table = mcb_pci_tbl, 14862306a36Sopenharmony_ci .probe = mcb_pci_probe, 14962306a36Sopenharmony_ci .remove = mcb_pci_remove, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cimodule_pci_driver(mcb_pci_driver); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciMODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); 15562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 15662306a36Sopenharmony_ciMODULE_DESCRIPTION("MCB over PCI support"); 15762306a36Sopenharmony_ciMODULE_IMPORT_NS(MCB); 158