diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 23:19:31 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 23:19:31 -0400 |
| commit | 07021b43597f506cc525d139ed1a94e79cf184f2 (patch) | |
| tree | 888ab33ec69b397ae6f8a2c82d14197047ee827e /drivers/pci/hotplug | |
| parent | d1f5323370fceaed43a7ee38f4c7bfc7e70f28d0 (diff) | |
| parent | b7b7013cac55d794940bd9cb7b7c55c9dececac4 (diff) | |
Merge tag 'powerpc-4.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc updates from Michael Ellerman:
"Highlights:
- Major rework of Book3S 64-bit exception vectors (Nicholas Piggin)
- Use gas sections for arranging exception vectors et. al.
- Large set of TM cleanups and selftests (Cyril Bur)
- Enable transactional memory (TM) lazily for userspace (Cyril Bur)
- Support for XZ compression in the zImage wrapper (Oliver
O'Halloran)
- Add support for bpf constant blinding (Naveen N. Rao)
- Beginnings of upstream support for PA Semi Nemo motherboards
(Darren Stevens)
Fixes:
- Ensure .mem(init|exit).text are within _stext/_etext (Michael
Ellerman)
- xmon: Don't use ld on 32-bit (Michael Ellerman)
- vdso64: Use double word compare on pointers (Anton Blanchard)
- powerpc/nvram: Fix an incorrect partition merge (Pan Xinhui)
- powerpc: Fix usage of _PAGE_RO in hugepage (Christophe Leroy)
- powerpc/mm: Update FORCE_MAX_ZONEORDER range to allow hugetlb w/4K
(Aneesh Kumar K.V)
- Fix memory leak in queue_hotplug_event() error path (Andrew
Donnellan)
- Replay hypervisor maintenance interrupt first (Nicholas Piggin)
Various performance optimisations (Anton Blanchard):
- Align hot loops of memset() and backwards_memcpy()
- During context switch, check before setting mm_cpumask
- Remove static branch prediction in atomic{, 64}_add_unless
- Only disable HAVE_EFFICIENT_UNALIGNED_ACCESS on POWER7 little
endian
- Set default CPU type to POWER8 for little endian builds
Cleanups & features:
- Sparse fixes/cleanups (Daniel Axtens)
- Preserve CFAR value on SLB miss caused by access to bogus address
(Paul Mackerras)
- Radix MMU fixups for POWER9 (Aneesh Kumar K.V)
- Support for setting used_(vsr|vr|spe) in sigreturn path (for CRIU)
(Simon Guo)
- Optimise syscall entry for virtual, relocatable case (Nicholas
Piggin)
- Optimise MSR handling in exception handling (Nicholas Piggin)
- Support for kexec with Radix MMU (Benjamin Herrenschmidt)
- powernv EEH fixes (Russell Currey)
- Suprise PCI hotplug support for powernv (Gavin Shan)
- Endian/sparse fixes for powernv PCI (Gavin Shan)
- Defconfig updates (Anton Blanchard)
- KVM: PPC: Book3S HV: Migrate pinned pages out of CMA (Balbir Singh)
- cxl: Flush PSL cache before resetting the adapter (Frederic Barrat)
- cxl: replace loop with for_each_child_of_node(), remove unneeded
of_node_put() (Andrew Donnellan)
- Fix HV facility unavailable to use correct handler (Nicholas
Piggin)
- Remove unnecessary syscall trampoline (Nicholas Piggin)
- fadump: Fix build break when CONFIG_PROC_VMCORE=n (Michael
Ellerman)
- Quieten EEH message when no adapters are found (Anton Blanchard)
- powernv: Add PHB register dump debugfs handle (Russell Currey)
- Use kprobe blacklist for exception handlers & asm functions
(Nicholas Piggin)
- Document the syscall ABI (Nicholas Piggin)
- MAINTAINERS: Update cxl maintainers (Michael Neuling)
- powerpc: Remove all usages of NO_IRQ (Michael Ellerman)
Minor cleanups:
- Andrew Donnellan, Christophe Leroy, Colin Ian King, Cyril Bur,
Frederic Barrat, Pan Xinhui, PrasannaKumar Muralidharan, Rui Teng,
Simon Guo"
* tag 'powerpc-4.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (156 commits)
powerpc/bpf: Add support for bpf constant blinding
powerpc/bpf: Implement support for tail calls
powerpc/bpf: Introduce accessors for using the tmp local stack space
powerpc/fadump: Fix build break when CONFIG_PROC_VMCORE=n
powerpc: tm: Enable transactional memory (TM) lazily for userspace
powerpc/tm: Add TM Unavailable Exception
powerpc: Remove do_load_up_transact_{fpu,altivec}
powerpc: tm: Rename transct_(*) to ck(\1)_state
powerpc: tm: Always use fp_state and vr_state to store live registers
selftests/powerpc: Add checks for transactional VSXs in signal contexts
selftests/powerpc: Add checks for transactional VMXs in signal contexts
selftests/powerpc: Add checks for transactional FPUs in signal contexts
selftests/powerpc: Add checks for transactional GPRs in signal contexts
selftests/powerpc: Check that signals always get delivered
selftests/powerpc: Add TM tcheck helpers in C
selftests/powerpc: Allow tests to extend their kill timeout
selftests/powerpc: Introduce GPR asm helper header file
selftests/powerpc: Move VMX stack frame macros to header file
selftests/powerpc: Rework FPU stack placement macros and move to header file
selftests/powerpc: Check for VSX preservation across userspace preemption
...
Diffstat (limited to 'drivers/pci/hotplug')
| -rw-r--r-- | drivers/pci/hotplug/pnv_php.c | 283 |
1 files changed, 249 insertions, 34 deletions
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index e6245b03f0a1..56efaf72d08e 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c | |||
| @@ -22,6 +22,12 @@ | |||
| 22 | #define DRIVER_AUTHOR "Gavin Shan, IBM Corporation" | 22 | #define DRIVER_AUTHOR "Gavin Shan, IBM Corporation" |
| 23 | #define DRIVER_DESC "PowerPC PowerNV PCI Hotplug Driver" | 23 | #define DRIVER_DESC "PowerPC PowerNV PCI Hotplug Driver" |
| 24 | 24 | ||
| 25 | struct pnv_php_event { | ||
| 26 | bool added; | ||
| 27 | struct pnv_php_slot *php_slot; | ||
| 28 | struct work_struct work; | ||
| 29 | }; | ||
| 30 | |||
| 25 | static LIST_HEAD(pnv_php_slot_list); | 31 | static LIST_HEAD(pnv_php_slot_list); |
| 26 | static DEFINE_SPINLOCK(pnv_php_lock); | 32 | static DEFINE_SPINLOCK(pnv_php_lock); |
| 27 | 33 | ||
| @@ -29,12 +35,40 @@ static void pnv_php_register(struct device_node *dn); | |||
| 29 | static void pnv_php_unregister_one(struct device_node *dn); | 35 | static void pnv_php_unregister_one(struct device_node *dn); |
| 30 | static void pnv_php_unregister(struct device_node *dn); | 36 | static void pnv_php_unregister(struct device_node *dn); |
| 31 | 37 | ||
| 38 | static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) | ||
| 39 | { | ||
| 40 | struct pci_dev *pdev = php_slot->pdev; | ||
| 41 | u16 ctrl; | ||
| 42 | |||
| 43 | if (php_slot->irq > 0) { | ||
| 44 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); | ||
| 45 | ctrl &= ~(PCI_EXP_SLTCTL_HPIE | | ||
| 46 | PCI_EXP_SLTCTL_PDCE | | ||
| 47 | PCI_EXP_SLTCTL_DLLSCE); | ||
| 48 | pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); | ||
| 49 | |||
| 50 | free_irq(php_slot->irq, php_slot); | ||
| 51 | php_slot->irq = 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (php_slot->wq) { | ||
| 55 | destroy_workqueue(php_slot->wq); | ||
| 56 | php_slot->wq = NULL; | ||
| 57 | } | ||
| 58 | |||
| 59 | if (pdev->msix_enabled) | ||
| 60 | pci_disable_msix(pdev); | ||
| 61 | else if (pdev->msi_enabled) | ||
| 62 | pci_disable_msi(pdev); | ||
| 63 | } | ||
| 64 | |||
| 32 | static void pnv_php_free_slot(struct kref *kref) | 65 | static void pnv_php_free_slot(struct kref *kref) |
| 33 | { | 66 | { |
| 34 | struct pnv_php_slot *php_slot = container_of(kref, | 67 | struct pnv_php_slot *php_slot = container_of(kref, |
| 35 | struct pnv_php_slot, kref); | 68 | struct pnv_php_slot, kref); |
| 36 | 69 | ||
| 37 | WARN_ON(!list_empty(&php_slot->children)); | 70 | WARN_ON(!list_empty(&php_slot->children)); |
| 71 | pnv_php_disable_irq(php_slot); | ||
| 38 | kfree(php_slot->name); | 72 | kfree(php_slot->name); |
| 39 | kfree(php_slot); | 73 | kfree(php_slot); |
| 40 | } | 74 | } |
| @@ -122,7 +156,7 @@ static void pnv_php_detach_device_nodes(struct device_node *parent) | |||
| 122 | 156 | ||
| 123 | of_node_put(dn); | 157 | of_node_put(dn); |
| 124 | refcount = atomic_read(&dn->kobj.kref.refcount); | 158 | refcount = atomic_read(&dn->kobj.kref.refcount); |
| 125 | if (unlikely(refcount != 1)) | 159 | if (refcount != 1) |
| 126 | pr_warn("Invalid refcount %d on <%s>\n", | 160 | pr_warn("Invalid refcount %d on <%s>\n", |
| 127 | refcount, of_node_full_name(dn)); | 161 | refcount, of_node_full_name(dn)); |
| 128 | 162 | ||
| @@ -184,11 +218,11 @@ static int pnv_php_populate_changeset(struct of_changeset *ocs, | |||
| 184 | 218 | ||
| 185 | for_each_child_of_node(dn, child) { | 219 | for_each_child_of_node(dn, child) { |
| 186 | ret = of_changeset_attach_node(ocs, child); | 220 | ret = of_changeset_attach_node(ocs, child); |
| 187 | if (unlikely(ret)) | 221 | if (ret) |
| 188 | break; | 222 | break; |
| 189 | 223 | ||
| 190 | ret = pnv_php_populate_changeset(ocs, child); | 224 | ret = pnv_php_populate_changeset(ocs, child); |
| 191 | if (unlikely(ret)) | 225 | if (ret) |
| 192 | break; | 226 | break; |
| 193 | } | 227 | } |
| 194 | 228 | ||
| @@ -201,7 +235,7 @@ static void *pnv_php_add_one_pdn(struct device_node *dn, void *data) | |||
| 201 | struct pci_dn *pdn; | 235 | struct pci_dn *pdn; |
| 202 | 236 | ||
| 203 | pdn = pci_add_device_node_info(hose, dn); | 237 | pdn = pci_add_device_node_info(hose, dn); |
| 204 | if (unlikely(!pdn)) | 238 | if (!pdn) |
| 205 | return ERR_PTR(-ENOMEM); | 239 | return ERR_PTR(-ENOMEM); |
| 206 | 240 | ||
| 207 | return NULL; | 241 | return NULL; |
| @@ -224,21 +258,21 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) | |||
| 224 | * fits the real size. | 258 | * fits the real size. |
| 225 | */ | 259 | */ |
| 226 | fdt1 = kzalloc(0x10000, GFP_KERNEL); | 260 | fdt1 = kzalloc(0x10000, GFP_KERNEL); |
| 227 | if (unlikely(!fdt1)) { | 261 | if (!fdt1) { |
| 228 | ret = -ENOMEM; | 262 | ret = -ENOMEM; |
| 229 | dev_warn(&php_slot->pdev->dev, "Cannot alloc FDT blob\n"); | 263 | dev_warn(&php_slot->pdev->dev, "Cannot alloc FDT blob\n"); |
| 230 | goto out; | 264 | goto out; |
| 231 | } | 265 | } |
| 232 | 266 | ||
| 233 | ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000); | 267 | ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000); |
| 234 | if (unlikely(ret)) { | 268 | if (ret) { |
| 235 | dev_warn(&php_slot->pdev->dev, "Error %d getting FDT blob\n", | 269 | dev_warn(&php_slot->pdev->dev, "Error %d getting FDT blob\n", |
| 236 | ret); | 270 | ret); |
| 237 | goto free_fdt1; | 271 | goto free_fdt1; |
| 238 | } | 272 | } |
| 239 | 273 | ||
| 240 | fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL); | 274 | fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL); |
| 241 | if (unlikely(!fdt)) { | 275 | if (!fdt) { |
| 242 | ret = -ENOMEM; | 276 | ret = -ENOMEM; |
| 243 | dev_warn(&php_slot->pdev->dev, "Cannot %d bytes memory\n", | 277 | dev_warn(&php_slot->pdev->dev, "Cannot %d bytes memory\n", |
| 244 | fdt_totalsize(fdt1)); | 278 | fdt_totalsize(fdt1)); |
| @@ -248,7 +282,7 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) | |||
| 248 | /* Unflatten device tree blob */ | 282 | /* Unflatten device tree blob */ |
| 249 | memcpy(fdt, fdt1, fdt_totalsize(fdt1)); | 283 | memcpy(fdt, fdt1, fdt_totalsize(fdt1)); |
| 250 | dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL); | 284 | dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL); |
| 251 | if (unlikely(!dt)) { | 285 | if (!dt) { |
| 252 | ret = -EINVAL; | 286 | ret = -EINVAL; |
| 253 | dev_warn(&php_slot->pdev->dev, "Cannot unflatten FDT\n"); | 287 | dev_warn(&php_slot->pdev->dev, "Cannot unflatten FDT\n"); |
| 254 | goto free_fdt; | 288 | goto free_fdt; |
| @@ -258,7 +292,7 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) | |||
| 258 | of_changeset_init(&php_slot->ocs); | 292 | of_changeset_init(&php_slot->ocs); |
| 259 | pnv_php_reverse_nodes(php_slot->dn); | 293 | pnv_php_reverse_nodes(php_slot->dn); |
| 260 | ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn); | 294 | ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn); |
| 261 | if (unlikely(ret)) { | 295 | if (ret) { |
| 262 | pnv_php_reverse_nodes(php_slot->dn); | 296 | pnv_php_reverse_nodes(php_slot->dn); |
| 263 | dev_warn(&php_slot->pdev->dev, "Error %d populating changeset\n", | 297 | dev_warn(&php_slot->pdev->dev, "Error %d populating changeset\n", |
| 264 | ret); | 298 | ret); |
| @@ -267,7 +301,7 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) | |||
| 267 | 301 | ||
| 268 | php_slot->dn->child = NULL; | 302 | php_slot->dn->child = NULL; |
| 269 | ret = of_changeset_apply(&php_slot->ocs); | 303 | ret = of_changeset_apply(&php_slot->ocs); |
| 270 | if (unlikely(ret)) { | 304 | if (ret) { |
| 271 | dev_warn(&php_slot->pdev->dev, "Error %d applying changeset\n", | 305 | dev_warn(&php_slot->pdev->dev, "Error %d applying changeset\n", |
| 272 | ret); | 306 | ret); |
| 273 | goto destroy_changeset; | 307 | goto destroy_changeset; |
| @@ -301,7 +335,7 @@ int pnv_php_set_slot_power_state(struct hotplug_slot *slot, | |||
| 301 | int ret; | 335 | int ret; |
| 302 | 336 | ||
| 303 | ret = pnv_pci_set_power_state(php_slot->id, state, &msg); | 337 | ret = pnv_pci_set_power_state(php_slot->id, state, &msg); |
| 304 | if (likely(ret > 0)) { | 338 | if (ret > 0) { |
| 305 | if (be64_to_cpu(msg.params[1]) != php_slot->dn->phandle || | 339 | if (be64_to_cpu(msg.params[1]) != php_slot->dn->phandle || |
| 306 | be64_to_cpu(msg.params[2]) != state || | 340 | be64_to_cpu(msg.params[2]) != state || |
| 307 | be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) { | 341 | be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) { |
| @@ -311,7 +345,7 @@ int pnv_php_set_slot_power_state(struct hotplug_slot *slot, | |||
| 311 | be64_to_cpu(msg.params[3])); | 345 | be64_to_cpu(msg.params[3])); |
| 312 | return -ENOMSG; | 346 | return -ENOMSG; |
| 313 | } | 347 | } |
| 314 | } else if (unlikely(ret < 0)) { | 348 | } else if (ret < 0) { |
| 315 | dev_warn(&php_slot->pdev->dev, "Error %d powering %s\n", | 349 | dev_warn(&php_slot->pdev->dev, "Error %d powering %s\n", |
| 316 | ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off"); | 350 | ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off"); |
| 317 | return ret; | 351 | return ret; |
| @@ -338,7 +372,7 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state) | |||
| 338 | * be on. | 372 | * be on. |
| 339 | */ | 373 | */ |
| 340 | ret = pnv_pci_get_power_state(php_slot->id, &power_state); | 374 | ret = pnv_pci_get_power_state(php_slot->id, &power_state); |
| 341 | if (unlikely(ret)) { | 375 | if (ret) { |
| 342 | dev_warn(&php_slot->pdev->dev, "Error %d getting power status\n", | 376 | dev_warn(&php_slot->pdev->dev, "Error %d getting power status\n", |
| 343 | ret); | 377 | ret); |
| 344 | } else { | 378 | } else { |
| @@ -360,7 +394,7 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state) | |||
| 360 | * get that, it will fail back to be empty. | 394 | * get that, it will fail back to be empty. |
| 361 | */ | 395 | */ |
| 362 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); | 396 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); |
| 363 | if (likely(ret >= 0)) { | 397 | if (ret >= 0) { |
| 364 | *state = presence; | 398 | *state = presence; |
| 365 | slot->info->adapter_status = presence; | 399 | slot->info->adapter_status = presence; |
| 366 | ret = 0; | 400 | ret = 0; |
| @@ -393,7 +427,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) | |||
| 393 | 427 | ||
| 394 | /* Retrieve slot presence status */ | 428 | /* Retrieve slot presence status */ |
| 395 | ret = pnv_php_get_adapter_state(slot, &presence); | 429 | ret = pnv_php_get_adapter_state(slot, &presence); |
| 396 | if (unlikely(ret)) | 430 | if (ret) |
| 397 | return ret; | 431 | return ret; |
| 398 | 432 | ||
| 399 | /* Proceed if there have nothing behind the slot */ | 433 | /* Proceed if there have nothing behind the slot */ |
| @@ -414,7 +448,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) | |||
| 414 | php_slot->power_state_check = true; | 448 | php_slot->power_state_check = true; |
| 415 | 449 | ||
| 416 | ret = pnv_php_get_power_state(slot, &power_status); | 450 | ret = pnv_php_get_power_state(slot, &power_status); |
| 417 | if (unlikely(ret)) | 451 | if (ret) |
| 418 | return ret; | 452 | return ret; |
| 419 | 453 | ||
| 420 | if (power_status != OPAL_PCI_SLOT_POWER_ON) | 454 | if (power_status != OPAL_PCI_SLOT_POWER_ON) |
| @@ -423,7 +457,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) | |||
| 423 | 457 | ||
| 424 | /* Check the power status. Scan the slot if it is already on */ | 458 | /* Check the power status. Scan the slot if it is already on */ |
| 425 | ret = pnv_php_get_power_state(slot, &power_status); | 459 | ret = pnv_php_get_power_state(slot, &power_status); |
| 426 | if (unlikely(ret)) | 460 | if (ret) |
| 427 | return ret; | 461 | return ret; |
| 428 | 462 | ||
| 429 | if (power_status == OPAL_PCI_SLOT_POWER_ON) | 463 | if (power_status == OPAL_PCI_SLOT_POWER_ON) |
| @@ -431,7 +465,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) | |||
| 431 | 465 | ||
| 432 | /* Power is off, turn it on and then scan the slot */ | 466 | /* Power is off, turn it on and then scan the slot */ |
| 433 | ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON); | 467 | ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON); |
| 434 | if (unlikely(ret)) | 468 | if (ret) |
| 435 | return ret; | 469 | return ret; |
| 436 | 470 | ||
| 437 | scan: | 471 | scan: |
| @@ -513,29 +547,30 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) | |||
| 513 | struct pci_bus *bus; | 547 | struct pci_bus *bus; |
| 514 | const char *label; | 548 | const char *label; |
| 515 | uint64_t id; | 549 | uint64_t id; |
| 550 | int ret; | ||
| 516 | 551 | ||
| 517 | label = of_get_property(dn, "ibm,slot-label", NULL); | 552 | ret = of_property_read_string(dn, "ibm,slot-label", &label); |
| 518 | if (unlikely(!label)) | 553 | if (ret) |
| 519 | return NULL; | 554 | return NULL; |
| 520 | 555 | ||
| 521 | if (pnv_pci_get_slot_id(dn, &id)) | 556 | if (pnv_pci_get_slot_id(dn, &id)) |
| 522 | return NULL; | 557 | return NULL; |
| 523 | 558 | ||
| 524 | bus = pci_find_bus_by_node(dn); | 559 | bus = pci_find_bus_by_node(dn); |
| 525 | if (unlikely(!bus)) | 560 | if (!bus) |
| 526 | return NULL; | 561 | return NULL; |
| 527 | 562 | ||
| 528 | php_slot = kzalloc(sizeof(*php_slot), GFP_KERNEL); | 563 | php_slot = kzalloc(sizeof(*php_slot), GFP_KERNEL); |
| 529 | if (unlikely(!php_slot)) | 564 | if (!php_slot) |
| 530 | return NULL; | 565 | return NULL; |
| 531 | 566 | ||
| 532 | php_slot->name = kstrdup(label, GFP_KERNEL); | 567 | php_slot->name = kstrdup(label, GFP_KERNEL); |
| 533 | if (unlikely(!php_slot->name)) { | 568 | if (!php_slot->name) { |
| 534 | kfree(php_slot); | 569 | kfree(php_slot); |
| 535 | return NULL; | 570 | return NULL; |
| 536 | } | 571 | } |
| 537 | 572 | ||
| 538 | if (likely(dn->child && PCI_DN(dn->child))) | 573 | if (dn->child && PCI_DN(dn->child)) |
| 539 | php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn); | 574 | php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn); |
| 540 | else | 575 | else |
| 541 | php_slot->slot_no = -1; /* Placeholder slot */ | 576 | php_slot->slot_no = -1; /* Placeholder slot */ |
| @@ -567,7 +602,7 @@ static int pnv_php_register_slot(struct pnv_php_slot *php_slot) | |||
| 567 | 602 | ||
| 568 | /* Check if the slot is registered or not */ | 603 | /* Check if the slot is registered or not */ |
| 569 | parent = pnv_php_find_slot(php_slot->dn); | 604 | parent = pnv_php_find_slot(php_slot->dn); |
| 570 | if (unlikely(parent)) { | 605 | if (parent) { |
| 571 | pnv_php_put_slot(parent); | 606 | pnv_php_put_slot(parent); |
| 572 | return -EEXIST; | 607 | return -EEXIST; |
| 573 | } | 608 | } |
| @@ -575,7 +610,7 @@ static int pnv_php_register_slot(struct pnv_php_slot *php_slot) | |||
| 575 | /* Register PCI slot */ | 610 | /* Register PCI slot */ |
| 576 | ret = pci_hp_register(&php_slot->slot, php_slot->bus, | 611 | ret = pci_hp_register(&php_slot->slot, php_slot->bus, |
| 577 | php_slot->slot_no, php_slot->name); | 612 | php_slot->slot_no, php_slot->name); |
| 578 | if (unlikely(ret)) { | 613 | if (ret) { |
| 579 | dev_warn(&php_slot->pdev->dev, "Error %d registering slot\n", | 614 | dev_warn(&php_slot->pdev->dev, "Error %d registering slot\n", |
| 580 | ret); | 615 | ret); |
| 581 | return ret; | 616 | return ret; |
| @@ -609,33 +644,213 @@ static int pnv_php_register_slot(struct pnv_php_slot *php_slot) | |||
| 609 | return 0; | 644 | return 0; |
| 610 | } | 645 | } |
| 611 | 646 | ||
| 647 | static int pnv_php_enable_msix(struct pnv_php_slot *php_slot) | ||
| 648 | { | ||
| 649 | struct pci_dev *pdev = php_slot->pdev; | ||
| 650 | struct msix_entry entry; | ||
| 651 | int nr_entries, ret; | ||
| 652 | u16 pcie_flag; | ||
| 653 | |||
| 654 | /* Get total number of MSIx entries */ | ||
| 655 | nr_entries = pci_msix_vec_count(pdev); | ||
| 656 | if (nr_entries < 0) | ||
| 657 | return nr_entries; | ||
| 658 | |||
| 659 | /* Check hotplug MSIx entry is in range */ | ||
| 660 | pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &pcie_flag); | ||
| 661 | entry.entry = (pcie_flag & PCI_EXP_FLAGS_IRQ) >> 9; | ||
| 662 | if (entry.entry >= nr_entries) | ||
| 663 | return -ERANGE; | ||
| 664 | |||
| 665 | /* Enable MSIx */ | ||
| 666 | ret = pci_enable_msix_exact(pdev, &entry, 1); | ||
| 667 | if (ret) { | ||
| 668 | dev_warn(&pdev->dev, "Error %d enabling MSIx\n", ret); | ||
| 669 | return ret; | ||
| 670 | } | ||
| 671 | |||
| 672 | return entry.vector; | ||
| 673 | } | ||
| 674 | |||
| 675 | static void pnv_php_event_handler(struct work_struct *work) | ||
| 676 | { | ||
| 677 | struct pnv_php_event *event = | ||
| 678 | container_of(work, struct pnv_php_event, work); | ||
| 679 | struct pnv_php_slot *php_slot = event->php_slot; | ||
| 680 | |||
| 681 | if (event->added) | ||
| 682 | pnv_php_enable_slot(&php_slot->slot); | ||
| 683 | else | ||
| 684 | pnv_php_disable_slot(&php_slot->slot); | ||
| 685 | |||
| 686 | kfree(event); | ||
| 687 | } | ||
| 688 | |||
| 689 | static irqreturn_t pnv_php_interrupt(int irq, void *data) | ||
| 690 | { | ||
| 691 | struct pnv_php_slot *php_slot = data; | ||
| 692 | struct pci_dev *pchild, *pdev = php_slot->pdev; | ||
| 693 | struct eeh_dev *edev; | ||
| 694 | struct eeh_pe *pe; | ||
| 695 | struct pnv_php_event *event; | ||
| 696 | u16 sts, lsts; | ||
| 697 | u8 presence; | ||
| 698 | bool added; | ||
| 699 | unsigned long flags; | ||
| 700 | int ret; | ||
| 701 | |||
| 702 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); | ||
| 703 | sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); | ||
| 704 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); | ||
| 705 | if (sts & PCI_EXP_SLTSTA_DLLSC) { | ||
| 706 | pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); | ||
| 707 | added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); | ||
| 708 | } else if (sts & PCI_EXP_SLTSTA_PDC) { | ||
| 709 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); | ||
| 710 | if (!ret) | ||
| 711 | return IRQ_HANDLED; | ||
| 712 | added = !!(presence == OPAL_PCI_SLOT_PRESENT); | ||
| 713 | } else { | ||
| 714 | return IRQ_NONE; | ||
| 715 | } | ||
| 716 | |||
| 717 | /* Freeze the removed PE to avoid unexpected error reporting */ | ||
| 718 | if (!added) { | ||
| 719 | pchild = list_first_entry_or_null(&php_slot->bus->devices, | ||
| 720 | struct pci_dev, bus_list); | ||
| 721 | edev = pchild ? pci_dev_to_eeh_dev(pchild) : NULL; | ||
| 722 | pe = edev ? edev->pe : NULL; | ||
| 723 | if (pe) { | ||
| 724 | eeh_serialize_lock(&flags); | ||
| 725 | eeh_pe_state_mark(pe, EEH_PE_ISOLATED); | ||
| 726 | eeh_serialize_unlock(flags); | ||
| 727 | eeh_pe_set_option(pe, EEH_OPT_FREEZE_PE); | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | /* | ||
| 732 | * The PE is left in frozen state if the event is missed. It's | ||
| 733 | * fine as the PCI devices (PE) aren't functional any more. | ||
| 734 | */ | ||
| 735 | event = kzalloc(sizeof(*event), GFP_ATOMIC); | ||
| 736 | if (!event) { | ||
| 737 | dev_warn(&pdev->dev, "PCI slot [%s] missed hotplug event 0x%04x\n", | ||
| 738 | php_slot->name, sts); | ||
| 739 | return IRQ_HANDLED; | ||
| 740 | } | ||
| 741 | |||
| 742 | dev_info(&pdev->dev, "PCI slot [%s] %s (IRQ: %d)\n", | ||
| 743 | php_slot->name, added ? "added" : "removed", irq); | ||
| 744 | INIT_WORK(&event->work, pnv_php_event_handler); | ||
| 745 | event->added = added; | ||
| 746 | event->php_slot = php_slot; | ||
| 747 | queue_work(php_slot->wq, &event->work); | ||
| 748 | |||
| 749 | return IRQ_HANDLED; | ||
| 750 | } | ||
| 751 | |||
| 752 | static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) | ||
| 753 | { | ||
| 754 | struct pci_dev *pdev = php_slot->pdev; | ||
| 755 | u16 sts, ctrl; | ||
| 756 | int ret; | ||
| 757 | |||
| 758 | /* Allocate workqueue */ | ||
| 759 | php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); | ||
| 760 | if (!php_slot->wq) { | ||
| 761 | dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); | ||
| 762 | pnv_php_disable_irq(php_slot); | ||
| 763 | return; | ||
| 764 | } | ||
| 765 | |||
| 766 | /* Clear pending interrupts */ | ||
| 767 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); | ||
| 768 | sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); | ||
| 769 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); | ||
| 770 | |||
| 771 | /* Request the interrupt */ | ||
| 772 | ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, | ||
| 773 | php_slot->name, php_slot); | ||
| 774 | if (ret) { | ||
| 775 | pnv_php_disable_irq(php_slot); | ||
| 776 | dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq); | ||
| 777 | return; | ||
| 778 | } | ||
| 779 | |||
| 780 | /* Enable the interrupts */ | ||
| 781 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); | ||
| 782 | ctrl |= (PCI_EXP_SLTCTL_HPIE | | ||
| 783 | PCI_EXP_SLTCTL_PDCE | | ||
| 784 | PCI_EXP_SLTCTL_DLLSCE); | ||
| 785 | pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); | ||
| 786 | |||
| 787 | /* The interrupt is initialized successfully when @irq is valid */ | ||
| 788 | php_slot->irq = irq; | ||
| 789 | } | ||
| 790 | |||
| 791 | static void pnv_php_enable_irq(struct pnv_php_slot *php_slot) | ||
| 792 | { | ||
| 793 | struct pci_dev *pdev = php_slot->pdev; | ||
| 794 | int irq, ret; | ||
| 795 | |||
| 796 | ret = pci_enable_device(pdev); | ||
| 797 | if (ret) { | ||
| 798 | dev_warn(&pdev->dev, "Error %d enabling device\n", ret); | ||
| 799 | return; | ||
| 800 | } | ||
| 801 | |||
| 802 | pci_set_master(pdev); | ||
| 803 | |||
| 804 | /* Enable MSIx interrupt */ | ||
| 805 | irq = pnv_php_enable_msix(php_slot); | ||
| 806 | if (irq > 0) { | ||
| 807 | pnv_php_init_irq(php_slot, irq); | ||
| 808 | return; | ||
| 809 | } | ||
| 810 | |||
| 811 | /* | ||
| 812 | * Use MSI if MSIx doesn't work. Fail back to legacy INTx | ||
| 813 | * if MSI doesn't work either | ||
| 814 | */ | ||
| 815 | ret = pci_enable_msi(pdev); | ||
| 816 | if (!ret || pdev->irq) { | ||
| 817 | irq = pdev->irq; | ||
| 818 | pnv_php_init_irq(php_slot, irq); | ||
| 819 | } | ||
| 820 | } | ||
| 821 | |||
| 612 | static int pnv_php_register_one(struct device_node *dn) | 822 | static int pnv_php_register_one(struct device_node *dn) |
| 613 | { | 823 | { |
| 614 | struct pnv_php_slot *php_slot; | 824 | struct pnv_php_slot *php_slot; |
| 615 | const __be32 *prop32; | 825 | u32 prop32; |
| 616 | int ret; | 826 | int ret; |
| 617 | 827 | ||
| 618 | /* Check if it's hotpluggable slot */ | 828 | /* Check if it's hotpluggable slot */ |
| 619 | prop32 = of_get_property(dn, "ibm,slot-pluggable", NULL); | 829 | ret = of_property_read_u32(dn, "ibm,slot-pluggable", &prop32); |
| 620 | if (!prop32 || !of_read_number(prop32, 1)) | 830 | if (ret || !prop32) |
| 621 | return -ENXIO; | 831 | return -ENXIO; |
| 622 | 832 | ||
| 623 | prop32 = of_get_property(dn, "ibm,reset-by-firmware", NULL); | 833 | ret = of_property_read_u32(dn, "ibm,reset-by-firmware", &prop32); |
| 624 | if (!prop32 || !of_read_number(prop32, 1)) | 834 | if (ret || !prop32) |
| 625 | return -ENXIO; | 835 | return -ENXIO; |
| 626 | 836 | ||
| 627 | php_slot = pnv_php_alloc_slot(dn); | 837 | php_slot = pnv_php_alloc_slot(dn); |
| 628 | if (unlikely(!php_slot)) | 838 | if (!php_slot) |
| 629 | return -ENODEV; | 839 | return -ENODEV; |
| 630 | 840 | ||
| 631 | ret = pnv_php_register_slot(php_slot); | 841 | ret = pnv_php_register_slot(php_slot); |
| 632 | if (unlikely(ret)) | 842 | if (ret) |
| 633 | goto free_slot; | 843 | goto free_slot; |
| 634 | 844 | ||
| 635 | ret = pnv_php_enable(php_slot, false); | 845 | ret = pnv_php_enable(php_slot, false); |
| 636 | if (unlikely(ret)) | 846 | if (ret) |
| 637 | goto unregister_slot; | 847 | goto unregister_slot; |
| 638 | 848 | ||
| 849 | /* Enable interrupt if the slot supports surprise hotplug */ | ||
| 850 | ret = of_property_read_u32(dn, "ibm,slot-surprise-pluggable", &prop32); | ||
| 851 | if (!ret && prop32) | ||
| 852 | pnv_php_enable_irq(php_slot); | ||
| 853 | |||
| 639 | return 0; | 854 | return 0; |
| 640 | 855 | ||
| 641 | unregister_slot: | 856 | unregister_slot: |
