diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/pcie |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/Kconfig | 36 | ||||
-rw-r--r-- | drivers/pci/pcie/Makefile | 7 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv.h | 41 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_bus.c | 77 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 434 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 122 |
6 files changed, 717 insertions, 0 deletions
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig new file mode 100644 index 000000000000..1012db8b8b2c --- /dev/null +++ b/drivers/pci/pcie/Kconfig | |||
@@ -0,0 +1,36 @@ | |||
1 | # | ||
2 | # PCI Express Port Bus Configuration | ||
3 | # | ||
4 | config PCIEPORTBUS | ||
5 | bool "PCI Express support" | ||
6 | depends on PCI | ||
7 | help | ||
8 | This automatically enables PCI Express Port Bus support. Users can | ||
9 | choose Native Hot-Plug support, Advanced Error Reporting support, | ||
10 | Power Management Event support and Virtual Channel support to run | ||
11 | on PCI Express Ports (Root or Switch). | ||
12 | |||
13 | # | ||
14 | # Include service Kconfig here | ||
15 | # | ||
16 | config HOTPLUG_PCI_PCIE | ||
17 | tristate "PCI Express Hotplug driver" | ||
18 | depends on HOTPLUG_PCI && PCIEPORTBUS | ||
19 | help | ||
20 | Say Y here if you have a motherboard that supports PCI Express Native | ||
21 | Hotplug | ||
22 | |||
23 | To compile this driver as a module, choose M here: the | ||
24 | module will be called pciehp. | ||
25 | |||
26 | When in doubt, say N. | ||
27 | |||
28 | config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE | ||
29 | bool "Use polling mechanism for hot-plug events (for testing purpose)" | ||
30 | depends on HOTPLUG_PCI_PCIE | ||
31 | help | ||
32 | Say Y here if you want to use the polling mechanism for hot-plug | ||
33 | events for early platform testing. | ||
34 | |||
35 | When in doubt, say N. | ||
36 | |||
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile new file mode 100644 index 000000000000..984fa87283e3 --- /dev/null +++ b/drivers/pci/pcie/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for PCI-Express PORT Driver | ||
3 | # | ||
4 | |||
5 | pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o | ||
6 | |||
7 | obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o | ||
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h new file mode 100644 index 000000000000..6c3ad8918667 --- /dev/null +++ b/drivers/pci/pcie/portdrv.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * File: portdrv.h | ||
3 | * Purpose: PCI Express Port Bus Driver's Internal Data Structures | ||
4 | * | ||
5 | * Copyright (C) 2004 Intel | ||
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
7 | */ | ||
8 | |||
9 | #ifndef _PORTDRV_H_ | ||
10 | #define _PORTDRV_H_ | ||
11 | |||
12 | #if !defined(PCI_CAP_ID_PME) | ||
13 | #define PCI_CAP_ID_PME 1 | ||
14 | #endif | ||
15 | |||
16 | #if !defined(PCI_CAP_ID_EXP) | ||
17 | #define PCI_CAP_ID_EXP 0x10 | ||
18 | #endif | ||
19 | |||
20 | #define PORT_TYPE_MASK 0xf | ||
21 | #define PORT_TO_SLOT_MASK 0x100 | ||
22 | #define SLOT_HP_CAPABLE_MASK 0x40 | ||
23 | #define PCIE_CAPABILITIES_REG 0x2 | ||
24 | #define PCIE_SLOT_CAPABILITIES_REG 0x14 | ||
25 | #define PCIE_PORT_DEVICE_MAXSERVICES 4 | ||
26 | #define PCI_CFG_SPACE_SIZE 256 | ||
27 | |||
28 | #define get_descriptor_id(type, service) (((type - 4) << 4) | service) | ||
29 | |||
30 | extern struct bus_type pcie_port_bus_type; | ||
31 | extern int pcie_port_device_probe(struct pci_dev *dev); | ||
32 | extern int pcie_port_device_register(struct pci_dev *dev); | ||
33 | #ifdef CONFIG_PM | ||
34 | extern int pcie_port_device_suspend(struct pci_dev *dev, u32 state); | ||
35 | extern int pcie_port_device_resume(struct pci_dev *dev); | ||
36 | #endif | ||
37 | extern void pcie_port_device_remove(struct pci_dev *dev); | ||
38 | extern void pcie_port_bus_register(void); | ||
39 | extern void pcie_port_bus_unregister(void); | ||
40 | |||
41 | #endif /* _PORTDRV_H_ */ | ||
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c new file mode 100644 index 000000000000..9a02f28ed05f --- /dev/null +++ b/drivers/pci/pcie/portdrv_bus.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * File: portdrv_bus.c | ||
3 | * Purpose: PCI Express Port Bus Driver's Bus Overloading Functions | ||
4 | * | ||
5 | * Copyright (C) 2004 Intel | ||
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/pm.h> | ||
14 | |||
15 | #include <linux/pcieport_if.h> | ||
16 | |||
17 | static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); | ||
18 | static int pcie_port_bus_suspend(struct device *dev, u32 state); | ||
19 | static int pcie_port_bus_resume(struct device *dev); | ||
20 | |||
21 | struct bus_type pcie_port_bus_type = { | ||
22 | .name = "pci_express", | ||
23 | .match = pcie_port_bus_match, | ||
24 | .suspend = pcie_port_bus_suspend, | ||
25 | .resume = pcie_port_bus_resume, | ||
26 | }; | ||
27 | |||
28 | static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) | ||
29 | { | ||
30 | struct pcie_device *pciedev; | ||
31 | struct pcie_port_service_driver *driver; | ||
32 | |||
33 | if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) | ||
34 | return 0; | ||
35 | |||
36 | pciedev = to_pcie_device(dev); | ||
37 | driver = to_service_driver(drv); | ||
38 | if ( (driver->id_table->vendor != PCI_ANY_ID && | ||
39 | driver->id_table->vendor != pciedev->id.vendor) || | ||
40 | (driver->id_table->device != PCI_ANY_ID && | ||
41 | driver->id_table->device != pciedev->id.device) || | ||
42 | driver->id_table->port_type != pciedev->id.port_type || | ||
43 | driver->id_table->service_type != pciedev->id.service_type ) | ||
44 | return 0; | ||
45 | |||
46 | return 1; | ||
47 | } | ||
48 | |||
49 | static int pcie_port_bus_suspend(struct device *dev, u32 state) | ||
50 | { | ||
51 | struct pcie_device *pciedev; | ||
52 | struct pcie_port_service_driver *driver; | ||
53 | |||
54 | if (!dev || !dev->driver) | ||
55 | return 0; | ||
56 | |||
57 | pciedev = to_pcie_device(dev); | ||
58 | driver = to_service_driver(dev->driver); | ||
59 | if (driver && driver->suspend) | ||
60 | driver->suspend(pciedev, state); | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static int pcie_port_bus_resume(struct device *dev) | ||
65 | { | ||
66 | struct pcie_device *pciedev; | ||
67 | struct pcie_port_service_driver *driver; | ||
68 | |||
69 | if (!dev || !dev->driver) | ||
70 | return 0; | ||
71 | |||
72 | pciedev = to_pcie_device(dev); | ||
73 | driver = to_service_driver(dev->driver); | ||
74 | if (driver && driver->resume) | ||
75 | driver->resume(pciedev); | ||
76 | return 0; | ||
77 | } | ||
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c new file mode 100644 index 000000000000..127f64f85dc5 --- /dev/null +++ b/drivers/pci/pcie/portdrv_core.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * File: portdrv_core.c | ||
3 | * Purpose: PCI Express Port Bus Driver's Core Functions | ||
4 | * | ||
5 | * Copyright (C) 2004 Intel | ||
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/pm.h> | ||
14 | #include <linux/pcieport_if.h> | ||
15 | |||
16 | #include "portdrv.h" | ||
17 | |||
18 | extern int pcie_mch_quirk; /* MSI-quirk Indicator */ | ||
19 | |||
20 | static int pcie_port_probe_service(struct device *dev) | ||
21 | { | ||
22 | struct pcie_device *pciedev; | ||
23 | struct pcie_port_service_driver *driver; | ||
24 | int status = -ENODEV; | ||
25 | |||
26 | if (!dev || !dev->driver) | ||
27 | return status; | ||
28 | |||
29 | driver = to_service_driver(dev->driver); | ||
30 | if (!driver || !driver->probe) | ||
31 | return status; | ||
32 | |||
33 | pciedev = to_pcie_device(dev); | ||
34 | status = driver->probe(pciedev, driver->id_table); | ||
35 | if (!status) { | ||
36 | printk(KERN_DEBUG "Load service driver %s on pcie device %s\n", | ||
37 | driver->name, dev->bus_id); | ||
38 | get_device(dev); | ||
39 | } | ||
40 | return status; | ||
41 | } | ||
42 | |||
43 | static int pcie_port_remove_service(struct device *dev) | ||
44 | { | ||
45 | struct pcie_device *pciedev; | ||
46 | struct pcie_port_service_driver *driver; | ||
47 | |||
48 | if (!dev || !dev->driver) | ||
49 | return 0; | ||
50 | |||
51 | pciedev = to_pcie_device(dev); | ||
52 | driver = to_service_driver(dev->driver); | ||
53 | if (driver && driver->remove) { | ||
54 | printk(KERN_DEBUG "Unload service driver %s on pcie device %s\n", | ||
55 | driver->name, dev->bus_id); | ||
56 | driver->remove(pciedev); | ||
57 | put_device(dev); | ||
58 | } | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static void pcie_port_shutdown_service(struct device *dev) {} | ||
63 | |||
64 | static int pcie_port_suspend_service(struct device *dev, u32 state, u32 level) | ||
65 | { | ||
66 | struct pcie_device *pciedev; | ||
67 | struct pcie_port_service_driver *driver; | ||
68 | |||
69 | if (!dev || !dev->driver) | ||
70 | return 0; | ||
71 | |||
72 | pciedev = to_pcie_device(dev); | ||
73 | driver = to_service_driver(dev->driver); | ||
74 | if (driver && driver->suspend) | ||
75 | driver->suspend(pciedev, state); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int pcie_port_resume_service(struct device *dev, u32 state) | ||
80 | { | ||
81 | struct pcie_device *pciedev; | ||
82 | struct pcie_port_service_driver *driver; | ||
83 | |||
84 | if (!dev || !dev->driver) | ||
85 | return 0; | ||
86 | |||
87 | pciedev = to_pcie_device(dev); | ||
88 | driver = to_service_driver(dev->driver); | ||
89 | |||
90 | if (driver && driver->resume) | ||
91 | driver->resume(pciedev); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * release_pcie_device | ||
97 | * | ||
98 | * Being invoked automatically when device is being removed | ||
99 | * in response to device_unregister(dev) call. | ||
100 | * Release all resources being claimed. | ||
101 | */ | ||
102 | static void release_pcie_device(struct device *dev) | ||
103 | { | ||
104 | printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id); | ||
105 | kfree(to_pcie_device(dev)); | ||
106 | } | ||
107 | |||
108 | static int is_msi_quirked(struct pci_dev *dev) | ||
109 | { | ||
110 | int port_type, quirk = 0; | ||
111 | u16 reg16; | ||
112 | |||
113 | pci_read_config_word(dev, | ||
114 | pci_find_capability(dev, PCI_CAP_ID_EXP) + | ||
115 | PCIE_CAPABILITIES_REG, ®16); | ||
116 | port_type = (reg16 >> 4) & PORT_TYPE_MASK; | ||
117 | switch(port_type) { | ||
118 | case PCIE_RC_PORT: | ||
119 | if (pcie_mch_quirk == 1) | ||
120 | quirk = 1; | ||
121 | break; | ||
122 | case PCIE_SW_UPSTREAM_PORT: | ||
123 | case PCIE_SW_DOWNSTREAM_PORT: | ||
124 | default: | ||
125 | break; | ||
126 | } | ||
127 | return quirk; | ||
128 | } | ||
129 | |||
130 | static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) | ||
131 | { | ||
132 | int i, pos, nvec, status = -EINVAL; | ||
133 | int interrupt_mode = PCIE_PORT_INTx_MODE; | ||
134 | |||
135 | /* Set INTx as default */ | ||
136 | for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | ||
137 | if (mask & (1 << i)) | ||
138 | nvec++; | ||
139 | vectors[i] = dev->irq; | ||
140 | } | ||
141 | |||
142 | /* Check MSI quirk */ | ||
143 | if (is_msi_quirked(dev)) | ||
144 | return interrupt_mode; | ||
145 | |||
146 | /* Select MSI-X over MSI if supported */ | ||
147 | pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); | ||
148 | if (pos) { | ||
149 | struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = | ||
150 | {{0, 0}, {0, 1}, {0, 2}, {0, 3}}; | ||
151 | printk("%s Found MSIX capability\n", __FUNCTION__); | ||
152 | status = pci_enable_msix(dev, msix_entries, nvec); | ||
153 | if (!status) { | ||
154 | int j = 0; | ||
155 | |||
156 | interrupt_mode = PCIE_PORT_MSIX_MODE; | ||
157 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | ||
158 | if (mask & (1 << i)) | ||
159 | vectors[i] = msix_entries[j++].vector; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | if (status) { | ||
164 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); | ||
165 | if (pos) { | ||
166 | printk("%s Found MSI capability\n", __FUNCTION__); | ||
167 | status = pci_enable_msi(dev); | ||
168 | if (!status) { | ||
169 | interrupt_mode = PCIE_PORT_MSI_MODE; | ||
170 | for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++) | ||
171 | vectors[i] = dev->irq; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | return interrupt_mode; | ||
176 | } | ||
177 | |||
178 | static int get_port_device_capability(struct pci_dev *dev) | ||
179 | { | ||
180 | int services = 0, pos; | ||
181 | u16 reg16; | ||
182 | u32 reg32; | ||
183 | |||
184 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
185 | pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, ®16); | ||
186 | /* Hot-Plug Capable */ | ||
187 | if (reg16 & PORT_TO_SLOT_MASK) { | ||
188 | pci_read_config_dword(dev, | ||
189 | pos + PCIE_SLOT_CAPABILITIES_REG, ®32); | ||
190 | if (reg32 & SLOT_HP_CAPABLE_MASK) | ||
191 | services |= PCIE_PORT_SERVICE_HP; | ||
192 | } | ||
193 | /* PME Capable */ | ||
194 | pos = pci_find_capability(dev, PCI_CAP_ID_PME); | ||
195 | if (pos) | ||
196 | services |= PCIE_PORT_SERVICE_PME; | ||
197 | |||
198 | pos = PCI_CFG_SPACE_SIZE; | ||
199 | while (pos) { | ||
200 | pci_read_config_dword(dev, pos, ®32); | ||
201 | switch (reg32 & 0xffff) { | ||
202 | case PCI_EXT_CAP_ID_ERR: | ||
203 | services |= PCIE_PORT_SERVICE_AER; | ||
204 | pos = reg32 >> 20; | ||
205 | break; | ||
206 | case PCI_EXT_CAP_ID_VC: | ||
207 | services |= PCIE_PORT_SERVICE_VC; | ||
208 | pos = reg32 >> 20; | ||
209 | break; | ||
210 | default: | ||
211 | pos = 0; | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | return services; | ||
217 | } | ||
218 | |||
219 | static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, | ||
220 | int port_type, int service_type, int irq, int irq_mode) | ||
221 | { | ||
222 | struct device *device; | ||
223 | |||
224 | dev->port = parent; | ||
225 | dev->interrupt_mode = irq_mode; | ||
226 | dev->irq = irq; | ||
227 | dev->id.vendor = parent->vendor; | ||
228 | dev->id.device = parent->device; | ||
229 | dev->id.port_type = port_type; | ||
230 | dev->id.service_type = (1 << service_type); | ||
231 | |||
232 | /* Initialize generic device interface */ | ||
233 | device = &dev->device; | ||
234 | memset(device, 0, sizeof(struct device)); | ||
235 | INIT_LIST_HEAD(&device->node); | ||
236 | INIT_LIST_HEAD(&device->children); | ||
237 | INIT_LIST_HEAD(&device->bus_list); | ||
238 | device->bus = &pcie_port_bus_type; | ||
239 | device->driver = NULL; | ||
240 | device->driver_data = NULL; | ||
241 | device->release = release_pcie_device; /* callback to free pcie dev */ | ||
242 | sprintf(&device->bus_id[0], "pcie%02x", | ||
243 | get_descriptor_id(port_type, service_type)); | ||
244 | device->parent = &parent->dev; | ||
245 | } | ||
246 | |||
247 | static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, | ||
248 | int port_type, int service_type, int irq, int irq_mode) | ||
249 | { | ||
250 | struct pcie_device *device; | ||
251 | |||
252 | device = kmalloc(sizeof(struct pcie_device), GFP_KERNEL); | ||
253 | if (!device) | ||
254 | return NULL; | ||
255 | |||
256 | memset(device, 0, sizeof(struct pcie_device)); | ||
257 | pcie_device_init(parent, device, port_type, service_type, irq,irq_mode); | ||
258 | printk(KERN_DEBUG "Allocate Port Service[%s]\n", device->device.bus_id); | ||
259 | return device; | ||
260 | } | ||
261 | |||
262 | int pcie_port_device_probe(struct pci_dev *dev) | ||
263 | { | ||
264 | int pos, type; | ||
265 | u16 reg; | ||
266 | |||
267 | if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP))) | ||
268 | return -ENODEV; | ||
269 | |||
270 | pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, ®); | ||
271 | type = (reg >> 4) & PORT_TYPE_MASK; | ||
272 | if ( type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT || | ||
273 | type == PCIE_SW_DOWNSTREAM_PORT ) | ||
274 | return 0; | ||
275 | |||
276 | return -ENODEV; | ||
277 | } | ||
278 | |||
279 | int pcie_port_device_register(struct pci_dev *dev) | ||
280 | { | ||
281 | int status, type, capabilities, irq_mode, i; | ||
282 | int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; | ||
283 | u16 reg16; | ||
284 | |||
285 | /* Get port type */ | ||
286 | pci_read_config_word(dev, | ||
287 | pci_find_capability(dev, PCI_CAP_ID_EXP) + | ||
288 | PCIE_CAPABILITIES_REG, ®16); | ||
289 | type = (reg16 >> 4) & PORT_TYPE_MASK; | ||
290 | |||
291 | /* Now get port services */ | ||
292 | capabilities = get_port_device_capability(dev); | ||
293 | irq_mode = assign_interrupt_mode(dev, vectors, capabilities); | ||
294 | |||
295 | /* Allocate child services if any */ | ||
296 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | ||
297 | struct pcie_device *child; | ||
298 | |||
299 | if (capabilities & (1 << i)) { | ||
300 | child = alloc_pcie_device( | ||
301 | dev, /* parent */ | ||
302 | type, /* port type */ | ||
303 | i, /* service type */ | ||
304 | vectors[i], /* irq */ | ||
305 | irq_mode /* interrupt mode */); | ||
306 | if (child) { | ||
307 | status = device_register(&child->device); | ||
308 | if (status) { | ||
309 | kfree(child); | ||
310 | continue; | ||
311 | } | ||
312 | get_device(&child->device); | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | #ifdef CONFIG_PM | ||
320 | int pcie_port_device_suspend(struct pci_dev *dev, u32 state) | ||
321 | { | ||
322 | struct list_head *head, *tmp; | ||
323 | struct device *parent, *child; | ||
324 | struct device_driver *driver; | ||
325 | struct pcie_port_service_driver *service_driver; | ||
326 | |||
327 | parent = &dev->dev; | ||
328 | head = &parent->children; | ||
329 | tmp = head->next; | ||
330 | while (head != tmp) { | ||
331 | child = container_of(tmp, struct device, node); | ||
332 | tmp = tmp->next; | ||
333 | if (child->bus != &pcie_port_bus_type) | ||
334 | continue; | ||
335 | driver = child->driver; | ||
336 | if (!driver) | ||
337 | continue; | ||
338 | service_driver = to_service_driver(driver); | ||
339 | if (service_driver->suspend) | ||
340 | service_driver->suspend(to_pcie_device(child), state); | ||
341 | } | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | int pcie_port_device_resume(struct pci_dev *dev) | ||
346 | { | ||
347 | struct list_head *head, *tmp; | ||
348 | struct device *parent, *child; | ||
349 | struct device_driver *driver; | ||
350 | struct pcie_port_service_driver *service_driver; | ||
351 | |||
352 | parent = &dev->dev; | ||
353 | head = &parent->children; | ||
354 | tmp = head->next; | ||
355 | while (head != tmp) { | ||
356 | child = container_of(tmp, struct device, node); | ||
357 | tmp = tmp->next; | ||
358 | if (child->bus != &pcie_port_bus_type) | ||
359 | continue; | ||
360 | driver = child->driver; | ||
361 | if (!driver) | ||
362 | continue; | ||
363 | service_driver = to_service_driver(driver); | ||
364 | if (service_driver->resume) | ||
365 | service_driver->resume(to_pcie_device(child)); | ||
366 | } | ||
367 | return 0; | ||
368 | |||
369 | } | ||
370 | #endif | ||
371 | |||
372 | void pcie_port_device_remove(struct pci_dev *dev) | ||
373 | { | ||
374 | struct list_head *head, *tmp; | ||
375 | struct device *parent, *child; | ||
376 | struct device_driver *driver; | ||
377 | struct pcie_port_service_driver *service_driver; | ||
378 | int interrupt_mode = PCIE_PORT_INTx_MODE; | ||
379 | |||
380 | parent = &dev->dev; | ||
381 | head = &parent->children; | ||
382 | tmp = head->next; | ||
383 | while (head != tmp) { | ||
384 | child = container_of(tmp, struct device, node); | ||
385 | tmp = tmp->next; | ||
386 | if (child->bus != &pcie_port_bus_type) | ||
387 | continue; | ||
388 | driver = child->driver; | ||
389 | if (driver) { | ||
390 | service_driver = to_service_driver(driver); | ||
391 | if (service_driver->remove) | ||
392 | service_driver->remove(to_pcie_device(child)); | ||
393 | } | ||
394 | interrupt_mode = (to_pcie_device(child))->interrupt_mode; | ||
395 | put_device(child); | ||
396 | device_unregister(child); | ||
397 | } | ||
398 | /* Switch to INTx by default if MSI enabled */ | ||
399 | if (interrupt_mode == PCIE_PORT_MSIX_MODE) | ||
400 | pci_disable_msix(dev); | ||
401 | else if (interrupt_mode == PCIE_PORT_MSI_MODE) | ||
402 | pci_disable_msi(dev); | ||
403 | } | ||
404 | |||
405 | void pcie_port_bus_register(void) | ||
406 | { | ||
407 | bus_register(&pcie_port_bus_type); | ||
408 | } | ||
409 | |||
410 | void pcie_port_bus_unregister(void) | ||
411 | { | ||
412 | bus_unregister(&pcie_port_bus_type); | ||
413 | } | ||
414 | |||
415 | int pcie_port_service_register(struct pcie_port_service_driver *new) | ||
416 | { | ||
417 | new->driver.name = (char *)new->name; | ||
418 | new->driver.bus = &pcie_port_bus_type; | ||
419 | new->driver.probe = pcie_port_probe_service; | ||
420 | new->driver.remove = pcie_port_remove_service; | ||
421 | new->driver.shutdown = pcie_port_shutdown_service; | ||
422 | new->driver.suspend = pcie_port_suspend_service; | ||
423 | new->driver.resume = pcie_port_resume_service; | ||
424 | |||
425 | return driver_register(&new->driver); | ||
426 | } | ||
427 | |||
428 | void pcie_port_service_unregister(struct pcie_port_service_driver *new) | ||
429 | { | ||
430 | driver_unregister(&new->driver); | ||
431 | } | ||
432 | |||
433 | EXPORT_SYMBOL(pcie_port_service_register); | ||
434 | EXPORT_SYMBOL(pcie_port_service_unregister); | ||
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c new file mode 100644 index 000000000000..3184843c3649 --- /dev/null +++ b/drivers/pci/pcie/portdrv_pci.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * File: portdrv_pci.c | ||
3 | * Purpose: PCI Express Port Bus Driver | ||
4 | * | ||
5 | * Copyright (C) 2004 Intel | ||
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/pm.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/pcieport_if.h> | ||
16 | |||
17 | #include "portdrv.h" | ||
18 | |||
19 | /* | ||
20 | * Version Information | ||
21 | */ | ||
22 | #define DRIVER_VERSION "v1.0" | ||
23 | #define DRIVER_AUTHOR "tom.l.nguyen@intel.com" | ||
24 | #define DRIVER_DESC "PCIE Port Bus Driver" | ||
25 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
26 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
27 | MODULE_LICENSE("GPL"); | ||
28 | |||
29 | /* global data */ | ||
30 | static const char device_name[] = "pcieport-driver"; | ||
31 | |||
32 | /* | ||
33 | * pcie_portdrv_probe - Probe PCI-Express port devices | ||
34 | * @dev: PCI-Express port device being probed | ||
35 | * | ||
36 | * If detected invokes the pcie_port_device_register() method for | ||
37 | * this port device. | ||
38 | * | ||
39 | */ | ||
40 | static int __devinit pcie_portdrv_probe (struct pci_dev *dev, | ||
41 | const struct pci_device_id *id ) | ||
42 | { | ||
43 | int status; | ||
44 | |||
45 | status = pcie_port_device_probe(dev); | ||
46 | if (status) | ||
47 | return status; | ||
48 | |||
49 | if (pci_enable_device(dev) < 0) | ||
50 | return -ENODEV; | ||
51 | |||
52 | pci_set_master(dev); | ||
53 | if (!dev->irq) { | ||
54 | printk(KERN_WARNING | ||
55 | "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n", | ||
56 | __FUNCTION__, dev->device, dev->vendor); | ||
57 | } | ||
58 | if (pcie_port_device_register(dev)) | ||
59 | return -ENOMEM; | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static void pcie_portdrv_remove (struct pci_dev *dev) | ||
65 | { | ||
66 | pcie_port_device_remove(dev); | ||
67 | } | ||
68 | |||
69 | #ifdef CONFIG_PM | ||
70 | static int pcie_portdrv_suspend (struct pci_dev *dev, u32 state) | ||
71 | { | ||
72 | return pcie_port_device_suspend(dev, state); | ||
73 | } | ||
74 | |||
75 | static int pcie_portdrv_resume (struct pci_dev *dev) | ||
76 | { | ||
77 | return pcie_port_device_resume(dev); | ||
78 | } | ||
79 | #endif | ||
80 | |||
81 | /* | ||
82 | * LINUX Device Driver Model | ||
83 | */ | ||
84 | static const struct pci_device_id port_pci_ids[] = { { | ||
85 | /* handle any PCI-Express port */ | ||
86 | PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0), | ||
87 | }, { /* end: all zeroes */ } | ||
88 | }; | ||
89 | MODULE_DEVICE_TABLE(pci, port_pci_ids); | ||
90 | |||
91 | static struct pci_driver pcie_portdrv = { | ||
92 | .name = (char *)device_name, | ||
93 | .id_table = &port_pci_ids[0], | ||
94 | |||
95 | .probe = pcie_portdrv_probe, | ||
96 | .remove = pcie_portdrv_remove, | ||
97 | |||
98 | #ifdef CONFIG_PM | ||
99 | .suspend = pcie_portdrv_suspend, | ||
100 | .resume = pcie_portdrv_resume, | ||
101 | #endif /* PM */ | ||
102 | }; | ||
103 | |||
104 | static int __init pcie_portdrv_init(void) | ||
105 | { | ||
106 | int retval = 0; | ||
107 | |||
108 | pcie_port_bus_register(); | ||
109 | retval = pci_register_driver(&pcie_portdrv); | ||
110 | if (retval) | ||
111 | pcie_port_bus_unregister(); | ||
112 | return retval; | ||
113 | } | ||
114 | |||
115 | static void __exit pcie_portdrv_exit(void) | ||
116 | { | ||
117 | pci_unregister_driver(&pcie_portdrv); | ||
118 | pcie_port_bus_unregister(); | ||
119 | } | ||
120 | |||
121 | module_init(pcie_portdrv_init); | ||
122 | module_exit(pcie_portdrv_exit); | ||