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 | |
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 | |
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(dev: &pdev->dev, size: sizeof(struct priv), GFP_KERNEL); |
38 | if (!priv) |
39 | return -ENOMEM; |
40 | |
41 | ret = pci_enable_device(dev: pdev); |
42 | if (ret) { |
43 | dev_err(&pdev->dev, "Failed to enable PCI device\n" ); |
44 | return -ENODEV; |
45 | } |
46 | pci_set_master(dev: 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(dev: &pdev->dev, offset: 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, data: priv); |
80 | |
81 | priv->bus = mcb_alloc_bus(carrier: &pdev->dev); |
82 | if (IS_ERR(ptr: priv->bus)) { |
83 | ret = PTR_ERR(ptr: priv->bus); |
84 | goto out_disable; |
85 | } |
86 | |
87 | priv->bus->get_irq = mcb_pci_get_irq; |
88 | |
89 | ret = chameleon_parse_cells(bus: priv->bus, mapbase: priv->mapbase, base: 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(dev: &pdev->dev, addr: 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(dev: &pdev->dev, offset: priv->mapbase, size: 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(bus: priv->bus); |
119 | |
120 | return 0; |
121 | |
122 | out_mcb_bus: |
123 | mcb_release_bus(bus: priv->bus); |
124 | out_disable: |
125 | pci_disable_device(dev: pdev); |
126 | return ret; |
127 | } |
128 | |
129 | static void mcb_pci_remove(struct pci_dev *pdev) |
130 | { |
131 | struct priv *priv = pci_get_drvdata(pdev); |
132 | |
133 | mcb_release_bus(bus: priv->bus); |
134 | |
135 | pci_disable_device(dev: 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 | |