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
22extern 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
35u8 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
55int 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
82u16 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
100int 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
129int 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
150int 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
176int 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", slot_name(slot));
198			return -ENODEV;
199		}
200	}
201	return 0;
202}
203
204int cpci_led_off(struct slot *slot)
205{
206	int hs_cap;
207	u16 hs_csr;
208
209	hs_cap = pci_bus_find_capability(slot->bus,
210					 slot->devfn,
211					 PCI_CAP_ID_CHSWP);
212	if (!hs_cap)
213		return -ENODEV;
214	if (pci_bus_read_config_word(slot->bus,
215				     slot->devfn,
216				     hs_cap + 2,
217				     &hs_csr))
218		return -ENODEV;
219	if (hs_csr & HS_CSR_LOO) {
220		hs_csr &= ~HS_CSR_LOO;
221		if (pci_bus_write_config_word(slot->bus,
222					      slot->devfn,
223					      hs_cap + 2,
224					      hs_csr)) {
225			err("Could not clear LOO for slot %s", slot_name(slot));
226			return -ENODEV;
227		}
228	}
229	return 0;
230}
231
232
233/*
234 * Device configuration functions
235 */
236
237int cpci_configure_slot(struct slot *slot)
238{
239	struct pci_dev *dev;
240	struct pci_bus *parent;
241	int ret = 0;
242
243	dbg("%s - enter", __func__);
244
245	pci_lock_rescan_remove();
246
247	if (slot->dev == NULL) {
248		dbg("pci_dev null, finding %02x:%02x:%x",
249		    slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
250		slot->dev = pci_get_slot(slot->bus, slot->devfn);
251	}
252
253	/* Still NULL? Well then scan for it! */
254	if (slot->dev == NULL) {
255		int n;
256		dbg("pci_dev still null");
257
258		/*
259		 * This will generate pci_dev structures for all functions, but
260		 * we will only call this case when lookup fails.
261		 */
262		n = pci_scan_slot(slot->bus, slot->devfn);
263		dbg("%s: pci_scan_slot returned %d", __func__, n);
264		slot->dev = pci_get_slot(slot->bus, slot->devfn);
265		if (slot->dev == NULL) {
266			err("Could not find PCI device for slot %02x", slot->number);
267			ret = -ENODEV;
268			goto out;
269		}
270	}
271	parent = slot->dev->bus;
272
273	for_each_pci_bridge(dev, parent) {
274		if (PCI_SLOT(dev->devfn) == PCI_SLOT(slot->devfn))
275			pci_hp_add_bridge(dev);
276	}
277
278	pci_assign_unassigned_bridge_resources(parent->self);
279
280	pci_bus_add_devices(parent);
281
282 out:
283	pci_unlock_rescan_remove();
284	dbg("%s - exit", __func__);
285	return ret;
286}
287
288int cpci_unconfigure_slot(struct slot *slot)
289{
290	struct pci_dev *dev, *temp;
291
292	dbg("%s - enter", __func__);
293	if (!slot->dev) {
294		err("No device for slot %02x\n", slot->number);
295		return -ENODEV;
296	}
297
298	pci_lock_rescan_remove();
299
300	list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) {
301		if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
302			continue;
303		pci_dev_get(dev);
304		pci_stop_and_remove_bus_device(dev);
305		pci_dev_put(dev);
306	}
307	pci_dev_put(slot->dev);
308	slot->dev = NULL;
309
310	pci_unlock_rescan_remove();
311
312	dbg("%s - exit", __func__);
313	return 0;
314}
315