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