diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/eisa/eisa-bus.c | 4 | ||||
-rw-r--r-- | drivers/pci/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/bus.c | 1 | ||||
-rw-r--r-- | drivers/pci/host/pci-mvebu.c | 91 | ||||
-rw-r--r-- | drivers/pci/host/pci-rcar-gen2.c | 12 | ||||
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 2 | ||||
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 59 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 14 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 15 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 90 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 380 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_pci.c | 6 | ||||
-rw-r--r-- | drivers/pci/iov.c | 1 | ||||
-rw-r--r-- | drivers/pci/msi.c | 19 | ||||
-rw-r--r-- | drivers/pci/pci-acpi.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 38 | ||||
-rw-r--r-- | drivers/pci/pci.c | 102 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_acpi.c | 48 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_errprint.c | 95 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 2 | ||||
-rw-r--r-- | drivers/pci/probe.c | 23 | ||||
-rw-r--r-- | drivers/pci/remove.c | 25 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 16 | ||||
-rw-r--r-- | drivers/pci/vc.c | 434 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_config.c | 12 |
25 files changed, 959 insertions, 534 deletions
diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 272a3ec3595..8842cde6917 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -232,8 +232,10 @@ static int __init eisa_init_device(struct eisa_root_device *root, static int __init eisa_register_device(struct eisa_device *edev) { int rc = device_register(&edev->dev); - if (rc) + if (rc) { + put_device(&edev->dev); return rc; + } rc = device_create_file(&edev->dev, &dev_attr_signature); if (rc) diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6ebf5bf8e7a..17d2b07ee67 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -4,7 +4,7 @@ obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ - irq.o vpd.o setup-bus.o + irq.o vpd.o setup-bus.o vc.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index fc1b7401374..a037d81f21e 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -176,6 +176,7 @@ int pci_bus_add_device(struct pci_dev *dev) */ pci_fixup_device(pci_fixup_final, dev); pci_create_sysfs_dev_files(dev); + pci_proc_attach_device(dev); dev->match_driver = true; retval = device_attach(&dev->dev); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index c269e430c76..533e0df0271 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -150,6 +150,11 @@ static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg) return readl(port->base + reg); } +static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) +{ + return port->io_target != -1 && port->io_attr != -1; +} + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) { return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); @@ -300,7 +305,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) /* Are the new iobase/iolimit values invalid? */ if (port->bridge.iolimit < port->bridge.iobase || - port->bridge.iolimitupper < port->bridge.iobaseupper) { + port->bridge.iolimitupper < port->bridge.iobaseupper || + !(port->bridge.command & PCI_COMMAND_IO)) { /* If a window was configured, remove it */ if (port->iowin_base) { @@ -313,6 +319,12 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) return; } + if (!mvebu_has_ioport(port)) { + dev_WARN(&port->pcie->pdev->dev, + "Attempt to set IO when IO is disabled\n"); + return; + } + /* * We read the PCI-to-PCI bridge emulated registers, and * calculate the base address and size of the address decoding @@ -337,7 +349,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) { /* Are the new membase/memlimit values invalid? */ - if (port->bridge.memlimit < port->bridge.membase) { + if (port->bridge.memlimit < port->bridge.membase || + !(port->bridge.command & PCI_COMMAND_MEMORY)) { /* If a window was configured, remove it */ if (port->memwin_base) { @@ -426,9 +439,12 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port, break; case PCI_IO_BASE: - *value = (bridge->secondary_status << 16 | - bridge->iolimit << 8 | - bridge->iobase); + if (!mvebu_has_ioport(port)) + *value = bridge->secondary_status << 16; + else + *value = (bridge->secondary_status << 16 | + bridge->iolimit << 8 | + bridge->iobase); break; case PCI_MEMORY_BASE: @@ -447,6 +463,11 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port, *value = 0; break; + case PCI_INTERRUPT_LINE: + /* LINE PIN MIN_GNT MAX_LAT */ + *value = 0; + break; + default: *value = 0xffffffff; return PCIBIOS_BAD_REGISTER_NUMBER; @@ -485,8 +506,19 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, switch (where & ~3) { case PCI_COMMAND: + { + u32 old = bridge->command; + + if (!mvebu_has_ioport(port)) + value &= ~PCI_COMMAND_IO; + bridge->command = value & 0xffff; + if ((old ^ bridge->command) & PCI_COMMAND_IO) + mvebu_pcie_handle_iobase_change(port); + if ((old ^ bridge->command) & PCI_COMMAND_MEMORY) + mvebu_pcie_handle_membase_change(port); break; + } case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value; @@ -500,7 +532,6 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, */ bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32; bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32; - bridge->secondary_status = value >> 16; mvebu_pcie_handle_iobase_change(port); break; @@ -651,7 +682,9 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; - pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); + if (resource_size(&pcie->realio) != 0) + pci_add_resource_offset(&sys->resources, &pcie->realio, + sys->io_offset); pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); @@ -752,12 +785,17 @@ static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, #define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF) static int mvebu_get_tgt_attr(struct device_node *np, int devfn, - unsigned long type, int *tgt, int *attr) + unsigned long type, + unsigned int *tgt, + unsigned int *attr) { const int na = 3, ns = 2; const __be32 *range; int rlen, nranges, rangesz, pna, i; + *tgt = -1; + *attr = -1; + range = of_get_property(np, "ranges", &rlen); if (!range) return -EINVAL; @@ -827,16 +865,15 @@ static int mvebu_pcie_probe(struct platform_device *pdev) } mvebu_mbus_get_pcie_io_aperture(&pcie->io); - if (resource_size(&pcie->io) == 0) { - dev_err(&pdev->dev, "invalid I/O aperture size\n"); - return -EINVAL; - } - pcie->realio.flags = pcie->io.flags; - pcie->realio.start = PCIBIOS_MIN_IO; - pcie->realio.end = min_t(resource_size_t, - IO_SPACE_LIMIT, - resource_size(&pcie->io)); + if (resource_size(&pcie->io) != 0) { + pcie->realio.flags = pcie->io.flags; + pcie->realio.start = PCIBIOS_MIN_IO; + pcie->realio.end = min_t(resource_size_t, + IO_SPACE_LIMIT, + resource_size(&pcie->io)); + } else + pcie->realio = pcie->io; /* Get the bus range */ ret = of_pci_parse_bus_range(np, &pcie->busn); @@ -895,12 +932,12 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } - ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO, - &port->io_target, &port->io_attr); - if (ret < 0) { - dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for io window\n", - port->port, port->lane); - continue; + if (resource_size(&pcie->io) != 0) + mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO, + &port->io_target, &port->io_attr); + else { + port->io_target = -1; + port->io_attr = -1; } port->reset_gpio = of_get_named_gpio_flags(child, @@ -949,14 +986,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) mvebu_pcie_set_local_dev_nr(port, 1); - port->clk = of_clk_get_by_name(child, NULL); - if (IS_ERR(port->clk)) { - dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n", - port->port, port->lane); - iounmap(port->base); - continue; - } - port->dn = child; spin_lock_init(&port->conf_lock); mvebu_sw_pci_bridge_init(port); diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index cbaa5c4397e..ceec147baec 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> /* AHB-PCI Bridge PCI communication registers */ @@ -77,6 +78,7 @@ #define RCAR_PCI_NR_CONTROLLERS 3 struct rcar_pci_priv { + struct device *dev; void __iomem *reg; struct resource io_res; struct resource mem_res; @@ -169,8 +171,11 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) void __iomem *reg = priv->reg; u32 val; + pm_runtime_enable(priv->dev); + pm_runtime_get_sync(priv->dev); + val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); - pr_info("PCI: bus%u revision %x\n", sys->busnr, val); + dev_info(priv->dev, "PCI: bus%u revision %x\n", sys->busnr, val); /* Disable Direct Power Down State and assert reset */ val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; @@ -276,8 +281,8 @@ static int __init rcar_pci_probe(struct platform_device *pdev) cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, cfg_res); - if (!reg) - return -ENODEV; + if (IS_ERR(reg)) + return PTR_ERR(reg); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!mem_res || !mem_res->start) @@ -301,6 +306,7 @@ static int __init rcar_pci_probe(struct platform_device *pdev) priv->irq = platform_get_irq(pdev, 0); priv->reg = reg; + priv->dev = &pdev->dev; return rcar_pci_add_controller(priv); } diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 0afbbbc55c8..b8ba2f79455 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -805,7 +805,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_PCIE_CONFIG); value = afi_readl(pcie, AFI_FUSE); - value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; afi_writel(pcie, value, AFI_FUSE); /* initialize internal PHY, enable up to 16 PCIE lanes */ diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index e33b68be039..1c92833a4ed 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -209,6 +209,23 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return 0; } +static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, + unsigned int nvec, unsigned int pos) +{ + unsigned int i, res, bit, val; + + for (i = 0; i < nvec; i++) { + irq_set_msi_desc_off(irq_base, i, NULL); + clear_bit(pos + i, pp->msi_irq_in_use); + /* Disable corresponding interrupt on MSI interrupt controller */ + res = ((pos + i) / 32) * 12; + bit = (pos + i) % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + } +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int res, bit, irq, pos0, pos1, i; @@ -242,18 +259,25 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) if (!irq) goto no_valid_irq; - i = 0; - while (i < no_irqs) { + /* + * irq_create_mapping (called from dw_pcie_host_init) pre-allocates + * descs so there is no need to allocate descs here. We can therefore + * assume that if irq_find_mapping above returns non-zero, then the + * descs are also successfully allocated. + */ + + for (i = 0; i < no_irqs; i++) { + if (irq_set_msi_desc_off(irq, i, desc) != 0) { + clear_irq_range(pp, irq, i, pos0); + goto no_valid_irq; + } set_bit(pos0 + i, pp->msi_irq_in_use); - irq_alloc_descs((irq + i), (irq + i), 1, 0); - irq_set_msi_desc(irq + i, desc); /*Enable corresponding interrupt in MSI interrupt controller */ res = ((pos0 + i) / 32) * 12; bit = (pos0 + i) % 32; dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); - i++; } *pos = pos0; @@ -266,7 +290,7 @@ no_valid_irq: static void clear_irq(unsigned int irq) { - int res, bit, val, pos; + unsigned int pos, nvec; struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; @@ -281,18 +305,15 @@ static void clear_irq(unsigned int irq) return; } + /* undo what was done in assign_irq */ pos = data->hwirq; + nvec = 1 << msi->msi_attrib.multiple; - irq_free_desc(irq); + clear_irq_range(pp, irq, nvec, pos); - clear_bit(pos, pp->msi_irq_in_use); - - /* Disable corresponding interrupt on MSI interrupt controller */ - res = (pos / 32) * 12; - bit = pos % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + /* all irqs cleared; reset attributes */ + msi->irq = 0; + msi->msi_attrib.multiple = 0; } static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, @@ -320,10 +341,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, if (irq < 0) return irq; - msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; - msg_ctr |= msgvec << 4; - pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, - msg_ctr); + /* + * write_msi_msg() will update PCI_MSI_FLAGS so there is + * no need to explicitly call pci_write_config_word(). + */ desc->msi_attrib.multiple = msgvec; msg.address_lo = virt_to_phys((void *)pp->msi_data); diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 21e865ded1d..ffe6a6b336c 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -140,15 +140,15 @@ struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); int pciehp_enable_slot(struct slot *p_slot); int pciehp_disable_slot(struct slot *p_slot); -int pcie_enable_notification(struct controller *ctrl); +void pcie_enable_notification(struct controller *ctrl); int pciehp_power_on_slot(struct slot *slot); -int pciehp_power_off_slot(struct slot *slot); -int pciehp_get_power_status(struct slot *slot, u8 *status); -int pciehp_get_attention_status(struct slot *slot, u8 *status); +void pciehp_power_off_slot(struct slot *slot); +void pciehp_get_power_status(struct slot *slot, u8 *status); +void pciehp_get_attention_status(struct slot *slot, u8 *status); -int pciehp_set_attention_status(struct slot *slot, u8 status); -int pciehp_get_latch_status(struct slot *slot, u8 *status); -int pciehp_get_adapter_status(struct slot *slot, u8 *status); +void pciehp_set_attention_status(struct slot *slot, u8 status); +void pciehp_get_latch_status(struct slot *slot, u8 *status); +void pciehp_get_adapter_status(struct slot *slot, u8 *status); int pciehp_query_power_fault(struct slot *slot); void pciehp_green_led_on(struct slot *slot); void pciehp_green_led_off(struct slot *slot); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index bbd48bbe4e9..143a389d81f 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -160,7 +160,8 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_set_attention_status(slot, status); + pciehp_set_attention_status(slot, status); + return 0; } @@ -192,7 +193,8 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_power_status(slot, value); + pciehp_get_power_status(slot, value); + return 0; } static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) @@ -202,7 +204,8 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_attention_status(slot, value); + pciehp_get_attention_status(slot, value); + return 0; } static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) @@ -212,7 +215,8 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_latch_status(slot, value); + pciehp_get_latch_status(slot, value); + return 0; } static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) @@ -222,7 +226,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_adapter_status(slot, value); + pciehp_get_adapter_status(slot, value); + return 0; } static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 38f01867917..50628487597 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -158,11 +158,8 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) { /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ if (POWER_CTRL(ctrl)) { - if (pciehp_power_off_slot(pslot)) { - ctrl_err(ctrl, - "Issue of Slot Power Off command failed\n"); - return; - } + pciehp_power_off_slot(pslot); + /* * After turning power off, we must wait for at least 1 second * before taking any action that relies on power having been @@ -171,16 +168,8 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) msleep(1000); } - if (PWR_LED(ctrl)) - pciehp_green_led_off(pslot); - - if (ATTN_LED(ctrl)) { - if (pciehp_set_attention_status(pslot, 1)) { - ctrl_err(ctrl, - "Issue of Set Attention Led command failed\n"); - return; - } - } + pciehp_green_led_off(pslot); + pciehp_set_attention_status(pslot, 1); } /** @@ -203,8 +192,7 @@ static int board_added(struct slot *p_slot) return retval; } - if (PWR_LED(ctrl)) - pciehp_green_led_blink(p_slot); + pciehp_green_led_blink(p_slot); /* Check link training status */ retval = pciehp_check_link_status(ctrl); @@ -227,9 +215,7 @@ static int board_added(struct slot *p_slot) goto err_exit; } - if (PWR_LED(ctrl)) - pciehp_green_led_on(p_slot); - + pciehp_green_led_on(p_slot); return 0; err_exit: @@ -243,7 +229,7 @@ err_exit: */ static int remove_board(struct slot *p_slot) { - int retval = 0; + int retval; struct controller *ctrl = p_slot->ctrl; retval = pciehp_unconfigure_device(p_slot); @@ -251,13 +237,8 @@ static int remove_board(struct slot *p_slot) return retval; if (POWER_CTRL(ctrl)) { - /* power off slot */ - retval = pciehp_power_off_slot(p_slot); - if (retval) { - ctrl_err(ctrl, - "Issue of Slot Disable command failed\n"); - return retval; - } + pciehp_power_off_slot(p_slot); + /* * After turning power off, we must wait for at least 1 second * before taking any action that relies on power having been @@ -267,9 +248,7 @@ static int remove_board(struct slot *p_slot) } /* turn off Green LED */ - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); - + pciehp_green_led_off(p_slot); return 0; } @@ -305,7 +284,7 @@ static void pciehp_power_thread(struct work_struct *work) break; case POWERON_STATE: mutex_unlock(&p_slot->lock); - if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl)) + if (pciehp_enable_slot(p_slot)) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; @@ -372,11 +351,8 @@ static void handle_button_press_event(struct slot *p_slot) "press.\n", slot_name(p_slot)); } /* blink green LED and turn off amber */ - if (PWR_LED(ctrl)) - pciehp_green_led_blink(p_slot); - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 0); - + pciehp_green_led_blink(p_slot); + pciehp_set_attention_status(p_slot, 0); queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: @@ -389,14 +365,11 @@ static void handle_button_press_event(struct slot *p_slot) ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); cancel_delayed_work(&p_slot->work); if (p_slot->state == BLINKINGOFF_STATE) { - if (PWR_LED(ctrl)) - pciehp_green_led_on(p_slot); + pciehp_green_led_on(p_slot); } else { - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); + pciehp_green_led_off(p_slot); } - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 0); + pciehp_set_attention_status(p_slot, 0); ctrl_info(ctrl, "PCI slot #%s - action canceled " "due to button press\n", slot_name(p_slot)); p_slot->state = STATIC_STATE; @@ -456,10 +429,8 @@ static void interrupt_event_handler(struct work_struct *work) case INT_POWER_FAULT: if (!POWER_CTRL(ctrl)) break; - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 1); - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); + pciehp_set_attention_status(p_slot, 1); + pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: case INT_PRESENCE_OFF: @@ -482,14 +453,14 @@ int pciehp_enable_slot(struct slot *p_slot) int rc; struct controller *ctrl = p_slot->ctrl; - rc = pciehp_get_adapter_status(p_slot, &getstatus); - if (rc || !getstatus) { + pciehp_get_adapter_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); return -ENODEV; } if (MRL_SENS(p_slot->ctrl)) { - rc = pciehp_get_latch_status(p_slot, &getstatus); - if (rc || getstatus) { + pciehp_get_latch_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -497,8 +468,8 @@ int pciehp_enable_slot(struct slot *p_slot) } if (POWER_CTRL(p_slot->ctrl)) { - rc = pciehp_get_power_status(p_slot, &getstatus); - if (rc || getstatus) { + pciehp_get_power_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Already enabled on slot(%s)\n", slot_name(p_slot)); return -EINVAL; @@ -518,15 +489,14 @@ int pciehp_enable_slot(struct slot *p_slot) int pciehp_disable_slot(struct slot *p_slot) { u8 getstatus = 0; - int ret = 0; struct controller *ctrl = p_slot->ctrl; if (!p_slot->ctrl) return 1; if (!HP_SUPR_RM(p_slot->ctrl)) { - ret = pciehp_get_adapter_status(p_slot, &getstatus); - if (ret || !getstatus) { + pciehp_get_adapter_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -534,8 +504,8 @@ int pciehp_disable_slot(struct slot *p_slot) } if (MRL_SENS(p_slot->ctrl)) { - ret = pciehp_get_latch_status(p_slot, &getstatus); - if (ret || getstatus) { + pciehp_get_latch_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -543,8 +513,8 @@ int pciehp_disable_slot(struct slot *p_slot) } if (POWER_CTRL(p_slot->ctrl)) { - ret = pciehp_get_power_status(p_slot, &getstatus); - if (ret || !getstatus) { + pciehp_get_power_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "Already disabled on slot(%s)\n", slot_name(p_slot)); return -EINVAL; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 3eea3fdd4b0..14acfccb767 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -41,34 +41,11 @@ #include "../pci.h" #include "pciehp.h" -static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value) +static inline struct pci_dev *ctrl_dev(struct controller *ctrl) { - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_read_word(dev, reg, value); + return ctrl->pcie->port; } -static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value) -{ - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_read_dword(dev, reg, value); -} - -static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value) -{ - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_write_word(dev, reg, value); -} - -static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) -{ - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_write_dword(dev, reg, value); -} - -/* Power Control Command */ -#define POWER_ON 0 -#define POWER_OFF PCI_EXP_SLTCTL_PCC - static irqreturn_t pcie_isr(int irq, void *dev_id); static void start_int_poll_timer(struct controller *ctrl, int sec); @@ -129,20 +106,23 @@ static inline void pciehp_free_irq(struct controller *ctrl) static int pcie_poll_cmd(struct controller *ctrl) { + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; - int err, timeout = 1000; + int timeout = 1000; - err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { - pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); return 1; } while (timeout > 0) { msleep(10); timeout -= 10; - err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { - pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); return 1; } } @@ -169,21 +149,15 @@ static void pcie_wait_cmd(struct controller *ctrl, int poll) * @cmd: command value written to slot control register * @mask: bitmask of slot control register to be modified */ -static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) +static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { - int retval = 0; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; u16 slot_ctrl; mutex_lock(&ctrl->ctrl_lock); - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - goto out; - } - + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (slot_status & PCI_EXP_SLTSTA_CC) { if (!ctrl->no_cmd_complete) { /* @@ -207,24 +181,17 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) } } - retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); - goto out; - } - + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); slot_ctrl &= ~mask; slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); - retval = pciehp_writew(ctrl, PCI_EXP_SLTCTL, slot_ctrl); - if (retval) - ctrl_err(ctrl, "Cannot write to SLOTCTRL register\n"); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); /* * Wait for command completion. */ - if (!retval && !ctrl->no_cmd_complete) { + if (!ctrl->no_cmd_complete) { int poll = 0; /* * if hotplug interrupt is not enabled or command @@ -236,19 +203,16 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) poll = 1; pcie_wait_cmd(ctrl, poll); } - out: mutex_unlock(&ctrl->ctrl_lock); - return retval; } static bool check_link_active(struct controller *ctrl) { - bool ret = false; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_status; + bool ret; - if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status)) - return ret; - + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); if (ret) @@ -311,9 +275,9 @@ static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) int pciehp_check_link_status(struct controller *ctrl) { + struct pci_dev *pdev = ctrl_dev(ctrl); + bool found; u16 lnk_status; - int retval = 0; - bool found = false; /* * Data Link Layer Link Active Reporting must be capable for @@ -330,52 +294,37 @@ int pciehp_check_link_status(struct controller *ctrl) found = pci_bus_check_dev(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0)); - retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); - if (retval) { - ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); - return retval; - } - + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); if ((lnk_status & PCI_EXP_LNKSTA_LT) || !(lnk_status & PCI_EXP_LNKSTA_NLW)) { ctrl_err(ctrl, "Link Training Error occurs \n"); - retval = -1; - return retval; + return -1; } pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); - if (!found && !retval) - retval = -1; + if (!found) + return -1; - return retval; + return 0; } static int __pciehp_link_set(struct controller *ctrl, bool enable) { + struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_ctrl; - int retval = 0; - retval = pciehp_readw(ctrl, PCI_EXP_LNKCTL, &lnk_ctrl); - if (retval) { - ctrl_err(ctrl, "Cannot read LNKCTRL register\n"); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); if (enable) lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; else lnk_ctrl |= PCI_EXP_LNKCTL_LD; - retval = pciehp_writew(ctrl, PCI_EXP_LNKCTL, lnk_ctrl); - if (retval) { - ctrl_err(ctrl, "Cannot write LNKCTRL register\n"); - return retval; - } + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); - - return retval; + return 0; } static int pciehp_link_enable(struct controller *ctrl) @@ -388,223 +337,165 @@ static int pciehp_link_disable(struct controller *ctrl) return __pciehp_link_set(ctrl, false); } -int pciehp_get_attention_status(struct slot *slot, u8 *status) +void pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; - u8 atten_led_state; - int retval = 0; - - retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); - atten_led_state = (slot_ctrl & PCI_EXP_SLTCTL_AIC) >> 6; - - switch (atten_led_state) { - case 0: - *status = 0xFF; /* Reserved */ - break; - case 1: + switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) { + case PCI_EXP_SLTCTL_ATTN_IND_ON: *status = 1; /* On */ break; - case 2: + case PCI_EXP_SLTCTL_ATTN_IND_BLINK: *status = 2; /* Blink */ break; - case 3: + case PCI_EXP_SLTCTL_ATTN_IND_OFF: *status = 0; /* Off */ break; default: *status = 0xFF; break; } - - return 0; } -int pciehp_get_power_status(struct slot *slot, u8 *status) +void pciehp_get_power_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; - u8 pwr_state; - int retval = 0; - retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); - pwr_state = (slot_ctrl & PCI_EXP_SLTCTL_PCC) >> 10; - - switch (pwr_state) { - case 0: - *status = 1; + switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) { + case PCI_EXP_SLTCTL_PWR_ON: + *status = 1; /* On */ break; - case 1: - *status = 0; + case PCI_EXP_SLTCTL_PWR_OFF: + *status = 0; /* Off */ break; default: *status = 0xFF; break; } - - return retval; } -int pciehp_get_latch_status(struct slot *slot, u8 *status) +void pciehp_get_latch_status(struct slot *slot, u8 *status) { - struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; - int retval; - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); - return 0; } -int pciehp_get_adapter_status(struct slot *slot, u8 *status) +void pciehp_get_adapter_status(struct slot *slot, u8 *status) { - struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; - int retval; - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); - return 0; } int pciehp_query_power_fault(struct slot *slot) { - struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; - int retval; - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "Cannot check for power fault\n"); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); return !!(slot_status & PCI_EXP_SLTSTA_PFD); } -int pciehp_set_attention_status(struct slot *slot, u8 value) +void pciehp_set_attention_status(struct slot *slot, u8 value) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; - u16 cmd_mask; - cmd_mask = PCI_EXP_SLTCTL_AIC; + if (!ATTN_LED(ctrl)) + return; + switch (value) { case 0 : /* turn off */ - slot_cmd = 0x00C0; + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF; break; case 1: /* turn on */ - slot_cmd = 0x0040; + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON; break; case 2: /* turn blink */ - slot_cmd = 0x0080; + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK; break; default: - return -EINVAL; + return; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); - return pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC); } void pciehp_green_led_on(struct slot *slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - slot_cmd = 0x0100; - cmd_mask = PCI_EXP_SLTCTL_PIC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + if (!PWR_LED(ctrl)) + return; + + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_ON); } void pciehp_green_led_off(struct slot *slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - slot_cmd = 0x0300; - cmd_mask = PCI_EXP_SLTCTL_PIC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + if (!PWR_LED(ctrl)) + return; + + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_OFF); } void pciehp_green_led_blink(struct slot *slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - slot_cmd = 0x0200; - cmd_mask = PCI_EXP_SLTCTL_PIC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + if (!PWR_LED(ctrl)) + return; + + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_BLINK); } int pciehp_power_on_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; - int retval = 0; + int retval; /* Clear sticky power-fault bit from previous power failures */ - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - return retval; - } - slot_status &= PCI_EXP_SLTSTA_PFD; - if (slot_status) { - retval = pciehp_writew(ctrl, PCI_EXP_SLTSTA, slot_status); - if (retval) { - ctrl_err(ctrl, - "%s: Cannot write to SLOTSTATUS register\n", - __func__); - return retval; - } - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_PFD) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PFD); ctrl->power_fault_detected = 0; - slot_cmd = POWER_ON; - cmd_mask = PCI_EXP_SLTCTL_PCC; - retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); - if (retval) { - ctrl_err(ctrl, "Write %x command failed!\n", slot_cmd); - return retval; - } + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_ON); retval = pciehp_link_enable(ctrl); if (retval) @@ -613,12 +504,9 @@ int pciehp_power_on_slot(struct slot * slot) return retval; } -int pciehp_power_off_slot(struct slot * slot) +void pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - int retval; /* Disable the link at first */ pciehp_link_disable(ctrl); @@ -628,21 +516,16 @@ int pciehp_power_off_slot(struct slot * slot) else msleep(1000); - slot_cmd = POWER_OFF; - cmd_mask = PCI_EXP_SLTCTL_PCC; - retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); - if (retval) { - ctrl_err(ctrl, "Write command failed!\n"); - return retval; - } + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); - return 0; + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_OFF); } static irqreturn_t pcie_isr(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; + struct pci_dev *pdev = ctrl_dev(ctrl); struct slot *slot = ctrl->slot; u16 detected, intr_loc; @@ -653,11 +536,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) */ intr_loc = 0; do { - if (pciehp_readw(ctrl, PCI_EXP_SLTSTA, &detected)) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS\n", - __func__); - return IRQ_NONE; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected); detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | @@ -666,11 +545,9 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) intr_loc |= detected; if (!intr_loc) return IRQ_NONE; - if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, intr_loc)) { - ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", - __func__); - return IRQ_NONE; - } + if (detected) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + intr_loc); } while (detected); ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc); @@ -705,7 +582,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) return IRQ_HANDLED; } -int pcie_enable_notification(struct controller *ctrl) +void pcie_enable_notification(struct controller *ctrl) { u16 cmd, mask; @@ -731,22 +608,18 @@ int pcie_enable_notification(struct controller *ctrl) PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); - if (pcie_write_cmd(ctrl, cmd, mask)) { - ctrl_err(ctrl, "Cannot enable software notification\n"); - return -1; - } - return 0; + pcie_write_cmd(ctrl, cmd, mask); } static void pcie_disable_notification(struct controller *ctrl) { u16 mask; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_DLLSCE); - if (pcie_write_cmd(ctrl, 0, mask)) - ctrl_warn(ctrl, "Cannot disable software notification\n"); + pcie_write_cmd(ctrl, 0, mask); } /* @@ -758,6 +631,7 @@ static void pcie_disable_notification(struct controller *ctrl) int pciehp_reset_slot(struct slot *slot, int probe) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); if (probe) return 0; @@ -771,7 +645,8 @@ int pciehp_reset_slot(struct slot *slot, int probe) pci_reset_bridge_secondary_bus(ctrl->pcie->port); if (HP_SUPR_RM(ctrl)) { - pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC); pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE); if (pciehp_poll_mode) int_poll_timeout(ctrl->poll_timer.data); @@ -784,10 +659,7 @@ int pcie_init_notification(struct controller *ctrl) { if (pciehp_request_irq(ctrl)) return -1; - if (pcie_enable_notification(ctrl)) { - pciehp_free_irq(ctrl); - return -1; - } + pcie_enable_notification(ctrl); ctrl->notification_enabled = 1; return 0; } @@ -875,12 +747,14 @@ static inline void dbg_ctrl(struct controller *ctrl) EMI(ctrl) ? "yes" : "no"); ctrl_info(ctrl, " Command Completed : %3s\n", NO_CMD_CMPL(ctrl) ? "no" : "yes"); - pciehp_readw(ctrl, PCI_EXP_SLTSTA, ®16); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, ®16); ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16); - pciehp_readw(ctrl, PCI_EXP_SLTCTL, ®16); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, ®16); ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16); } +#define FLAG(x,y) (((x) & (y)) ? '+' : '-') + struct controller *pcie_init(struct pcie_device *dev) { struct controller *ctrl; @@ -893,11 +767,7 @@ struct controller *pcie_init(struct pcie_device *dev) goto abort; } ctrl->pcie = dev; - if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) { - ctrl_err(ctrl, "Cannot read SLOTCAP register\n"); - goto abort_ctrl; - } - + pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); ctrl->slot_cap = slot_cap; mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); @@ -913,25 +783,31 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->no_cmd_complete = 1; /* Check if Data Link Layer Link Active Reporting is implemented */ - if (pciehp_readl(ctrl, PCI_EXP_LNKCAP, &link_cap)) { - ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); - goto abort_ctrl; - } + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { ctrl_dbg(ctrl, "Link Active Reporting supported\n"); ctrl->link_active_reporting = 1; } /* Clear all remaining event bits in Slot Status register */ - if (pciehp_writew(ctrl, PCI_EXP_SLTSTA, 0x1f)) - goto abort_ctrl; + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_CC); /* Disable software notification */ pcie_disable_notification(ctrl); - ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", - pdev->vendor, pdev->device, pdev->subsystem_vendor, - pdev->subsystem_device); + ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n", + (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, + FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), + FLAG(slot_cap, PCI_EXP_SLTCAP_AIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), + FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP), + FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), + FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC)); if (pcie_init_slot(ctrl)) goto abort_ctrl; diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 0e0d0f7f63f..198355112ee 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -78,7 +78,7 @@ int pciehp_configure_device(struct slot *p_slot) int pciehp_unconfigure_device(struct slot *p_slot) { - int ret, rc = 0; + int rc = 0; u8 bctl = 0; u8 presence = 0; struct pci_dev *dev, *temp; @@ -88,9 +88,7 @@ int pciehp_unconfigure_device(struct slot *p_slot) ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", __func__, pci_domain_nr(parent), parent->number); - ret = pciehp_get_adapter_status(p_slot, &presence); - if (ret) - presence = 0; + pciehp_get_adapter_status(p_slot, &presence); /* * Stopping an SR-IOV PF device removes all the associated VFs, diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 1fe2d6fb19d..68311ec849e 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -441,6 +441,7 @@ static int sriov_init(struct pci_dev *dev, int pos) found: pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); + pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0); pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); if (!offset || (total > 1 && !stride)) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3fcd67a1667..51bf0400a88 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -116,7 +116,7 @@ void __weak arch_teardown_msi_irqs(struct pci_dev *dev) return default_teardown_msi_irqs(dev); } -void default_restore_msi_irqs(struct pci_dev *dev, int irq) +static void default_restore_msi_irq(struct pci_dev *dev, int irq) { struct msi_desc *entry; @@ -134,9 +134,9 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq) write_msi_msg(irq, &entry->msg); } -void __weak arch_restore_msi_irqs(struct pci_dev *dev, int irq) +void __weak arch_restore_msi_irqs(struct pci_dev *dev) { - return default_restore_msi_irqs(dev, irq); + return default_restore_msi_irqs(dev); } static void msi_set_enable(struct pci_dev *dev, int enable) @@ -262,6 +262,15 @@ void unmask_msi_irq(struct irq_data *data) msi_set_mask_bit(data, 0); } +void default_restore_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &dev->msi_list, list) { + default_restore_msi_irq(dev, entry->irq); + } +} + void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { BUG_ON(entry->dev->current_state != PCI_D0); @@ -430,7 +439,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pci_intx_for_msi(dev, 0); msi_set_enable(dev, 0); - arch_restore_msi_irqs(dev, dev->irq); + arch_restore_msi_irqs(dev); pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); msi_mask_irq(entry, msi_capable_mask(control), entry->masked); @@ -455,8 +464,8 @@ static void __pci_restore_msix_state(struct pci_dev *dev) control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); + arch_restore_msi_irqs(dev); list_for_each_entry(entry, &dev->msi_list, list) { - arch_restore_msi_irqs(dev, entry->irq); msix_mask_irq(entry, entry->masked); } diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 577074efbe6..e0431f1af33 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -358,7 +358,7 @@ static void pci_acpi_cleanup(struct device *dev) static bool pci_acpi_bus_match(struct device *dev) { - return dev->bus == &pci_bus_type; + return dev_is_pci(dev); } static struct acpi_bus_type acpi_pci_bus = { diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 9042fdbd724..25f0bc65916 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -19,6 +19,7 @@ #include <linux/cpu.h> #include <linux/pm_runtime.h> #include <linux/suspend.h> +#include <linux/kexec.h> #include "pci.h" struct pci_dynid { @@ -288,12 +289,27 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, int error, node; struct drv_dev_and_id ddi = { drv, dev, id }; - /* Execute driver initialization on node where the device's - bus is attached to. This way the driver likely allocates - its local memory on the right node without any need to - change it. */ + /* + * Execute driver initialization on node where the device is + * attached. This way the driver likely allocates its local memory + * on the right node. + */ node = dev_to_node(&dev->dev); - if (node >= 0) { + + /* + * On NUMA systems, we are likely to call a PF probe function using + * work_on_cpu(). If that probe calls pci_enable_sriov() (which + * adds the VF devices via pci_bus_add_device()), we may re-enter + * this function to call the VF probe function. Calling + * work_on_cpu() again will cause a lockdep warning. Since VFs are + * always on the same node as the PF, we can work around this by + * avoiding work_on_cpu() when we're already on the correct node. + * + * Preemption is enabled, so it's theoretically unsafe to use + * numa_node_id(), but even if we run the probe function on the + * wrong node, it should be functionally correct. + */ + if (node >= 0 && node != numa_node_id()) { int cpu; get_online_cpus(); @@ -305,6 +321,7 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, put_online_cpus(); } else error = local_pci_probe(&ddi); + return error; } @@ -399,12 +416,17 @@ static void pci_device_shutdown(struct device *dev) pci_msi_shutdown(pci_dev); pci_msix_shutdown(pci_dev); +#ifdef CONFIG_KEXEC /* - * Turn off Bus Master bit on the device to tell it to not - * continue to do DMA. Don't touch devices in D3cold or unknown states. + * If this is a kexec reboot, turn off Bus Master bit on the + * device to tell it to not continue to do DMA. Don't touch + * devices in D3cold or unknown states. + * If it is not a kexec reboot, firmware will hit the PCI + * devices with big hammer and stop their DMA any way. */ - if (pci_dev->current_state <= PCI_D3hot) + if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot)) pci_clear_master(pci_dev); +#endif } #ifdef CONFIG_PM diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 33120d15666..508e560b7d2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -431,6 +431,32 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) } /** + * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos + * @dev: the PCI device to operate on + * @pos: config space offset of status word + * @mask: mask of bit(s) to care about in status word + * + * Return 1 when mask bit(s) in status word clear, 0 otherwise. + */ +int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask) +{ + int i; + + /* Wait for Transaction Pending bit clean */ + for (i = 0; i < 4; i++) { + u16 status; + if (i) + msleep((1 << (i - 1)) * 100); + + pci_read_config_word(dev, pos, &status); + if (!(status & mask)) + return 1; + } + + return 0; +} + +/** * pci_restore_bars - restore a devices BAR values (e.g. after wake-up) * @dev: PCI device to have its BARs restored * @@ -835,18 +861,28 @@ EXPORT_SYMBOL(pci_choose_state); #define PCI_EXP_SAVE_REGS 7 -static struct pci_cap_saved_state *pci_find_saved_cap( - struct pci_dev *pci_dev, char cap) +static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev, + u16 cap, bool extended) { struct pci_cap_saved_state *tmp; hlist_for_each_entry(tmp, &pci_dev->saved_cap_space, next) { - if (tmp->cap.cap_nr == cap) + if (tmp->cap.cap_extended == extended && tmp->cap.cap_nr == cap) return tmp; } return NULL; } +struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap) +{ + return _pci_find_saved_cap(dev, cap, false); +} + +struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, u16 cap) +{ + return _pci_find_saved_cap(dev, cap, true); +} + static int pci_save_pcie_state(struct pci_dev *dev) { int i = 0; @@ -948,6 +984,8 @@ pci_save_state(struct pci_dev *dev) return i; if ((i = pci_save_pcix_state(dev)) != 0) return i; + if ((i = pci_save_vc_state(dev)) != 0) + return i; return 0; } @@ -1010,6 +1048,7 @@ void pci_restore_state(struct pci_dev *dev) /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); pci_restore_ats_state(dev); + pci_restore_vc_state(dev); pci_restore_config_space(dev); @@ -1087,7 +1126,7 @@ int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state) while (cap->size) { struct pci_cap_saved_state *tmp; - tmp = pci_find_saved_cap(dev, cap->cap_nr); + tmp = _pci_find_saved_cap(dev, cap->cap_nr, cap->cap_extended); if (!tmp || tmp->cap.size != cap->size) return -EINVAL; @@ -2021,18 +2060,24 @@ static void pci_add_saved_cap(struct pci_dev *pci_dev, } /** - * pci_add_cap_save_buffer - allocate buffer for saving given capability registers + * _pci_add_cap_save_buffer - allocate buffer for saving given + * capability registers * @dev: the PCI device * @cap: the capability to allocate the buffer for + * @extended: Standard or Extended capability ID * @size: requested size of the buffer */ -static int pci_add_cap_save_buffer( - struct pci_dev *dev, char cap, unsigned int size) +static int _pci_add_cap_save_buffer(struct pci_dev *dev, u16 cap, + bool extended, unsigned int size) { int pos; struct pci_cap_saved_state *save_state; - pos = pci_find_capability(dev, cap); + if (extended) + pos = pci_find_ext_capability(dev, cap); + else + pos = pci_find_capability(dev, cap); + if (pos <= 0) return 0; @@ -2041,12 +2086,23 @@ static int pci_add_cap_save_buffer( return -ENOMEM; save_state->cap.cap_nr = cap; + save_state->cap.cap_extended = extended; save_state->cap.size = size; pci_add_saved_cap(dev, save_state); return 0; } +int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size) +{ + return _pci_add_cap_save_buffer(dev, cap, false, size); +} + +int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size) +{ + return _pci_add_cap_save_buffer(dev, cap, true, size); +} + /** * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities * @dev: the PCI device @@ -2065,6 +2121,8 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) if (error) dev_err(&dev->dev, "unable to preallocate PCI-X save buffer\n"); + + pci_allocate_vc_save_buffers(dev); } void pci_free_cap_save_buffers(struct pci_dev *dev) @@ -3204,20 +3262,10 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary); */ int pci_wait_for_pending_transaction(struct pci_dev *dev) { - int i; - u16 status; - - /* Wait for Transaction Pending bit clean */ - for (i = 0; i < 4; i++) { - if (i) - msleep((1 << (i - 1)) * 100); - - pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status); - if (!(status & PCI_EXP_DEVSTA_TRPND)) - return 1; - } + if (!pci_is_pcie(dev)) + return 1; - return 0; + return pci_wait_for_pending(dev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_TRPND); } EXPORT_SYMBOL(pci_wait_for_pending_transaction); @@ -3244,10 +3292,8 @@ static int pcie_flr(struct pci_dev *dev, int probe) static int pci_af_flr(struct pci_dev *dev, int probe) { - int i; int pos; u8 cap; - u8 status; pos = pci_find_capability(dev, PCI_CAP_ID_AF); if (!pos) @@ -3261,14 +3307,8 @@ static int pci_af_flr(struct pci_dev *dev, int probe) return 0; /* Wait for Transaction Pending bit clean */ - for (i = 0; i < 4; i++) { - if (i) - msleep((1 << (i - 1)) * 100); - - pci_read_config_byte(dev, pos + PCI_AF_STATUS, &status); - if (!(status & PCI_AF_STATUS_TP)) - goto clear; - } + if (pci_wait_for_pending(dev, PCI_AF_STATUS, PCI_AF_STATUS_TP)) + goto clear; dev_err(&dev->dev, "transaction is not cleared; " "proceeding with reset anyway\n"); diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index cf611ab2193..4d6991794fa 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -50,14 +50,37 @@ struct aer_hest_parse_info { int firmware_first; }; +static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) +{ + if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || + hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || + hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) + return 1; + return 0; +} + static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) { struct aer_hest_parse_info *info = data; struct acpi_hest_aer_common *p; int ff; + if (!hest_source_is_pcie_aer(hest_hdr)) + return 0; + p = (struct acpi_hest_aer_common *)(hest_hdr + 1); ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + + /* + * If no specific device is supplied, determine whether + * FIRMWARE_FIRST is set for *any* PCIe device. + */ + if (!info->pci_dev) { + info->firmware_first |= ff; + return 0; + } + + /* Otherwise, check the specific device */ if (p->flags & ACPI_HEST_GLOBAL) { if (hest_match_type(hest_hdr, info->pci_dev)) info->firmware_first = ff; @@ -97,33 +120,20 @@ int pcie_aer_get_firmware_first(struct pci_dev *dev) static bool aer_firmware_first; -static int aer_hest_parse_aff(struct acpi_hest_header *hest_hdr, void *data) -{ - struct acpi_hest_aer_common *p; - - if (aer_firmware_first) - return 0; - - switch (hest_hdr->type) { - case ACPI_HEST_TYPE_AER_ROOT_PORT: - case ACPI_HEST_TYPE_AER_ENDPOINT: - case ACPI_HEST_TYPE_AER_BRIDGE: - p = (struct acpi_hest_aer_common *)(hest_hdr + 1); - aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - default: - return 0; - } -} - /** * aer_acpi_firmware_first - Check if APEI should control AER. */ bool aer_acpi_firmware_first(void) { static bool parsed = false; + struct aer_hest_parse_info info = { + .pci_dev = NULL, /* Check all PCIe devices */ + .firmware_first = 0, + }; if (!parsed) { - apei_hest_parse(aer_hest_parse_aff, NULL); + apei_hest_parse(aer_hest_parse, &info); + aer_firmware_first = info.firmware_first; parsed = true; } return aer_firmware_first; diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 2c7c9f5f592..34ff7026440 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -124,6 +124,21 @@ static const char *aer_agent_string[] = { "Transmitter ID" }; +static void __print_tlp_header(struct pci_dev *dev, + struct aer_header_log_regs *t) +{ + unsigned char *tlp = (unsigned char *)&t; + + dev_err(&dev->dev, " TLP Header:" + " %02x%02x%02x%02x %02x%02x%02x%02x" + " %02x%02x%02x%02x %02x%02x%02x%02x\n", + *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, + *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), + *(tlp + 11), *(tlp + 10), *(tlp + 9), + *(tlp + 8), *(tlp + 15), *(tlp + 14), + *(tlp + 13), *(tlp + 12)); +} + static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { @@ -153,48 +168,39 @@ static void __aer_print_error(struct pci_dev *dev, void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { + int layer, agent; int id = ((dev->bus->number << 8) | dev->devfn); - if (info->status == 0) { + if (!info->status) { dev_err(&dev->dev, "PCIe Bus Error: severity=%s, type=Unaccessible, " "id=%04x(Unregistered Agent ID)\n", aer_error_severity_string[info->severity], id); - } else { - int layer, agent; + goto out; + } - layer = AER_GET_LAYER_ERROR(info->severity, info->status); - agent = AER_GET_AGENT(info->severity, info->status); + layer = AER_GET_LAYER_ERROR(info->severity, info->status); + agent = AER_GET_AGENT(info->severity, info->status); - dev_err(&dev->dev, - "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n", - aer_error_severity_string[info->severity], - aer_error_layer[layer], id, aer_agent_string[agent]); + dev_err(&dev->dev, + "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], id, aer_agent_string[agent]); - dev_err(&dev->dev, - " device [%04x:%04x] error status/mask=%08x/%08x\n", - dev->vendor, dev->device, - info->status, info->mask); - - __aer_print_error(dev, info); - - if (info->tlp_header_valid) { - unsigned char *tlp = (unsigned char *) &info->tlp; - dev_err(&dev->dev, " TLP Header:" - " %02x%02x%02x%02x %02x%02x%02x%02x" - " %02x%02x%02x%02x %02x%02x%02x%02x\n", - *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, - *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), - *(tlp + 11), *(tlp + 10), *(tlp + 9), - *(tlp + 8), *(tlp + 15), *(tlp + 14), - *(tlp + 13), *(tlp + 12)); - } - } + dev_err(&dev->dev, + " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, + info->status, info->mask); + + __aer_print_error(dev, info); + if (info->tlp_header_valid) + __print_tlp_header(dev, &info->tlp); + +out: if (info->id && info->error_dev_num > 1 && info->id == id) - dev_err(&dev->dev, - " Error of this Agent(%04x) is reported first\n", - id); + dev_err(&dev->dev, " Error of this Agent(%04x) is reported first\n", id); + trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask), info->severity); } @@ -228,6 +234,7 @@ void cper_print_aer(struct pci_dev *dev, int cper_severity, const char **status_strs; aer_severity = cper_severity_to_aer(cper_severity); + if (aer_severity == AER_CORRECTABLE) { status = aer->cor_status; mask = aer->cor_mask; @@ -240,28 +247,22 @@ void cper_print_aer(struct pci_dev *dev, int cper_severity, status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string); tlp_header_valid = status & AER_LOG_TLP_MASKS; } + layer = AER_GET_LAYER_ERROR(aer_severity, status); agent = AER_GET_AGENT(aer_severity, status); - dev_err(&dev->dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", - status, mask); + + dev_err(&dev->dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); cper_print_bits("", status, status_strs, status_strs_size); dev_err(&dev->dev, "aer_layer=%s, aer_agent=%s\n", - aer_error_layer[layer], aer_agent_string[agent]); + aer_error_layer[layer], aer_agent_string[agent]); + if (aer_severity != AER_CORRECTABLE) dev_err(&dev->dev, "aer_uncor_severity: 0x%08x\n", - aer->uncor_severity); - if (tlp_header_valid) { - const unsigned char *tlp; - tlp = (const unsigned char *)&aer->header_log; - dev_err(&dev->dev, "aer_tlp_header:" - " %02x%02x%02x%02x %02x%02x%02x%02x" - " %02x%02x%02x%02x %02x%02x%02x%02x\n", - *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, - *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), - *(tlp + 11), *(tlp + 10), *(tlp + 9), - *(tlp + 8), *(tlp + 15), *(tlp + 14), - *(tlp + 13), *(tlp + 12)); - } + aer->uncor_severity); + + if (tlp_header_valid) + __print_tlp_header(dev, &aer->header_log); + trace_aer_event(dev_name(&dev->dev), (status & ~mask), aer_severity); } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 0b6e7660406..ce9d9ae17bf 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -554,7 +554,7 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) if (pcie_ports_disabled) return -ENODEV; - new->driver.name = (char *)new->name; + new->driver.name = new->name; new->driver.bus = &pcie_port_bus_type; new->driver.probe = pcie_port_probe_service; new->driver.remove = pcie_port_remove_service; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 38e403dddf6..12ec56c9a91 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1154,6 +1154,18 @@ static void pci_release_capabilities(struct pci_dev *dev) pci_free_cap_save_buffers(dev); } +static void pci_free_resources(struct pci_dev *dev) +{ + int i; + + pci_cleanup_rom(dev); + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *res = dev->resource + i; + if (res->parent) + release_resource(res); + } +} + /** * pci_release_dev - free a pci device structure when all users of it are finished. * @dev: device that's been disconnected @@ -1163,9 +1175,14 @@ static void pci_release_capabilities(struct pci_dev *dev) */ static void pci_release_dev(struct device *dev) { - struct pci_dev *pci_dev; + struct pci_dev *pci_dev = to_pci_dev(dev); + + down_write(&pci_bus_sem); + list_del(&pci_dev->bus_list); + up_write(&pci_bus_sem); + + pci_free_resources(pci_dev); - pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); pci_release_of_node(pci_dev); pcibios_release_device(pci_dev); @@ -1381,8 +1398,6 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->match_driver = false; ret = device_add(&dev->dev); WARN_ON(ret < 0); - - pci_proc_attach_device(dev); } struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 1576851028d..f452148e6d5 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -3,20 +3,6 @@ #include <linux/pci-aspm.h> #include "pci.h" -static void pci_free_resources(struct pci_dev *dev) -{ - int i; - - msi_remove_pci_irq_vectors(dev); - - pci_cleanup_rom(dev); - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *res = dev->resource + i; - if (res->parent) - release_resource(res); - } -} - static void pci_stop_dev(struct pci_dev *dev) { pci_pme_active(dev, false); @@ -24,7 +10,7 @@ static void pci_stop_dev(struct pci_dev *dev) if (dev->is_added) { pci_proc_detach_device(dev); pci_remove_sysfs_dev_files(dev); - device_del(&dev->dev); + device_release_driver(&dev->dev); dev->is_added = 0; } @@ -34,11 +20,8 @@ static void pci_stop_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev) { - down_write(&pci_bus_sem); - list_del(&dev->bus_list); - up_write(&pci_bus_sem); + device_del(&dev->dev); - pci_free_resources(dev); put_device(&dev->dev); } @@ -126,7 +109,7 @@ void pci_stop_root_bus(struct pci_bus *bus) pci_stop_bus_device(child); /* stop the host bridge */ - device_del(&host_bridge->dev); + device_release_driver(&host_bridge->dev); } void pci_remove_root_bus(struct pci_bus *bus) @@ -145,5 +128,5 @@ void pci_remove_root_bus(struct pci_bus *bus) host_bridge->bus = NULL; /* remove the host bridge */ - put_device(&host_bridge->dev); + device_unregister(&host_bridge->dev); } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 219a4106480..2e344a5581a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -538,7 +538,8 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_bus_region region; unsigned long io_mask; u8 io_base_lo, io_limit_lo; - u32 l, io_upper16; + u16 l; + u32 io_upper16; io_mask = PCI_IO_RANGE_MASK; if (bridge->io_window_1k) @@ -548,11 +549,10 @@ static void pci_setup_bridge_io(struct pci_bus *bus) res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_IO) { - pci_read_config_dword(bridge, PCI_IO_BASE, &l); - l &= 0xffff0000; + pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; - l |= ((u32) io_limit_lo << 8) | io_base_lo; + l = ((u16) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); @@ -564,7 +564,7 @@ static void pci_setup_bridge_io(struct pci_bus *bus) /* Temporarily disable the I/O range before updating PCI_IO_BASE. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); /* Update lower 16 bits of I/O base/limit. */ - pci_write_config_dword(bridge, PCI_IO_BASE, l); + pci_write_config_word(bridge, PCI_IO_BASE, l); /* Update upper 16 bits of I/O base/limit. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); } @@ -665,21 +665,23 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) pci_read_config_word(bridge, PCI_IO_BASE, &io); if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0); + pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); pci_read_config_word(bridge, PCI_IO_BASE, &io); pci_write_config_word(bridge, PCI_IO_BASE, 0x0); } if (io) b_res[0].flags |= IORESOURCE_IO; + /* DECchip 21050 pass 2 errata: the bridge may miss an address disconnect boundary by one PCI data phase. Workaround: do not use prefetching on this device. */ if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) return; + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); if (!pmem) { pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xfff0fff0); + 0xffe0fff0); pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); } diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c new file mode 100644 index 00000000000..7e1304d2e38 --- /dev/null +++ b/drivers/pci/vc.c @@ -0,0 +1,434 @@ +/* + * PCI Virtual Channel support + * + * Copyright (C) 2013 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson <alex.williamson@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pci_regs.h> +#include <linux/types.h> + +/** + * pci_vc_save_restore_dwords - Save or restore a series of dwords + * @dev: device + * @pos: starting config space position + * @buf: buffer to save to or restore from + * @dwords: number of dwords to save/restore + * @save: whether to save or restore + */ +static void pci_vc_save_restore_dwords(struct pci_dev *dev, int pos, + u32 *buf, int dwords, bool save) +{ + int i; + + for (i = 0; i < dwords; i++, buf++) { + if (save) + pci_read_config_dword(dev, pos + (i * 4), buf); + else + pci_write_config_dword(dev, pos + (i * 4), *buf); + } +} + +/** + * pci_vc_load_arb_table - load and wait for VC arbitration table + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * + * Set Load VC Arbitration Table bit requesting hardware to apply the VC + * Arbitration Table (previously loaded). When the VC Arbitration Table + * Status clears, hardware has latched the table into VC arbitration logic. + */ +static void pci_vc_load_arb_table(struct pci_dev *dev, int pos) +{ + u16 ctrl; + + pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, &ctrl); + pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL, + ctrl | PCI_VC_PORT_CTRL_LOAD_TABLE); + if (pci_wait_for_pending(dev, pos + PCI_VC_PORT_STATUS, + PCI_VC_PORT_STATUS_TABLE)) + return; + + dev_err(&dev->dev, "VC arbitration table failed to load\n"); +} + +/** + * pci_vc_load_port_arb_table - Load and wait for VC port arbitration table + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * @res: VC resource number, ie. VCn (0-7) + * + * Set Load Port Arbitration Table bit requesting hardware to apply the Port + * Arbitration Table (previously loaded). When the Port Arbitration Table + * Status clears, hardware has latched the table into port arbitration logic. + */ +static void pci_vc_load_port_arb_table(struct pci_dev *dev, int pos, int res) +{ + int ctrl_pos, status_pos; + u32 ctrl; + + ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); + status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF); + + pci_read_config_dword(dev, ctrl_pos, &ctrl); + pci_write_config_dword(dev, ctrl_pos, + ctrl | PCI_VC_RES_CTRL_LOAD_TABLE); + + if (pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_TABLE)) + return; + + dev_err(&dev->dev, "VC%d port arbitration table failed to load\n", res); +} + +/** + * pci_vc_enable - Enable virtual channel + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * @res: VC res number, ie. VCn (0-7) + * + * A VC is enabled by setting the enable bit in matching resource control + * registers on both sides of a link. We therefore need to find the opposite + * end of the link. To keep this simple we enable from the downstream device. + * RC devices do not have an upstream device, nor does it seem that VC9 do + * (spec is unclear). Once we find the upstream device, match the VC ID to + * get the correct resource, disable and enable on both ends. + */ +static void pci_vc_enable(struct pci_dev *dev, int pos, int res) +{ + int ctrl_pos, status_pos, id, pos2, evcc, i, ctrl_pos2, status_pos2; + u32 ctrl, header, cap1, ctrl2; + struct pci_dev *link = NULL; + + /* Enable VCs from the downstream device */ + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) + return; + + ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); + status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF); + + pci_read_config_dword(dev, ctrl_pos, &ctrl); + id = ctrl & PCI_VC_RES_CTRL_ID; + + pci_read_config_dword(dev, pos, &header); + + /* If there is no opposite end of the link, skip to enable */ + if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_VC9 || + pci_is_root_bus(dev->bus)) + goto enable; + + pos2 = pci_find_ext_capability(dev->bus->self, PCI_EXT_CAP_ID_VC); + if (!pos2) + goto enable; + + pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_CAP1, &cap1); + evcc = cap1 & PCI_VC_CAP1_EVCC; + + /* VC0 is hardwired enabled, so we can start with 1 */ + for (i = 1; i < evcc + 1; i++) { + ctrl_pos2 = pos2 + PCI_VC_RES_CTRL + + (i * PCI_CAP_VC_PER_VC_SIZEOF); + status_pos2 = pos2 + PCI_VC_RES_STATUS + + (i * PCI_CAP_VC_PER_VC_SIZEOF); + pci_read_config_dword(dev->bus->self, ctrl_pos2, &ctrl2); + if ((ctrl2 & PCI_VC_RES_CTRL_ID) == id) { + link = dev->bus->self; + break; + } + } + + if (!link) + goto enable; + + /* Disable if enabled */ + if (ctrl2 & PCI_VC_RES_CTRL_ENABLE) { + ctrl2 &= ~PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(link, ctrl_pos2, ctrl2); + } + + /* Enable on both ends */ + ctrl2 |= PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(link, ctrl_pos2, ctrl2); +enable: + ctrl |= PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(dev, ctrl_pos, ctrl); + + if (!pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_NEGO)) + dev_err(&dev->dev, "VC%d negotiation stuck pending\n", id); + + if (link && !pci_wait_for_pending(link, status_pos2, + PCI_VC_RES_STATUS_NEGO)) + dev_err(&link->dev, "VC%d negotiation stuck pending\n", id); +} + +/** + * pci_vc_do_save_buffer - Size, save, or restore VC state + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * @save_state: buffer for save/restore + * @name: for error message + * @save: if provided a buffer, this indicates what to do with it + * + * Walking Virtual Channel config space to size, save, or restore it + * is complicated, so we do it all from one function to reduce code and + * guarantee ordering matches in the buffer. When called with NULL + * @save_state, return the size of the necessary save buffer. When called + * with a non-NULL @save_state, @save determines whether we save to the + * buffer or restore from it. + */ +static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos, + struct pci_cap_saved_state *save_state, + bool save) +{ + u32 cap1; + char evcc, lpevcc, parb_size; + int i, len = 0; + u8 *buf = save_state ? (u8 *)save_state->cap.data : NULL; + + /* Sanity check buffer size for save/restore */ + if (buf && save_state->cap.size != + pci_vc_do_save_buffer(dev, pos, NULL, save)) { + dev_err(&dev->dev, + "VC save buffer size does not match @0x%x\n", pos); + return -ENOMEM; + } + + pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP1, &cap1); + /* Extended VC Count (not counting VC0) */ + evcc = cap1 & PCI_VC_CAP1_EVCC; + /* Low Priority Extended VC Count (not counting VC0) */ + lpevcc = (cap1 & PCI_VC_CAP1_LPEVCC) >> 4; + /* Port Arbitration Table Entry Size (bits) */ + parb_size = 1 << ((cap1 & PCI_VC_CAP1_ARB_SIZE) >> 10); + + /* + * Port VC Control Register contains VC Arbitration Select, which + * cannot be modified when more than one LPVC is in operation. We + * therefore save/restore it first, as only VC0 should be enabled + * after device reset. + */ + if (buf) { + if (save) + pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, + (u16 *)buf); + else + pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL, + *(u16 *)buf); + buf += 2; + } + len += 2; + + /* + * If we have any Low Priority VCs and a VC Arbitration Table Offset + * in Port VC Capability Register 2 then save/restore it next. + */ + if (lpevcc) { + u32 cap2; + int vcarb_offset; + + pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP2, &cap2); + vcarb_offset = ((cap2 & PCI_VC_CAP2_ARB_OFF) >> 24) * 16; + + if (vcarb_offset) { + int size, vcarb_phases = 0; + + if (cap2 & PCI_VC_CAP2_128_PHASE) + vcarb_phases = 128; + else if (cap2 & PCI_VC_CAP2_64_PHASE) + vcarb_phases = 64; + else if (cap2 & PCI_VC_CAP2_32_PHASE) + vcarb_phases = 32; + + /* Fixed 4 bits per phase per lpevcc (plus VC0) */ + size = ((lpevcc + 1) * vcarb_phases * 4) / 8; + + if (size && buf) { + pci_vc_save_restore_dwords(dev, + pos + vcarb_offset, + (u32 *)buf, + size / 4, save); + /* + * On restore, we need to signal hardware to + * re-load the VC Arbitration Table. + */ + if (!save) + pci_vc_load_arb_table(dev, pos); + + buf += size; + } + len += size; + } + } + + /* + * In addition to each VC Resource Control Register, we may have a + * Port Arbitration Table attached to each VC. The Port Arbitration + * Table Offset in each VC Resource Capability Register tells us if + * it exists. The entry size is global from the Port VC Capability + * Register1 above. The number of phases is determined per VC. + */ + for (i = 0; i < evcc + 1; i++) { + u32 cap; + int parb_offset; + + pci_read_config_dword(dev, pos + PCI_VC_RES_CAP + + (i * PCI_CAP_VC_PER_VC_SIZEOF), &cap); + parb_offset = ((cap & PCI_VC_RES_CAP_ARB_OFF) >> 24) * 16; + if (parb_offset) { + int size, parb_phases = 0; + + if (cap & PCI_VC_RES_CAP_256_PHASE) + parb_phases = 256; + else if (cap & (PCI_VC_RES_CAP_128_PHASE | + PCI_VC_RES_CAP_128_PHASE_TB)) + parb_phases = 128; + else if (cap & PCI_VC_RES_CAP_64_PHASE) + parb_phases = 64; + else if (cap & PCI_VC_RES_CAP_32_PHASE) + parb_phases = 32; + + size = (parb_size * parb_phases) / 8; + + if (size && buf) { + pci_vc_save_restore_dwords(dev, + pos + parb_offset, + (u32 *)buf, + size / 4, save); + buf += size; + } + len += size; + } + + /* VC Resource Control Register */ + if (buf) { + int ctrl_pos = pos + PCI_VC_RES_CTRL + + (i * PCI_CAP_VC_PER_VC_SIZEOF); + if (save) + pci_read_config_dword(dev, ctrl_pos, + (u32 *)buf); + else { + u32 tmp, ctrl = *(u32 *)buf; + /* + * For an FLR case, the VC config may remain. + * Preserve enable bit, restore the rest. + */ + pci_read_config_dword(dev, ctrl_pos, &tmp); + tmp &= PCI_VC_RES_CTRL_ENABLE; + tmp |= ctrl & ~PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(dev, ctrl_pos, tmp); + /* Load port arbitration table if used */ + if (ctrl & PCI_VC_RES_CTRL_ARB_SELECT) + pci_vc_load_port_arb_table(dev, pos, i); + /* Re-enable if needed */ + if ((ctrl ^ tmp) & PCI_VC_RES_CTRL_ENABLE) + pci_vc_enable(dev, pos, i); + } + buf += 4; + } + len += 4; + } + + return buf ? 0 : len; +} + +static struct { + u16 id; + const char *name; +} vc_caps[] = { { PCI_EXT_CAP_ID_MFVC, "MFVC" }, + { PCI_EXT_CAP_ID_VC, "VC" }, + { PCI_EXT_CAP_ID_VC9, "VC9" } }; + +/** + * pci_save_vc_state - Save VC state to pre-allocate save buffer + * @dev: device + * + * For each type of VC capability, VC/VC9/MFVC, find the capability and + * save it to the pre-allocated save buffer. + */ +int pci_save_vc_state(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { + int pos, ret; + struct pci_cap_saved_state *save_state; + + pos = pci_find_ext_capability(dev, vc_caps[i].id); + if (!pos) + continue; + + save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id); + if (!save_state) { + dev_err(&dev->dev, "%s buffer not found in %s\n", + vc_caps[i].name, __func__); + return -ENOMEM; + } + + ret = pci_vc_do_save_buffer(dev, pos, save_state, true); + if (ret) { + dev_err(&dev->dev, "%s save unsuccessful %s\n", + vc_caps[i].name, __func__); + return ret; + } + } + + return 0; +} + +/** + * pci_restore_vc_state - Restore VC state from save buffer + * @dev: device + * + * For each type of VC capability, VC/VC9/MFVC, find the capability and + * restore it from the previously saved buffer. + */ +void pci_restore_vc_state(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { + int pos; + struct pci_cap_saved_state *save_state; + + pos = pci_find_ext_capability(dev, vc_caps[i].id); + save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id); + if (!save_state || !pos) + continue; + + pci_vc_do_save_buffer(dev, pos, save_state, false); + } +} + +/** + * pci_allocate_vc_save_buffers - Allocate save buffers for VC caps + * @dev: device + * + * For each type of VC capability, VC/VC9/MFVC, find the capability, size + * it, and allocate a buffer for save/restore. + */ + +void pci_allocate_vc_save_buffers(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { + int len, pos = pci_find_ext_capability(dev, vc_caps[i].id); + + if (!pos) + continue; + + len = pci_vc_do_save_buffer(dev, pos, NULL, false); + if (pci_add_ext_cap_save_buffer(dev, vc_caps[i].id, len)) + dev_err(&dev->dev, + "unable to preallocate %s save buffer\n", + vc_caps[i].name); + } +} diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index ffd0632c3cb..83cd1574c81 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -975,20 +975,20 @@ static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos) int ret, evcc, phases, vc_arb; int len = PCI_CAP_VC_BASE_SIZEOF; - ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp); + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_CAP1, &tmp); if (ret) return pcibios_err_to_errno(ret); - evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */ - ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp); + evcc = tmp & PCI_VC_CAP1_EVCC; /* extended vc count */ + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_CAP2, &tmp); if (ret) return pcibios_err_to_errno(ret); - if (tmp & PCI_VC_REG2_128_PHASE) + if (tmp & PCI_VC_CAP2_128_PHASE) phases = 128; - else if (tmp & PCI_VC_REG2_64_PHASE) + else if (tmp & PCI_VC_CAP2_64_PHASE) phases = 64; - else if (tmp & PCI_VC_REG2_32_PHASE) + else if (tmp & PCI_VC_CAP2_32_PHASE) phases = 32; else phases = 0; |