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 			    slot->hotplug_slot->name);
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 			    slot->hotplug_slot->name);
242 			return -ENODEV;
243 		}
244 	}
245 	return 0;
246 }
247 
248 
249 /*
250  * Device configuration functions
251  */
252 
253 int __ref cpci_configure_slot(struct slot *slot)
254 {
255 	struct pci_bus *parent;
256 	int fn;
257 
258 	dbg("%s - enter", __func__);
259 
260 	if (slot->dev == NULL) {
261 		dbg("pci_dev null, finding %02x:%02x:%x",
262 		    slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
263 		slot->dev = pci_get_slot(slot->bus, slot->devfn);
264 	}
265 
266 	/* Still NULL? Well then scan for it! */
267 	if (slot->dev == NULL) {
268 		int n;
269 		dbg("pci_dev still null");
270 
271 		/*
272 		 * This will generate pci_dev structures for all functions, but
273 		 * we will only call this case when lookup fails.
274 		 */
275 		n = pci_scan_slot(slot->bus, slot->devfn);
276 		dbg("%s: pci_scan_slot returned %d", __func__, n);
277 		slot->dev = pci_get_slot(slot->bus, slot->devfn);
278 		if (slot->dev == NULL) {
279 			err("Could not find PCI device for slot %02x", slot->number);
280 			return -ENODEV;
281 		}
282 	}
283 	parent = slot->dev->bus;
284 
285 	for (fn = 0; fn < 8; fn++) {
286 		struct pci_dev *dev;
287 
288 		dev = pci_get_slot(parent, PCI_DEVFN(PCI_SLOT(slot->devfn), fn));
289 		if (!dev)
290 			continue;
291 		if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
292 		    (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
293 			/* Find an unused bus number for the new bridge */
294 			struct pci_bus *child;
295 			unsigned char busnr, start = parent->secondary;
296 			unsigned char end = parent->subordinate;
297 
298 			for (busnr = start; busnr <= end; busnr++) {
299 				if (!pci_find_bus(pci_domain_nr(parent),
300 						  busnr))
301 					break;
302 			}
303 			if (busnr >= end) {
304 				err("No free bus for hot-added bridge\n");
305 				pci_dev_put(dev);
306 				continue;
307 			}
308 			child = pci_add_new_bus(parent, dev, busnr);
309 			if (!child) {
310 				err("Cannot add new bus for %s\n",
311 				    pci_name(dev));
312 				pci_dev_put(dev);
313 				continue;
314 			}
315 			child->subordinate = pci_do_scan_bus(child);
316 			pci_bus_size_bridges(child);
317 		}
318 		pci_dev_put(dev);
319 	}
320 
321 	pci_bus_assign_resources(parent);
322 	pci_bus_add_devices(parent);
323 	pci_enable_bridges(parent);
324 
325 	dbg("%s - exit", __func__);
326 	return 0;
327 }
328 
329 int cpci_unconfigure_slot(struct slot* slot)
330 {
331 	int i;
332 	struct pci_dev *dev;
333 
334 	dbg("%s - enter", __func__);
335 	if (!slot->dev) {
336 		err("No device for slot %02x\n", slot->number);
337 		return -ENODEV;
338 	}
339 
340 	for (i = 0; i < 8; i++) {
341 		dev = pci_get_slot(slot->bus,
342 				    PCI_DEVFN(PCI_SLOT(slot->devfn), i));
343 		if (dev) {
344 			pci_remove_bus_device(dev);
345 			pci_dev_put(dev);
346 		}
347 	}
348 	pci_dev_put(slot->dev);
349 	slot->dev = NULL;
350 
351 	dbg("%s - exit", __func__);
352 	return 0;
353 }
354