diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 20:19:31 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 20:19:31 -0700 |
commit | 07021b43597f506cc525d139ed1a94e79cf184f2 (patch) | |
tree | 888ab33ec69b397ae6f8a2c82d14197047ee827e /drivers | |
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')
-rw-r--r-- | drivers/char/hw_random/pasemi-rng.c | 2 | ||||
-rw-r--r-- | drivers/macintosh/macio_asic.c | 4 | ||||
-rw-r--r-- | drivers/macintosh/rack-meter.c | 2 | ||||
-rw-r--r-- | drivers/macintosh/smu.c | 18 | ||||
-rw-r--r-- | drivers/macintosh/via-cuda.c | 2 | ||||
-rw-r--r-- | drivers/macintosh/via-pmu.c | 6 | ||||
-rw-r--r-- | drivers/misc/cxl/cxl.h | 6 | ||||
-rw-r--r-- | drivers/misc/cxl/native.c | 31 | ||||
-rw-r--r-- | drivers/misc/cxl/of.c | 8 | ||||
-rw-r--r-- | drivers/misc/cxl/pci.c | 7 | ||||
-rw-r--r-- | drivers/pci/hotplug/pnv_php.c | 283 | ||||
-rw-r--r-- | drivers/ps3/ps3-vuart.c | 4 |
12 files changed, 312 insertions, 61 deletions
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 699b7259f5d7..c19e23d22b36 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -26,7 +26,7 @@ #include <linux/delay.h> #include <linux/of_address.h> #include <linux/of_platform.h> -#include <asm/io.h> +#include <linux/io.h> #define SDCRNG_CTL_REG 0x00 #define SDCRNG_CTL_FVLD_M 0x0000f000 diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index b6819f0fc608..3f041b187033 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -236,7 +236,7 @@ static void macio_create_fixup_irq(struct macio_dev *dev, int index, unsigned int irq; irq = irq_create_mapping(NULL, line); - if (irq != NO_IRQ) { + if (!irq) { dev->interrupt[index].start = irq; dev->interrupt[index].flags = IORESOURCE_IRQ; dev->interrupt[index].name = dev_name(&dev->ofdev.dev); @@ -299,7 +299,7 @@ static void macio_setup_interrupts(struct macio_dev *dev) break; res = &dev->interrupt[j]; irq = irq_of_parse_and_map(np, i++); - if (irq == NO_IRQ) + if (!irq) break; res->start = irq; res->flags = IORESOURCE_IRQ; diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index 465c52219639..775527135b93 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -427,7 +427,7 @@ static int rackmeter_probe(struct macio_dev* mdev, rm->irq = macio_irq(mdev, 1); #else rm->irq = irq_of_parse_and_map(i2s, 1); - if (rm->irq == NO_IRQ || + if (!rm->irq || of_address_to_resource(i2s, 0, &ri2s) || of_address_to_resource(i2s, 1, &rdma)) { printk(KERN_ERR diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index d6f72c826c1c..08edb2c25b60 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -279,7 +279,7 @@ int smu_queue_cmd(struct smu_cmd *cmd) spin_unlock_irqrestore(&smu->lock, flags); /* Workaround for early calls when irq isn't available */ - if (!smu_irq_inited || smu->db_irq == NO_IRQ) + if (!smu_irq_inited || !smu->db_irq) smu_spinwait_cmd(cmd); return 0; @@ -498,8 +498,8 @@ int __init smu_init (void) INIT_LIST_HEAD(&smu->cmd_list); INIT_LIST_HEAD(&smu->cmd_i2c_list); smu->of_node = np; - smu->db_irq = NO_IRQ; - smu->msg_irq = NO_IRQ; + smu->db_irq = 0; + smu->msg_irq = 0; /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a * 32 bits value safely @@ -587,13 +587,13 @@ static int smu_late_init(void) if (smu->db_node) { smu->db_irq = irq_of_parse_and_map(smu->db_node, 0); - if (smu->db_irq == NO_IRQ) + if (!smu->db_irq) printk(KERN_ERR "smu: failed to map irq for node %s\n", smu->db_node->full_name); } if (smu->msg_node) { smu->msg_irq = irq_of_parse_and_map(smu->msg_node, 0); - if (smu->msg_irq == NO_IRQ) + if (!smu->msg_irq) printk(KERN_ERR "smu: failed to map irq for node %s\n", smu->msg_node->full_name); } @@ -602,23 +602,23 @@ static int smu_late_init(void) * Try to request the interrupts */ - if (smu->db_irq != NO_IRQ) { + if (smu->db_irq) { if (request_irq(smu->db_irq, smu_db_intr, IRQF_SHARED, "SMU doorbell", smu) < 0) { printk(KERN_WARNING "SMU: can't " "request interrupt %d\n", smu->db_irq); - smu->db_irq = NO_IRQ; + smu->db_irq = 0; } } - if (smu->msg_irq != NO_IRQ) { + if (smu->msg_irq) { if (request_irq(smu->msg_irq, smu_msg_intr, IRQF_SHARED, "SMU message", smu) < 0) { printk(KERN_WARNING "SMU: can't " "request interrupt %d\n", smu->msg_irq); - smu->msg_irq = NO_IRQ; + smu->msg_irq = 0; } } diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index bad18130f125..2088e23a8002 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -209,7 +209,7 @@ static int __init via_cuda_start(void) cuda_irq = IRQ_MAC_ADB; #else cuda_irq = irq_of_parse_and_map(vias, 0); - if (cuda_irq == NO_IRQ) { + if (!cuda_irq) { printk(KERN_ERR "via-cuda: can't map interrupts for %s\n", vias->full_name); return -ENODEV; diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index f8b6d1403c16..91081dcdc272 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -145,7 +145,7 @@ static int pmu_fully_inited; static int pmu_has_adb; static struct device_node *gpio_node; static unsigned char __iomem *gpio_reg; -static int gpio_irq = NO_IRQ; +static int gpio_irq = 0; static int gpio_irq_enabled = -1; static volatile int pmu_suspended; static spinlock_t pmu_lock; @@ -402,7 +402,7 @@ static int __init via_pmu_start(void) batt_req.complete = 1; irq = irq_of_parse_and_map(vias, 0); - if (irq == NO_IRQ) { + if (!irq) { printk(KERN_ERR "via-pmu: can't map interrupt\n"); return -ENODEV; } @@ -424,7 +424,7 @@ static int __init via_pmu_start(void) if (gpio_node) gpio_irq = irq_of_parse_and_map(gpio_node, 0); - if (gpio_irq != NO_IRQ) { + if (gpio_irq) { if (request_irq(gpio_irq, gpio1_interrupt, IRQF_NO_SUSPEND, "GPIO1 ADB", (void *)0)) diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 344a0ff8f8c7..01d372aba131 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -162,7 +162,10 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PSL_SPAP_V 0x0000000000000001ULL /****** CXL_PSL_Control ****************************************************/ -#define CXL_PSL_Control_tb 0x0000000000000001ULL +#define CXL_PSL_Control_tb (0x1ull << (63-63)) +#define CXL_PSL_Control_Fr (0x1ull << (63-31)) +#define CXL_PSL_Control_Fs_MASK (0x3ull << (63-29)) +#define CXL_PSL_Control_Fs_Complete (0x3ull << (63-29)) /****** CXL_PSL_DLCNTL *****************************************************/ #define CXL_PSL_DLCNTL_D (0x1ull << (63-28)) @@ -854,6 +857,7 @@ int cxl_register_one_irq(struct cxl *adapter, irq_handler_t handler, int cxl_check_error(struct cxl_afu *afu); int cxl_afu_slbia(struct cxl_afu *afu); int cxl_tlb_slb_invalidate(struct cxl *adapter); +int cxl_data_cache_flush(struct cxl *adapter); int cxl_afu_disable(struct cxl_afu *afu); int cxl_psl_purge(struct cxl_afu *afu); diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index e606fdc4bc9c..a217a74ccc98 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -290,6 +290,37 @@ int cxl_tlb_slb_invalidate(struct cxl *adapter) return 0; } +int cxl_data_cache_flush(struct cxl *adapter) +{ + u64 reg; + unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + + pr_devel("Flushing data cache\n"); + + reg = cxl_p1_read(adapter, CXL_PSL_Control); + reg |= CXL_PSL_Control_Fr; + cxl_p1_write(adapter, CXL_PSL_Control, reg); + + reg = cxl_p1_read(adapter, CXL_PSL_Control); + while ((reg & CXL_PSL_Control_Fs_MASK) != CXL_PSL_Control_Fs_Complete) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&adapter->dev, "WARNING: cache flush timed out!\n"); + return -EBUSY; + } + + if (!cxl_ops->link_ok(adapter, NULL)) { + dev_warn(&adapter->dev, "WARNING: link down when flushing cache\n"); + return -EIO; + } + cpu_relax(); + reg = cxl_p1_read(adapter, CXL_PSL_Control); + } + + reg &= ~CXL_PSL_Control_Fr; + cxl_p1_write(adapter, CXL_PSL_Control, reg); + return 0; +} + static int cxl_write_sstp(struct cxl_afu *afu, u64 sstp0, u64 sstp1) { int rc; diff --git a/drivers/misc/cxl/of.c b/drivers/misc/cxl/of.c index edc458395f68..ec175ea5dfba 100644 --- a/drivers/misc/cxl/of.c +++ b/drivers/misc/cxl/of.c @@ -460,7 +460,7 @@ int cxl_of_probe(struct platform_device *pdev) struct device_node *afu_np = NULL; struct cxl *adapter = NULL; int ret; - int slice, slice_ok; + int slice = 0, slice_ok = 0; pr_devel("in %s\n", __func__); @@ -476,13 +476,13 @@ int cxl_of_probe(struct platform_device *pdev) } /* init afu */ - slice_ok = 0; - for (afu_np = NULL, slice = 0; (afu_np = of_get_next_child(np, afu_np)); slice++) { + for_each_child_of_node(np, afu_np) { if ((ret = cxl_guest_init_afu(adapter, slice, afu_np))) dev_err(&pdev->dev, "AFU %i failed to initialise: %i\n", slice, ret); else slice_ok++; + slice++; } if (slice_ok == 0) { @@ -490,8 +490,6 @@ int cxl_of_probe(struct platform_device *pdev) adapter->slices = 0; } - if (afu_np) - of_node_put(afu_np); return 0; } diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 6f0c4ac4b649..7afad8477ad5 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -1239,6 +1239,9 @@ int cxl_pci_reset(struct cxl *adapter) dev_info(&dev->dev, "CXL reset\n"); + /* the adapter is about to be reset, so ignore errors */ + cxl_data_cache_flush(adapter); + /* pcie_warm_reset requests a fundamental pci reset which includes a * PERST assert/deassert. PERST triggers a loading of the image * if "user" or "factory" is selected in sysfs */ @@ -1530,11 +1533,11 @@ static void set_sl_ops(struct cxl *adapter, struct pci_dev *dev) { if (dev->vendor == PCI_VENDOR_ID_MELLANOX && dev->device == 0x1013) { /* Mellanox CX-4 */ - dev_info(&adapter->dev, "Device uses an XSL\n"); + dev_info(&dev->dev, "Device uses an XSL\n"); adapter->native->sl_ops = &xsl_ops; adapter->min_pe = 1; /* Workaround for CX-4 hardware bug */ } else { - dev_info(&adapter->dev, "Device uses a PSL\n"); + dev_info(&dev->dev, "Device uses a PSL\n"); adapter->native->sl_ops = &psl_ops; } } 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 @@ #define DRIVER_AUTHOR "Gavin Shan, IBM Corporation" #define DRIVER_DESC "PowerPC PowerNV PCI Hotplug Driver" +struct pnv_php_event { + bool added; + struct pnv_php_slot *php_slot; + struct work_struct work; +}; + static LIST_HEAD(pnv_php_slot_list); static DEFINE_SPINLOCK(pnv_php_lock); @@ -29,12 +35,40 @@ static void pnv_php_register(struct device_node *dn); static void pnv_php_unregister_one(struct device_node *dn); static void pnv_php_unregister(struct device_node *dn); +static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) +{ + struct pci_dev *pdev = php_slot->pdev; + u16 ctrl; + + if (php_slot->irq > 0) { + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); + ctrl &= ~(PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_DLLSCE); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); + + free_irq(php_slot->irq, php_slot); + php_slot->irq = 0; + } + + if (php_slot->wq) { + destroy_workqueue(php_slot->wq); + php_slot->wq = NULL; + } + + if (pdev->msix_enabled) + pci_disable_msix(pdev); + else if (pdev->msi_enabled) + pci_disable_msi(pdev); +} + static void pnv_php_free_slot(struct kref *kref) { struct pnv_php_slot *php_slot = container_of(kref, struct pnv_php_slot, kref); WARN_ON(!list_empty(&php_slot->children)); + pnv_php_disable_irq(php_slot); kfree(php_slot->name); kfree(php_slot); } @@ -122,7 +156,7 @@ static void pnv_php_detach_device_nodes(struct device_node *parent) of_node_put(dn); refcount = atomic_read(&dn->kobj.kref.refcount); - if (unlikely(refcount != 1)) + if (refcount != 1) pr_warn("Invalid refcount %d on <%s>\n", refcount, of_node_full_name(dn)); @@ -184,11 +218,11 @@ static int pnv_php_populate_changeset(struct of_changeset *ocs, for_each_child_of_node(dn, child) { ret = of_changeset_attach_node(ocs, child); - if (unlikely(ret)) + if (ret) break; ret = pnv_php_populate_changeset(ocs, child); - if (unlikely(ret)) + if (ret) break; } @@ -201,7 +235,7 @@ static void *pnv_php_add_one_pdn(struct device_node *dn, void *data) struct pci_dn *pdn; pdn = pci_add_device_node_info(hose, dn); - if (unlikely(!pdn)) + if (!pdn) return ERR_PTR(-ENOMEM); return NULL; @@ -224,21 +258,21 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) * fits the real size. */ fdt1 = kzalloc(0x10000, GFP_KERNEL); - if (unlikely(!fdt1)) { + if (!fdt1) { ret = -ENOMEM; dev_warn(&php_slot->pdev->dev, "Cannot alloc FDT blob\n"); goto out; } ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000); - if (unlikely(ret)) { + if (ret) { dev_warn(&php_slot->pdev->dev, "Error %d getting FDT blob\n", ret); goto free_fdt1; } fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL); - if (unlikely(!fdt)) { + if (!fdt) { ret = -ENOMEM; dev_warn(&php_slot->pdev->dev, "Cannot %d bytes memory\n", fdt_totalsize(fdt1)); @@ -248,7 +282,7 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) /* Unflatten device tree blob */ memcpy(fdt, fdt1, fdt_totalsize(fdt1)); dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL); - if (unlikely(!dt)) { + if (!dt) { ret = -EINVAL; dev_warn(&php_slot->pdev->dev, "Cannot unflatten FDT\n"); goto free_fdt; @@ -258,7 +292,7 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) of_changeset_init(&php_slot->ocs); pnv_php_reverse_nodes(php_slot->dn); ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn); - if (unlikely(ret)) { + if (ret) { pnv_php_reverse_nodes(php_slot->dn); dev_warn(&php_slot->pdev->dev, "Error %d populating changeset\n", ret); @@ -267,7 +301,7 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot) php_slot->dn->child = NULL; ret = of_changeset_apply(&php_slot->ocs); - if (unlikely(ret)) { + if (ret) { dev_warn(&php_slot->pdev->dev, "Error %d applying changeset\n", ret); goto destroy_changeset; @@ -301,7 +335,7 @@ int pnv_php_set_slot_power_state(struct hotplug_slot *slot, int ret; ret = pnv_pci_set_power_state(php_slot->id, state, &msg); - if (likely(ret > 0)) { + if (ret > 0) { if (be64_to_cpu(msg.params[1]) != php_slot->dn->phandle || be64_to_cpu(msg.params[2]) != state || be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) { @@ -311,7 +345,7 @@ int pnv_php_set_slot_power_state(struct hotplug_slot *slot, be64_to_cpu(msg.params[3])); return -ENOMSG; } - } else if (unlikely(ret < 0)) { + } else if (ret < 0) { dev_warn(&php_slot->pdev->dev, "Error %d powering %s\n", ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off"); return ret; @@ -338,7 +372,7 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state) * be on. */ ret = pnv_pci_get_power_state(php_slot->id, &power_state); - if (unlikely(ret)) { + if (ret) { dev_warn(&php_slot->pdev->dev, "Error %d getting power status\n", ret); } else { @@ -360,7 +394,7 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state) * get that, it will fail back to be empty. */ ret = pnv_pci_get_presence_state(php_slot->id, &presence); - if (likely(ret >= 0)) { + if (ret >= 0) { *state = presence; slot->info->adapter_status = presence; ret = 0; @@ -393,7 +427,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) /* Retrieve slot presence status */ ret = pnv_php_get_adapter_state(slot, &presence); - if (unlikely(ret)) + if (ret) return ret; /* 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) php_slot->power_state_check = true; ret = pnv_php_get_power_state(slot, &power_status); - if (unlikely(ret)) + if (ret) return ret; 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) /* Check the power status. Scan the slot if it is already on */ ret = pnv_php_get_power_state(slot, &power_status); - if (unlikely(ret)) + if (ret) return ret; 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) /* Power is off, turn it on and then scan the slot */ ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON); - if (unlikely(ret)) + if (ret) return ret; scan: @@ -513,29 +547,30 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) struct pci_bus *bus; const char *label; uint64_t id; + int ret; - label = of_get_property(dn, "ibm,slot-label", NULL); - if (unlikely(!label)) + ret = of_property_read_string(dn, "ibm,slot-label", &label); + if (ret) return NULL; if (pnv_pci_get_slot_id(dn, &id)) return NULL; bus = pci_find_bus_by_node(dn); - if (unlikely(!bus)) + if (!bus) return NULL; php_slot = kzalloc(sizeof(*php_slot), GFP_KERNEL); - if (unlikely(!php_slot)) + if (!php_slot) return NULL; php_slot->name = kstrdup(label, GFP_KERNEL); - if (unlikely(!php_slot->name)) { + if (!php_slot->name) { kfree(php_slot); return NULL; } - if (likely(dn->child && PCI_DN(dn->child))) + if (dn->child && PCI_DN(dn->child)) php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn); else php_slot->slot_no = -1; /* Placeholder slot */ @@ -567,7 +602,7 @@ static int pnv_php_register_slot(struct pnv_php_slot *php_slot) /* Check if the slot is registered or not */ parent = pnv_php_find_slot(php_slot->dn); - if (unlikely(parent)) { + if (parent) { pnv_php_put_slot(parent); return -EEXIST; } @@ -575,7 +610,7 @@ static int pnv_php_register_slot(struct pnv_php_slot *php_slot) /* Register PCI slot */ ret = pci_hp_register(&php_slot->slot, php_slot->bus, php_slot->slot_no, php_slot->name); - if (unlikely(ret)) { + if (ret) { dev_warn(&php_slot->pdev->dev, "Error %d registering slot\n", ret); return ret; @@ -609,33 +644,213 @@ static int pnv_php_register_slot(struct pnv_php_slot *php_slot) return 0; } +static int pnv_php_enable_msix(struct pnv_php_slot *php_slot) +{ + struct pci_dev *pdev = php_slot->pdev; + struct msix_entry entry; + int nr_entries, ret; + u16 pcie_flag; + + /* Get total number of MSIx entries */ + nr_entries = pci_msix_vec_count(pdev); + if (nr_entries < 0) + return nr_entries; + + /* Check hotplug MSIx entry is in range */ + pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &pcie_flag); + entry.entry = (pcie_flag & PCI_EXP_FLAGS_IRQ) >> 9; + if (entry.entry >= nr_entries) + return -ERANGE; + + /* Enable MSIx */ + ret = pci_enable_msix_exact(pdev, &entry, 1); + if (ret) { + dev_warn(&pdev->dev, "Error %d enabling MSIx\n", ret); + return ret; + } + + return entry.vector; +} + +static void pnv_php_event_handler(struct work_struct *work) +{ + struct pnv_php_event *event = + container_of(work, struct pnv_php_event, work); + struct pnv_php_slot *php_slot = event->php_slot; + + if (event->added) + pnv_php_enable_slot(&php_slot->slot); + else + pnv_php_disable_slot(&php_slot->slot); + + kfree(event); +} + +static irqreturn_t pnv_php_interrupt(int irq, void *data) +{ + struct pnv_php_slot *php_slot = data; + struct pci_dev *pchild, *pdev = php_slot->pdev; + struct eeh_dev *edev; + struct eeh_pe *pe; + struct pnv_php_event *event; + u16 sts, lsts; + u8 presence; + bool added; + unsigned long flags; + int ret; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); + sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); + if (sts & PCI_EXP_SLTSTA_DLLSC) { + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); + added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); + } else if (sts & PCI_EXP_SLTSTA_PDC) { + ret = pnv_pci_get_presence_state(php_slot->id, &presence); + if (!ret) + return IRQ_HANDLED; + added = !!(presence == OPAL_PCI_SLOT_PRESENT); + } else { + return IRQ_NONE; + } + + /* Freeze the removed PE to avoid unexpected error reporting */ + if (!added) { + pchild = list_first_entry_or_null(&php_slot->bus->devices, + struct pci_dev, bus_list); + edev = pchild ? pci_dev_to_eeh_dev(pchild) : NULL; + pe = edev ? edev->pe : NULL; + if (pe) { + eeh_serialize_lock(&flags); + eeh_pe_state_mark(pe, EEH_PE_ISOLATED); + eeh_serialize_unlock(flags); + eeh_pe_set_option(pe, EEH_OPT_FREEZE_PE); + } + } + + /* + * The PE is left in frozen state if the event is missed. It's + * fine as the PCI devices (PE) aren't functional any more. + */ + event = kzalloc(sizeof(*event), GFP_ATOMIC); + if (!event) { + dev_warn(&pdev->dev, "PCI slot [%s] missed hotplug event 0x%04x\n", + php_slot->name, sts); + return IRQ_HANDLED; + } + + dev_info(&pdev->dev, "PCI slot [%s] %s (IRQ: %d)\n", + php_slot->name, added ? "added" : "removed", irq); + INIT_WORK(&event->work, pnv_php_event_handler); + event->added = added; + event->php_slot = php_slot; + queue_work(php_slot->wq, &event->work); + + return IRQ_HANDLED; +} + +static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) +{ + struct pci_dev *pdev = php_slot->pdev; + u16 sts, ctrl; + int ret; + + /* Allocate workqueue */ + php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); + if (!php_slot->wq) { + dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); + pnv_php_disable_irq(php_slot); + return; + } + + /* Clear pending interrupts */ + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); + sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); + + /* Request the interrupt */ + ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, + php_slot->name, php_slot); + if (ret) { + pnv_php_disable_irq(php_slot); + dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq); + return; + } + + /* Enable the interrupts */ + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); + ctrl |= (PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_DLLSCE); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); + + /* The interrupt is initialized successfully when @irq is valid */ + php_slot->irq = irq; +} + +static void pnv_php_enable_irq(struct pnv_php_slot *php_slot) +{ + struct pci_dev *pdev = php_slot->pdev; + int irq, ret; + + ret = pci_enable_device(pdev); + if (ret) { + dev_warn(&pdev->dev, "Error %d enabling device\n", ret); + return; + } + + pci_set_master(pdev); + + /* Enable MSIx interrupt */ + irq = pnv_php_enable_msix(php_slot); + if (irq > 0) { + pnv_php_init_irq(php_slot, irq); + return; + } + + /* + * Use MSI if MSIx doesn't work. Fail back to legacy INTx + * if MSI doesn't work either + */ + ret = pci_enable_msi(pdev); + if (!ret || pdev->irq) { + irq = pdev->irq; + pnv_php_init_irq(php_slot, irq); + } +} + static int pnv_php_register_one(struct device_node *dn) { struct pnv_php_slot *php_slot; - const __be32 *prop32; + u32 prop32; int ret; /* Check if it's hotpluggable slot */ - prop32 = of_get_property(dn, "ibm,slot-pluggable", NULL); - if (!prop32 || !of_read_number(prop32, 1)) + ret = of_property_read_u32(dn, "ibm,slot-pluggable", &prop32); + if (ret || !prop32) return -ENXIO; - prop32 = of_get_property(dn, "ibm,reset-by-firmware", NULL); - if (!prop32 || !of_read_number(prop32, 1)) + ret = of_property_read_u32(dn, "ibm,reset-by-firmware", &prop32); + if (ret || !prop32) return -ENXIO; php_slot = pnv_php_alloc_slot(dn); - if (unlikely(!php_slot)) + if (!php_slot) return -ENODEV; ret = pnv_php_register_slot(php_slot); - if (unlikely(ret)) + if (ret) goto free_slot; ret = pnv_php_enable(php_slot, false); - if (unlikely(ret)) + if (ret) goto unregister_slot; + /* Enable interrupt if the slot supports surprise hotplug */ + ret = of_property_read_u32(dn, "ibm,slot-surprise-pluggable", &prop32); + if (!ret && prop32) + pnv_php_enable_irq(php_slot); + return 0; unregister_slot: diff --git a/drivers/ps3/ps3-vuart.c b/drivers/ps3/ps3-vuart.c index 632701a1d993..b7f300b79ffd 100644 --- a/drivers/ps3/ps3-vuart.c +++ b/drivers/ps3/ps3-vuart.c @@ -958,7 +958,7 @@ static int ps3_vuart_bus_interrupt_get(void) fail_request_irq: ps3_vuart_irq_destroy(vuart_bus_priv.virq); - vuart_bus_priv.virq = NO_IRQ; + vuart_bus_priv.virq = 0; fail_alloc_irq: kfree(vuart_bus_priv.bmp); vuart_bus_priv.bmp = NULL; @@ -982,7 +982,7 @@ static int ps3_vuart_bus_interrupt_put(void) free_irq(vuart_bus_priv.virq, &vuart_bus_priv); ps3_vuart_irq_destroy(vuart_bus_priv.virq); - vuart_bus_priv.virq = NO_IRQ; + vuart_bus_priv.virq = 0; kfree(vuart_bus_priv.bmp); vuart_bus_priv.bmp = NULL; |