18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MEN Chameleon Bus. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) 68c2ecf20Sopenharmony_ci * Author: Johannes Thumshirn <johannes.thumshirn@men.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/mcb.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "mcb-internal.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct priv { 168c2ecf20Sopenharmony_ci struct mcb_bus *bus; 178c2ecf20Sopenharmony_ci phys_addr_t mapbase; 188c2ecf20Sopenharmony_ci void __iomem *base; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int mcb_pci_get_irq(struct mcb_device *mdev) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct mcb_bus *mbus = mdev->bus; 248c2ecf20Sopenharmony_ci struct device *dev = mbus->carrier; 258c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci return pdev->irq; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct resource *res; 338c2ecf20Sopenharmony_ci struct priv *priv; 348c2ecf20Sopenharmony_ci int ret, table_size; 358c2ecf20Sopenharmony_ci unsigned long flags; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!priv) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 428c2ecf20Sopenharmony_ci if (ret) { 438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PCI device\n"); 448c2ecf20Sopenharmony_ci return -ENODEV; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci pci_set_master(pdev); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci priv->mapbase = pci_resource_start(pdev, 0); 498c2ecf20Sopenharmony_ci if (!priv->mapbase) { 508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No PCI resource\n"); 518c2ecf20Sopenharmony_ci ret = -ENODEV; 528c2ecf20Sopenharmony_ci goto out_disable; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci res = devm_request_mem_region(&pdev->dev, priv->mapbase, 568c2ecf20Sopenharmony_ci CHAM_HEADER_SIZE, 578c2ecf20Sopenharmony_ci KBUILD_MODNAME); 588c2ecf20Sopenharmony_ci if (!res) { 598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request PCI memory\n"); 608c2ecf20Sopenharmony_ci ret = -EBUSY; 618c2ecf20Sopenharmony_ci goto out_disable; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci priv->base = devm_ioremap(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE); 658c2ecf20Sopenharmony_ci if (!priv->base) { 668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap\n"); 678c2ecf20Sopenharmony_ci ret = -ENOMEM; 688c2ecf20Sopenharmony_ci goto out_disable; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci flags = pci_resource_flags(pdev, 0); 728c2ecf20Sopenharmony_ci if (flags & IORESOURCE_IO) { 738c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 758c2ecf20Sopenharmony_ci "IO mapped PCI devices are not supported\n"); 768c2ecf20Sopenharmony_ci goto out_disable; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, priv); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci priv->bus = mcb_alloc_bus(&pdev->dev); 828c2ecf20Sopenharmony_ci if (IS_ERR(priv->bus)) { 838c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->bus); 848c2ecf20Sopenharmony_ci goto out_disable; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci priv->bus->get_irq = mcb_pci_get_irq; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base); 908c2ecf20Sopenharmony_ci if (ret < 0) 918c2ecf20Sopenharmony_ci goto out_mcb_bus; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci table_size = ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (table_size < CHAM_HEADER_SIZE) { 968c2ecf20Sopenharmony_ci /* Release the previous resources */ 978c2ecf20Sopenharmony_ci devm_iounmap(&pdev->dev, priv->base); 988c2ecf20Sopenharmony_ci devm_release_mem_region(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Then, allocate it again with the actual chameleon table size */ 1018c2ecf20Sopenharmony_ci res = devm_request_mem_region(&pdev->dev, priv->mapbase, 1028c2ecf20Sopenharmony_ci table_size, 1038c2ecf20Sopenharmony_ci KBUILD_MODNAME); 1048c2ecf20Sopenharmony_ci if (!res) { 1058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request PCI memory\n"); 1068c2ecf20Sopenharmony_ci ret = -EBUSY; 1078c2ecf20Sopenharmony_ci goto out_mcb_bus; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci priv->base = devm_ioremap(&pdev->dev, priv->mapbase, table_size); 1118c2ecf20Sopenharmony_ci if (!priv->base) { 1128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap\n"); 1138c2ecf20Sopenharmony_ci ret = -ENOMEM; 1148c2ecf20Sopenharmony_ci goto out_mcb_bus; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci mcb_bus_add_devices(priv->bus); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciout_mcb_bus: 1238c2ecf20Sopenharmony_ci mcb_release_bus(priv->bus); 1248c2ecf20Sopenharmony_ciout_disable: 1258c2ecf20Sopenharmony_ci pci_disable_device(pdev); 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void mcb_pci_remove(struct pci_dev *pdev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct priv *priv = pci_get_drvdata(pdev); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci mcb_release_bus(priv->bus); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci pci_disable_device(pdev); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct pci_device_id mcb_pci_tbl[] = { 1398c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) }, 1408c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) }, 1418c2ecf20Sopenharmony_ci { 0 }, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mcb_pci_tbl); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic struct pci_driver mcb_pci_driver = { 1468c2ecf20Sopenharmony_ci .name = "mcb-pci", 1478c2ecf20Sopenharmony_ci .id_table = mcb_pci_tbl, 1488c2ecf20Sopenharmony_ci .probe = mcb_pci_probe, 1498c2ecf20Sopenharmony_ci .remove = mcb_pci_remove, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cimodule_pci_driver(mcb_pci_driver); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); 1558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MCB over PCI support"); 1578c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(MCB); 158