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