xref: /openbmc/linux/drivers/mcb/mcb-pci.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
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  
15  struct priv {
16  	struct mcb_bus *bus;
17  	phys_addr_t mapbase;
18  	void __iomem *base;
19  };
20  
mcb_pci_get_irq(struct mcb_device * mdev)21  static 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  
mcb_pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)30  static 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  	priv->mapbase = pci_resource_start(pdev, 0);
49  	if (!priv->mapbase) {
50  		dev_err(&pdev->dev, "No PCI resource\n");
51  		ret = -ENODEV;
52  		goto out_disable;
53  	}
54  
55  	res = devm_request_mem_region(&pdev->dev, priv->mapbase,
56  				      CHAM_HEADER_SIZE,
57  				      KBUILD_MODNAME);
58  	if (!res) {
59  		dev_err(&pdev->dev, "Failed to request PCI memory\n");
60  		ret = -EBUSY;
61  		goto out_disable;
62  	}
63  
64  	priv->base = devm_ioremap(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE);
65  	if (!priv->base) {
66  		dev_err(&pdev->dev, "Cannot ioremap\n");
67  		ret = -ENOMEM;
68  		goto out_disable;
69  	}
70  
71  	flags = pci_resource_flags(pdev, 0);
72  	if (flags & IORESOURCE_IO) {
73  		ret = -ENOTSUPP;
74  		dev_err(&pdev->dev,
75  			"IO mapped PCI devices are not supported\n");
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  
122  out_mcb_bus:
123  	mcb_release_bus(priv->bus);
124  out_disable:
125  	pci_disable_device(pdev);
126  	return ret;
127  }
128  
mcb_pci_remove(struct pci_dev * pdev)129  static 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  
138  static 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  };
143  MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
144  
145  static 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  
152  module_pci_driver(mcb_pci_driver);
153  
154  MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
155  MODULE_LICENSE("GPL");
156  MODULE_DESCRIPTION("MCB over PCI support");
157  MODULE_IMPORT_NS(MCB);
158