1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * CompactPCI Hot Plug Driver PCI functions |
4 | * |
5 | * Copyright (C) 2002,2005 by SOMA Networks, Inc. |
6 | * |
7 | * All rights reserved. |
8 | * |
9 | * Send feedback to <scottm@somanetworks.com> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/pci_hotplug.h> |
16 | #include <linux/proc_fs.h> |
17 | #include "../pci.h" |
18 | #include "cpci_hotplug.h" |
19 | |
20 | #define MY_NAME "cpci_hotplug" |
21 | |
22 | #define dbg(format, arg...) \ |
23 | do { \ |
24 | if (cpci_debug) \ |
25 | printk(KERN_DEBUG "%s: " format "\n", \ |
26 | MY_NAME, ## arg); \ |
27 | } while (0) |
28 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg) |
29 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg) |
30 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg) |
31 | |
32 | |
33 | u8 cpci_get_attention_status(struct slot *slot) |
34 | { |
35 | int hs_cap; |
36 | u16 hs_csr; |
37 | |
38 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
39 | devfn: slot->devfn, |
40 | PCI_CAP_ID_CHSWP); |
41 | if (!hs_cap) |
42 | return 0; |
43 | |
44 | if (pci_bus_read_config_word(bus: slot->bus, |
45 | devfn: slot->devfn, |
46 | where: hs_cap + 2, |
47 | val: &hs_csr)) |
48 | return 0; |
49 | |
50 | return hs_csr & 0x0008 ? 1 : 0; |
51 | } |
52 | |
53 | int cpci_set_attention_status(struct slot *slot, int status) |
54 | { |
55 | int hs_cap; |
56 | u16 hs_csr; |
57 | |
58 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
59 | devfn: slot->devfn, |
60 | PCI_CAP_ID_CHSWP); |
61 | if (!hs_cap) |
62 | return 0; |
63 | if (pci_bus_read_config_word(bus: slot->bus, |
64 | devfn: slot->devfn, |
65 | where: hs_cap + 2, |
66 | val: &hs_csr)) |
67 | return 0; |
68 | if (status) |
69 | hs_csr |= HS_CSR_LOO; |
70 | else |
71 | hs_csr &= ~HS_CSR_LOO; |
72 | if (pci_bus_write_config_word(bus: slot->bus, |
73 | devfn: slot->devfn, |
74 | where: hs_cap + 2, |
75 | val: hs_csr)) |
76 | return 0; |
77 | return 1; |
78 | } |
79 | |
80 | u16 cpci_get_hs_csr(struct slot *slot) |
81 | { |
82 | int hs_cap; |
83 | u16 hs_csr; |
84 | |
85 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
86 | devfn: slot->devfn, |
87 | PCI_CAP_ID_CHSWP); |
88 | if (!hs_cap) |
89 | return 0xFFFF; |
90 | if (pci_bus_read_config_word(bus: slot->bus, |
91 | devfn: slot->devfn, |
92 | where: hs_cap + 2, |
93 | val: &hs_csr)) |
94 | return 0xFFFF; |
95 | return hs_csr; |
96 | } |
97 | |
98 | int cpci_check_and_clear_ins(struct slot *slot) |
99 | { |
100 | int hs_cap; |
101 | u16 hs_csr; |
102 | int ins = 0; |
103 | |
104 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
105 | devfn: slot->devfn, |
106 | PCI_CAP_ID_CHSWP); |
107 | if (!hs_cap) |
108 | return 0; |
109 | if (pci_bus_read_config_word(bus: slot->bus, |
110 | devfn: slot->devfn, |
111 | where: hs_cap + 2, |
112 | val: &hs_csr)) |
113 | return 0; |
114 | if (hs_csr & HS_CSR_INS) { |
115 | /* Clear INS (by setting it) */ |
116 | if (pci_bus_write_config_word(bus: slot->bus, |
117 | devfn: slot->devfn, |
118 | where: hs_cap + 2, |
119 | val: hs_csr)) |
120 | ins = 0; |
121 | else |
122 | ins = 1; |
123 | } |
124 | return ins; |
125 | } |
126 | |
127 | int cpci_check_ext(struct slot *slot) |
128 | { |
129 | int hs_cap; |
130 | u16 hs_csr; |
131 | int ext = 0; |
132 | |
133 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
134 | devfn: slot->devfn, |
135 | PCI_CAP_ID_CHSWP); |
136 | if (!hs_cap) |
137 | return 0; |
138 | if (pci_bus_read_config_word(bus: slot->bus, |
139 | devfn: slot->devfn, |
140 | where: hs_cap + 2, |
141 | val: &hs_csr)) |
142 | return 0; |
143 | if (hs_csr & HS_CSR_EXT) |
144 | ext = 1; |
145 | return ext; |
146 | } |
147 | |
148 | int cpci_clear_ext(struct slot *slot) |
149 | { |
150 | int hs_cap; |
151 | u16 hs_csr; |
152 | |
153 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
154 | devfn: slot->devfn, |
155 | PCI_CAP_ID_CHSWP); |
156 | if (!hs_cap) |
157 | return -ENODEV; |
158 | if (pci_bus_read_config_word(bus: slot->bus, |
159 | devfn: slot->devfn, |
160 | where: hs_cap + 2, |
161 | val: &hs_csr)) |
162 | return -ENODEV; |
163 | if (hs_csr & HS_CSR_EXT) { |
164 | /* Clear EXT (by setting it) */ |
165 | if (pci_bus_write_config_word(bus: slot->bus, |
166 | devfn: slot->devfn, |
167 | where: hs_cap + 2, |
168 | val: hs_csr)) |
169 | return -ENODEV; |
170 | } |
171 | return 0; |
172 | } |
173 | |
174 | int cpci_led_on(struct slot *slot) |
175 | { |
176 | int hs_cap; |
177 | u16 hs_csr; |
178 | |
179 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
180 | devfn: slot->devfn, |
181 | PCI_CAP_ID_CHSWP); |
182 | if (!hs_cap) |
183 | return -ENODEV; |
184 | if (pci_bus_read_config_word(bus: slot->bus, |
185 | devfn: slot->devfn, |
186 | where: hs_cap + 2, |
187 | val: &hs_csr)) |
188 | return -ENODEV; |
189 | if ((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { |
190 | hs_csr |= HS_CSR_LOO; |
191 | if (pci_bus_write_config_word(bus: slot->bus, |
192 | devfn: slot->devfn, |
193 | where: hs_cap + 2, |
194 | val: hs_csr)) { |
195 | err("Could not set LOO for slot %s" , slot_name(slot)); |
196 | return -ENODEV; |
197 | } |
198 | } |
199 | return 0; |
200 | } |
201 | |
202 | int cpci_led_off(struct slot *slot) |
203 | { |
204 | int hs_cap; |
205 | u16 hs_csr; |
206 | |
207 | hs_cap = pci_bus_find_capability(bus: slot->bus, |
208 | devfn: slot->devfn, |
209 | PCI_CAP_ID_CHSWP); |
210 | if (!hs_cap) |
211 | return -ENODEV; |
212 | if (pci_bus_read_config_word(bus: slot->bus, |
213 | devfn: slot->devfn, |
214 | where: hs_cap + 2, |
215 | val: &hs_csr)) |
216 | return -ENODEV; |
217 | if (hs_csr & HS_CSR_LOO) { |
218 | hs_csr &= ~HS_CSR_LOO; |
219 | if (pci_bus_write_config_word(bus: slot->bus, |
220 | devfn: slot->devfn, |
221 | where: hs_cap + 2, |
222 | val: hs_csr)) { |
223 | err("Could not clear LOO for slot %s" , slot_name(slot)); |
224 | return -ENODEV; |
225 | } |
226 | } |
227 | return 0; |
228 | } |
229 | |
230 | |
231 | /* |
232 | * Device configuration functions |
233 | */ |
234 | |
235 | int cpci_configure_slot(struct slot *slot) |
236 | { |
237 | struct pci_dev *dev; |
238 | struct pci_bus *parent; |
239 | int ret = 0; |
240 | |
241 | dbg("%s - enter" , __func__); |
242 | |
243 | pci_lock_rescan_remove(); |
244 | |
245 | if (slot->dev == NULL) { |
246 | dbg("pci_dev null, finding %02x:%02x:%x" , |
247 | slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); |
248 | slot->dev = pci_get_slot(bus: slot->bus, devfn: slot->devfn); |
249 | } |
250 | |
251 | /* Still NULL? Well then scan for it! */ |
252 | if (slot->dev == NULL) { |
253 | int n; |
254 | dbg("pci_dev still null" ); |
255 | |
256 | /* |
257 | * This will generate pci_dev structures for all functions, but |
258 | * we will only call this case when lookup fails. |
259 | */ |
260 | n = pci_scan_slot(bus: slot->bus, devfn: slot->devfn); |
261 | dbg("%s: pci_scan_slot returned %d" , __func__, n); |
262 | slot->dev = pci_get_slot(bus: slot->bus, devfn: slot->devfn); |
263 | if (slot->dev == NULL) { |
264 | err("Could not find PCI device for slot %02x" , slot->number); |
265 | ret = -ENODEV; |
266 | goto out; |
267 | } |
268 | } |
269 | parent = slot->dev->bus; |
270 | |
271 | for_each_pci_bridge(dev, parent) { |
272 | if (PCI_SLOT(dev->devfn) == PCI_SLOT(slot->devfn)) |
273 | pci_hp_add_bridge(dev); |
274 | } |
275 | |
276 | pci_assign_unassigned_bridge_resources(bridge: parent->self); |
277 | |
278 | pci_bus_add_devices(bus: parent); |
279 | |
280 | out: |
281 | pci_unlock_rescan_remove(); |
282 | dbg("%s - exit" , __func__); |
283 | return ret; |
284 | } |
285 | |
286 | int cpci_unconfigure_slot(struct slot *slot) |
287 | { |
288 | struct pci_dev *dev, *temp; |
289 | |
290 | dbg("%s - enter" , __func__); |
291 | if (!slot->dev) { |
292 | err("No device for slot %02x\n" , slot->number); |
293 | return -ENODEV; |
294 | } |
295 | |
296 | pci_lock_rescan_remove(); |
297 | |
298 | list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) { |
299 | if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) |
300 | continue; |
301 | pci_dev_get(dev); |
302 | pci_stop_and_remove_bus_device(dev); |
303 | pci_dev_put(dev); |
304 | } |
305 | pci_dev_put(dev: slot->dev); |
306 | slot->dev = NULL; |
307 | |
308 | pci_unlock_rescan_remove(); |
309 | |
310 | dbg("%s - exit" , __func__); |
311 | return 0; |
312 | } |
313 | |