Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
4e57b681 TS |
14 | #include <linux/string.h> |
15 | #include <linux/slab.h> | |
1da177e4 LT |
16 | #include <linux/pcieport_if.h> |
17 | ||
18 | #include "portdrv.h" | |
19 | ||
20 | extern int pcie_mch_quirk; /* MSI-quirk Indicator */ | |
21 | ||
22 | static int pcie_port_probe_service(struct device *dev) | |
23 | { | |
24 | struct pcie_device *pciedev; | |
25 | struct pcie_port_service_driver *driver; | |
34438ba6 | 26 | int status; |
1da177e4 LT |
27 | |
28 | if (!dev || !dev->driver) | |
34438ba6 | 29 | return -ENODEV; |
1da177e4 LT |
30 | |
31 | driver = to_service_driver(dev->driver); | |
32 | if (!driver || !driver->probe) | |
34438ba6 | 33 | return -ENODEV; |
1da177e4 LT |
34 | |
35 | pciedev = to_pcie_device(dev); | |
36 | status = driver->probe(pciedev, driver->id_table); | |
37 | if (!status) { | |
34438ba6 BH |
38 | dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", |
39 | driver->name); | |
1da177e4 LT |
40 | get_device(dev); |
41 | } | |
42 | return status; | |
43 | } | |
44 | ||
45 | static int pcie_port_remove_service(struct device *dev) | |
46 | { | |
47 | struct pcie_device *pciedev; | |
48 | struct pcie_port_service_driver *driver; | |
49 | ||
50 | if (!dev || !dev->driver) | |
51 | return 0; | |
52 | ||
53 | pciedev = to_pcie_device(dev); | |
54 | driver = to_service_driver(dev->driver); | |
55 | if (driver && driver->remove) { | |
34438ba6 BH |
56 | dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n", |
57 | driver->name); | |
1da177e4 LT |
58 | driver->remove(pciedev); |
59 | put_device(dev); | |
60 | } | |
61 | return 0; | |
62 | } | |
63 | ||
64 | static void pcie_port_shutdown_service(struct device *dev) {} | |
65 | ||
9480e307 | 66 | static int pcie_port_suspend_service(struct device *dev, pm_message_t state) |
1da177e4 LT |
67 | { |
68 | struct pcie_device *pciedev; | |
69 | struct pcie_port_service_driver *driver; | |
70 | ||
71 | if (!dev || !dev->driver) | |
72 | return 0; | |
73 | ||
74 | pciedev = to_pcie_device(dev); | |
75 | driver = to_service_driver(dev->driver); | |
76 | if (driver && driver->suspend) | |
77 | driver->suspend(pciedev, state); | |
78 | return 0; | |
79 | } | |
80 | ||
9480e307 | 81 | static int pcie_port_resume_service(struct device *dev) |
1da177e4 LT |
82 | { |
83 | struct pcie_device *pciedev; | |
84 | struct pcie_port_service_driver *driver; | |
85 | ||
86 | if (!dev || !dev->driver) | |
87 | return 0; | |
88 | ||
89 | pciedev = to_pcie_device(dev); | |
90 | driver = to_service_driver(dev->driver); | |
91 | ||
92 | if (driver && driver->resume) | |
93 | driver->resume(pciedev); | |
94 | return 0; | |
95 | } | |
96 | ||
97 | /* | |
98 | * release_pcie_device | |
99 | * | |
100 | * Being invoked automatically when device is being removed | |
101 | * in response to device_unregister(dev) call. | |
102 | * Release all resources being claimed. | |
103 | */ | |
104 | static void release_pcie_device(struct device *dev) | |
105 | { | |
34438ba6 | 106 | dev_printk(KERN_DEBUG, dev, "free port service\n"); |
1da177e4 LT |
107 | kfree(to_pcie_device(dev)); |
108 | } | |
109 | ||
110 | static int is_msi_quirked(struct pci_dev *dev) | |
111 | { | |
112 | int port_type, quirk = 0; | |
113 | u16 reg16; | |
114 | ||
115 | pci_read_config_word(dev, | |
116 | pci_find_capability(dev, PCI_CAP_ID_EXP) + | |
117 | PCIE_CAPABILITIES_REG, ®16); | |
118 | port_type = (reg16 >> 4) & PORT_TYPE_MASK; | |
119 | switch(port_type) { | |
120 | case PCIE_RC_PORT: | |
121 | if (pcie_mch_quirk == 1) | |
122 | quirk = 1; | |
123 | break; | |
124 | case PCIE_SW_UPSTREAM_PORT: | |
125 | case PCIE_SW_DOWNSTREAM_PORT: | |
126 | default: | |
127 | break; | |
128 | } | |
129 | return quirk; | |
130 | } | |
131 | ||
132 | static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) | |
133 | { | |
134 | int i, pos, nvec, status = -EINVAL; | |
135 | int interrupt_mode = PCIE_PORT_INTx_MODE; | |
136 | ||
137 | /* Set INTx as default */ | |
138 | for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | |
139 | if (mask & (1 << i)) | |
140 | nvec++; | |
141 | vectors[i] = dev->irq; | |
142 | } | |
143 | ||
144 | /* Check MSI quirk */ | |
145 | if (is_msi_quirked(dev)) | |
146 | return interrupt_mode; | |
147 | ||
148 | /* Select MSI-X over MSI if supported */ | |
149 | pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); | |
150 | if (pos) { | |
151 | struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = | |
152 | {{0, 0}, {0, 1}, {0, 2}, {0, 3}}; | |
34438ba6 | 153 | dev_info(&dev->dev, "found MSI-X capability\n"); |
1da177e4 LT |
154 | status = pci_enable_msix(dev, msix_entries, nvec); |
155 | if (!status) { | |
156 | int j = 0; | |
157 | ||
158 | interrupt_mode = PCIE_PORT_MSIX_MODE; | |
159 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | |
160 | if (mask & (1 << i)) | |
161 | vectors[i] = msix_entries[j++].vector; | |
162 | } | |
163 | } | |
164 | } | |
165 | if (status) { | |
166 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); | |
167 | if (pos) { | |
34438ba6 | 168 | dev_info(&dev->dev, "found MSI capability\n"); |
1da177e4 LT |
169 | status = pci_enable_msi(dev); |
170 | if (!status) { | |
171 | interrupt_mode = PCIE_PORT_MSI_MODE; | |
172 | for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++) | |
173 | vectors[i] = dev->irq; | |
174 | } | |
175 | } | |
176 | } | |
177 | return interrupt_mode; | |
178 | } | |
179 | ||
180 | static int get_port_device_capability(struct pci_dev *dev) | |
181 | { | |
182 | int services = 0, pos; | |
183 | u16 reg16; | |
184 | u32 reg32; | |
185 | ||
186 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | |
187 | pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, ®16); | |
188 | /* Hot-Plug Capable */ | |
189 | if (reg16 & PORT_TO_SLOT_MASK) { | |
190 | pci_read_config_dword(dev, | |
191 | pos + PCIE_SLOT_CAPABILITIES_REG, ®32); | |
192 | if (reg32 & SLOT_HP_CAPABLE_MASK) | |
193 | services |= PCIE_PORT_SERVICE_HP; | |
194 | } | |
39ec4561 SL |
195 | /* PME Capable - root port capability */ |
196 | if (((reg16 >> 4) & PORT_TYPE_MASK) == PCIE_RC_PORT) | |
1da177e4 LT |
197 | services |= PCIE_PORT_SERVICE_PME; |
198 | ||
199 | pos = PCI_CFG_SPACE_SIZE; | |
200 | while (pos) { | |
201 | pci_read_config_dword(dev, pos, ®32); | |
202 | switch (reg32 & 0xffff) { | |
203 | case PCI_EXT_CAP_ID_ERR: | |
204 | services |= PCIE_PORT_SERVICE_AER; | |
205 | pos = reg32 >> 20; | |
206 | break; | |
207 | case PCI_EXT_CAP_ID_VC: | |
208 | services |= PCIE_PORT_SERVICE_VC; | |
209 | pos = reg32 >> 20; | |
210 | break; | |
211 | default: | |
212 | pos = 0; | |
213 | break; | |
214 | } | |
215 | } | |
216 | ||
217 | return services; | |
218 | } | |
219 | ||
220 | static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, | |
221 | int port_type, int service_type, int irq, int irq_mode) | |
222 | { | |
223 | struct device *device; | |
224 | ||
225 | dev->port = parent; | |
226 | dev->interrupt_mode = irq_mode; | |
227 | dev->irq = irq; | |
228 | dev->id.vendor = parent->vendor; | |
229 | dev->id.device = parent->device; | |
230 | dev->id.port_type = port_type; | |
231 | dev->id.service_type = (1 << service_type); | |
232 | ||
233 | /* Initialize generic device interface */ | |
234 | device = &dev->device; | |
235 | memset(device, 0, sizeof(struct device)); | |
1da177e4 LT |
236 | device->bus = &pcie_port_bus_type; |
237 | device->driver = NULL; | |
d0e2b4a0 | 238 | device->driver_data = NULL; |
1da177e4 | 239 | device->release = release_pcie_device; /* callback to free pcie dev */ |
8c9ad508 SV |
240 | snprintf(device->bus_id, sizeof(device->bus_id), "%s:pcie%02x", |
241 | pci_name(parent), get_descriptor_id(port_type, service_type)); | |
1da177e4 LT |
242 | device->parent = &parent->dev; |
243 | } | |
244 | ||
d0e2b4a0 | 245 | static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, |
1da177e4 LT |
246 | int port_type, int service_type, int irq, int irq_mode) |
247 | { | |
248 | struct pcie_device *device; | |
249 | ||
f5afe806 | 250 | device = kzalloc(sizeof(struct pcie_device), GFP_KERNEL); |
1da177e4 LT |
251 | if (!device) |
252 | return NULL; | |
253 | ||
1da177e4 | 254 | pcie_device_init(parent, device, port_type, service_type, irq,irq_mode); |
34438ba6 | 255 | dev_printk(KERN_DEBUG, &device->device, "allocate port service\n"); |
1da177e4 LT |
256 | return device; |
257 | } | |
258 | ||
259 | int pcie_port_device_probe(struct pci_dev *dev) | |
260 | { | |
261 | int pos, type; | |
262 | u16 reg; | |
263 | ||
264 | if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP))) | |
265 | return -ENODEV; | |
266 | ||
267 | pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, ®); | |
268 | type = (reg >> 4) & PORT_TYPE_MASK; | |
269 | if ( type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT || | |
d0e2b4a0 | 270 | type == PCIE_SW_DOWNSTREAM_PORT ) |
1da177e4 | 271 | return 0; |
d0e2b4a0 | 272 | |
1da177e4 LT |
273 | return -ENODEV; |
274 | } | |
275 | ||
276 | int pcie_port_device_register(struct pci_dev *dev) | |
277 | { | |
5823d100 | 278 | struct pcie_port_device_ext *p_ext; |
1da177e4 LT |
279 | int status, type, capabilities, irq_mode, i; |
280 | int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; | |
281 | u16 reg16; | |
282 | ||
5823d100 | 283 | /* Allocate port device extension */ |
284 | if (!(p_ext = kmalloc(sizeof(struct pcie_port_device_ext), GFP_KERNEL))) | |
285 | return -ENOMEM; | |
286 | ||
287 | pci_set_drvdata(dev, p_ext); | |
288 | ||
1da177e4 | 289 | /* Get port type */ |
d0e2b4a0 | 290 | pci_read_config_word(dev, |
291 | pci_find_capability(dev, PCI_CAP_ID_EXP) + | |
1da177e4 LT |
292 | PCIE_CAPABILITIES_REG, ®16); |
293 | type = (reg16 >> 4) & PORT_TYPE_MASK; | |
294 | ||
295 | /* Now get port services */ | |
296 | capabilities = get_port_device_capability(dev); | |
297 | irq_mode = assign_interrupt_mode(dev, vectors, capabilities); | |
5823d100 | 298 | p_ext->interrupt_mode = irq_mode; |
1da177e4 LT |
299 | |
300 | /* Allocate child services if any */ | |
301 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | |
302 | struct pcie_device *child; | |
303 | ||
304 | if (capabilities & (1 << i)) { | |
305 | child = alloc_pcie_device( | |
306 | dev, /* parent */ | |
d0e2b4a0 | 307 | type, /* port type */ |
1da177e4 LT |
308 | i, /* service type */ |
309 | vectors[i], /* irq */ | |
310 | irq_mode /* interrupt mode */); | |
d0e2b4a0 | 311 | if (child) { |
1da177e4 LT |
312 | status = device_register(&child->device); |
313 | if (status) { | |
314 | kfree(child); | |
315 | continue; | |
316 | } | |
317 | get_device(&child->device); | |
318 | } | |
319 | } | |
320 | } | |
321 | return 0; | |
322 | } | |
323 | ||
324 | #ifdef CONFIG_PM | |
d0e2b4a0 | 325 | static int suspend_iter(struct device *dev, void *data) |
1da177e4 | 326 | { |
1da177e4 | 327 | struct pcie_port_service_driver *service_driver; |
2a569579 | 328 | pm_message_t state = * (pm_message_t *) data; |
d0e2b4a0 | 329 | |
330 | if ((dev->bus == &pcie_port_bus_type) && | |
331 | (dev->driver)) { | |
332 | service_driver = to_service_driver(dev->driver); | |
333 | if (service_driver->suspend) | |
334 | service_driver->suspend(to_pcie_device(dev), state); | |
335 | } | |
336 | return 0; | |
337 | } | |
1da177e4 | 338 | |
2a569579 | 339 | int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) |
d0e2b4a0 | 340 | { |
b19441af | 341 | return device_for_each_child(&dev->dev, &state, suspend_iter); |
1da177e4 LT |
342 | } |
343 | ||
d0e2b4a0 | 344 | static int resume_iter(struct device *dev, void *data) |
345 | { | |
1da177e4 LT |
346 | struct pcie_port_service_driver *service_driver; |
347 | ||
d0e2b4a0 | 348 | if ((dev->bus == &pcie_port_bus_type) && |
349 | (dev->driver)) { | |
350 | service_driver = to_service_driver(dev->driver); | |
351 | if (service_driver->resume) | |
352 | service_driver->resume(to_pcie_device(dev)); | |
1da177e4 | 353 | } |
d0e2b4a0 | 354 | return 0; |
355 | } | |
1da177e4 | 356 | |
d0e2b4a0 | 357 | int pcie_port_device_resume(struct pci_dev *dev) |
358 | { | |
b19441af | 359 | return device_for_each_child(&dev->dev, NULL, resume_iter); |
1da177e4 LT |
360 | } |
361 | #endif | |
362 | ||
d0e2b4a0 | 363 | static int remove_iter(struct device *dev, void *data) |
1da177e4 | 364 | { |
1da177e4 | 365 | struct pcie_port_service_driver *service_driver; |
1da177e4 | 366 | |
d0e2b4a0 | 367 | if (dev->bus == &pcie_port_bus_type) { |
368 | if (dev->driver) { | |
369 | service_driver = to_service_driver(dev->driver); | |
370 | if (service_driver->remove) | |
371 | service_driver->remove(to_pcie_device(dev)); | |
1da177e4 | 372 | } |
d0e2b4a0 | 373 | *(unsigned long*)data = (unsigned long)dev; |
374 | return 1; | |
1da177e4 | 375 | } |
d0e2b4a0 | 376 | return 0; |
377 | } | |
378 | ||
379 | void pcie_port_device_remove(struct pci_dev *dev) | |
380 | { | |
381 | struct device *device; | |
382 | unsigned long device_addr; | |
383 | int interrupt_mode = PCIE_PORT_INTx_MODE; | |
384 | int status; | |
385 | ||
386 | do { | |
387 | status = device_for_each_child(&dev->dev, &device_addr, remove_iter); | |
388 | if (status) { | |
389 | device = (struct device*)device_addr; | |
390 | interrupt_mode = (to_pcie_device(device))->interrupt_mode; | |
391 | put_device(device); | |
392 | device_unregister(device); | |
393 | } | |
394 | } while (status); | |
1da177e4 LT |
395 | /* Switch to INTx by default if MSI enabled */ |
396 | if (interrupt_mode == PCIE_PORT_MSIX_MODE) | |
397 | pci_disable_msix(dev); | |
398 | else if (interrupt_mode == PCIE_PORT_MSI_MODE) | |
399 | pci_disable_msi(dev); | |
400 | } | |
401 | ||
3ec6a8d0 | 402 | int pcie_port_bus_register(void) |
1da177e4 | 403 | { |
20d51660 | 404 | return bus_register(&pcie_port_bus_type); |
1da177e4 LT |
405 | } |
406 | ||
407 | void pcie_port_bus_unregister(void) | |
408 | { | |
409 | bus_unregister(&pcie_port_bus_type); | |
410 | } | |
411 | ||
412 | int pcie_port_service_register(struct pcie_port_service_driver *new) | |
413 | { | |
414 | new->driver.name = (char *)new->name; | |
415 | new->driver.bus = &pcie_port_bus_type; | |
416 | new->driver.probe = pcie_port_probe_service; | |
417 | new->driver.remove = pcie_port_remove_service; | |
418 | new->driver.shutdown = pcie_port_shutdown_service; | |
419 | new->driver.suspend = pcie_port_suspend_service; | |
420 | new->driver.resume = pcie_port_resume_service; | |
421 | ||
422 | return driver_register(&new->driver); | |
d0e2b4a0 | 423 | } |
1da177e4 LT |
424 | |
425 | void pcie_port_service_unregister(struct pcie_port_service_driver *new) | |
426 | { | |
427 | driver_unregister(&new->driver); | |
428 | } | |
429 | ||
430 | EXPORT_SYMBOL(pcie_port_service_register); | |
431 | EXPORT_SYMBOL(pcie_port_service_unregister); |