xref: /kernel/linux/linux-5.10/drivers/mcb/mcb-pci.c (revision 8c2ecf20)
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