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 __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,
289 				   PCI_DEVFN(PCI_SLOT(slot->devfn), fn));
290 		if (!dev)
291 			continue;
292 		if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
293 		    (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS))
294 			pci_hp_add_bridge(dev);
295 		pci_dev_put(dev);
296 	}
297 
298 	pci_assign_unassigned_bridge_resources(parent->self);
299 
300 	pci_bus_add_devices(parent);
301 
302 	dbg("%s - exit", __func__);
303 	return 0;
304 }
305 
306 int cpci_unconfigure_slot(struct slot* slot)
307 {
308 	int i;
309 	struct pci_dev *dev;
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 	for (i = 0; i < 8; i++) {
318 		dev = pci_get_slot(slot->bus,
319 				    PCI_DEVFN(PCI_SLOT(slot->devfn), i));
320 		if (dev) {
321 			pci_stop_and_remove_bus_device(dev);
322 			pci_dev_put(dev);
323 		}
324 	}
325 	pci_dev_put(slot->dev);
326 	slot->dev = NULL;
327 
328 	dbg("%s - exit", __func__);
329 	return 0;
330 }
331