}
/**
+ * pci_rebar_find_pos - find position of resize ctrl reg for BAR
+ * @pdev: PCI device
+ * @bar: BAR to find
+ *
+ * Helper to find the position of the ctrl register for a BAR.
+ * Returns -ENOTSUPP if resizable BARs are not supported at all.
+ * Returns -ENOENT if no ctrl register for the BAR could be found.
+ */
+ static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
+ {
+ unsigned int pos, nbars, i;
+ u32 ctrl;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos)
+ return -ENOTSUPP;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
+ PCI_REBAR_CTRL_NBAR_SHIFT;
+
+ for (i = 0; i < nbars; i++, pos += 8) {
+ int bar_idx;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+ if (bar_idx == bar)
+ return pos;
+ }
+
+ return -ENOENT;
+ }
+
+ /**
+ * pci_rebar_get_possible_sizes - get possible sizes for BAR
+ * @pdev: PCI device
+ * @bar: BAR to query
+ *
+ * Get the possible sizes of a resizable BAR as bitmask defined in the spec
+ * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
+ */
+ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+ {
+ int pos;
+ u32 cap;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return 0;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+ return (cap & PCI_REBAR_CAP_SIZES) >> 4;
+ }
+
+ /**
+ * pci_rebar_get_current_size - get the current size of a BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ *
+ * Read the size of a BAR from the resizable BAR config.
+ * Returns size if found or negative error code.
+ */
+ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
+ {
+ int pos;
+ u32 ctrl;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return pos;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> 8;
+ }
+
+ /**
+ * pci_rebar_set_size - set a new size for a BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ * @size: new size as defined in the spec (0=1MB, 19=512GB)
+ *
+ * Set the new size of a BAR as defined in the spec.
+ * Returns zero if resizing was successful, error code otherwise.
+ */
+ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
+ {
+ int pos;
+ u32 ctrl;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return pos;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+ ctrl |= size << 8;
+ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+ return 0;
+ }
+
+ /**
* pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
* @dev: the PCI device
* @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD)
* All operations are managed and will be undone on driver detach.
*
* Returns a pointer to the remapped memory or an ERR_PTR() encoded error code
- * on failure. Usage example:
+ * on failure. Usage example::
*
* res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
* base = devm_pci_remap_cfg_resource(&pdev->dev, res);
break;
}
}
+
+ #define PCI_RES_TYPE_MASK \
+ (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\
+ IORESOURCE_MEM_64)
+
static void pci_bridge_release_resources(struct pci_bus *bus,
unsigned long type)
{
struct pci_dev *dev = bus->self;
struct resource *r;
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
unsigned old_flags = 0;
struct resource *b_res;
int idx = 1;
*/
release_child_resources(r);
if (!release_resource(r)) {
- type = old_flags = r->flags & type_mask;
+ type = old_flags = r->flags & PCI_RES_TYPE_MASK;
dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n",
PCI_BRIDGE_RESOURCES + idx, r);
/* keep the old size */
enum release_type rel_type = leaf_only;
LIST_HEAD(fail_head);
struct pci_dev_resource *fail_res;
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
int pci_try_num = 1;
enum enable_type enable_local;
*/
list_for_each_entry(fail_res, &fail_head, list)
pci_bus_release_bridge_resources(fail_res->dev->bus,
- fail_res->flags & type_mask,
+ fail_res->flags & PCI_RES_TYPE_MASK,
rel_type);
/* restore size and flags */
}
}
+static void extend_bridge_window(struct pci_dev *bridge, struct resource *res,
+ struct list_head *add_list, resource_size_t available)
+{
+ struct pci_dev_resource *dev_res;
+
+ if (res->parent)
+ return;
+
+ if (resource_size(res) >= available)
+ return;
+
+ dev_res = res_to_dev_res(add_list, res);
+ if (!dev_res)
+ return;
+
+ /* Is there room to extend the window? */
+ if (available - resource_size(res) <= dev_res->add_size)
+ return;
+
+ dev_res->add_size = available - resource_size(res);
+ dev_dbg(&bridge->dev, "bridge window %pR extended by %pa\n", res,
+ &dev_res->add_size);
+}
+
+static void pci_bus_distribute_available_resources(struct pci_bus *bus,
+ struct list_head *add_list, resource_size_t available_io,
+ resource_size_t available_mmio, resource_size_t available_mmio_pref)
+{
+ resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref;
+ unsigned int normal_bridges = 0, hotplug_bridges = 0;
+ struct resource *io_res, *mmio_res, *mmio_pref_res;
+ struct pci_dev *dev, *bridge = bus->self;
+
+ io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
+ mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
+ mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
+
+ /*
+ * Update additional resource list (add_list) to fill all the
+ * extra resource space available for this port except the space
+ * calculated in __pci_bus_size_bridges() which covers all the
+ * devices currently connected to the port and below.
+ */
+ extend_bridge_window(bridge, io_res, add_list, available_io);
+ extend_bridge_window(bridge, mmio_res, add_list, available_mmio);
+ extend_bridge_window(bridge, mmio_pref_res, add_list,
+ available_mmio_pref);
+
+ /*
+ * Calculate the total amount of extra resource space we can
+ * pass to bridges below this one. This is basically the
+ * extra space reduced by the minimal required space for the
+ * non-hotplug bridges.
+ */
+ remaining_io = available_io;
+ remaining_mmio = available_mmio;
+ remaining_mmio_pref = available_mmio_pref;
+
+ /*
+ * Calculate how many hotplug bridges and normal bridges there
+ * are on this bus. We will distribute the additional available
+ * resources between hotplug bridges.
+ */
+ for_each_pci_bridge(dev, bus) {
+ if (dev->is_hotplug_bridge)
+ hotplug_bridges++;
+ else
+ normal_bridges++;
+ }
+
+ for_each_pci_bridge(dev, bus) {
+ const struct resource *res;
+
+ if (dev->is_hotplug_bridge)
+ continue;
+
+ /*
+ * Reduce the available resource space by what the
+ * bridge and devices below it occupy.
+ */
+ res = &dev->resource[PCI_BRIDGE_RESOURCES + 0];
+ if (!res->parent && available_io > resource_size(res))
+ remaining_io -= resource_size(res);
+
+ res = &dev->resource[PCI_BRIDGE_RESOURCES + 1];
+ if (!res->parent && available_mmio > resource_size(res))
+ remaining_mmio -= resource_size(res);
+
+ res = &dev->resource[PCI_BRIDGE_RESOURCES + 2];
+ if (!res->parent && available_mmio_pref > resource_size(res))
+ remaining_mmio_pref -= resource_size(res);
+ }
+
+ /*
+ * Go over devices on this bus and distribute the remaining
+ * resource space between hotplug bridges.
+ */
+ for_each_pci_bridge(dev, bus) {
+ struct pci_bus *b;
+
+ b = dev->subordinate;
+ if (!b)
+ continue;
+
+ if (!hotplug_bridges && normal_bridges == 1) {
+ /*
+ * There is only one bridge on the bus (upstream
+ * port) so it gets all available resources
+ * which it can then distribute to the possible
+ * hotplug bridges below.
+ */
+ pci_bus_distribute_available_resources(b, add_list,
+ available_io, available_mmio,
+ available_mmio_pref);
+ } else if (dev->is_hotplug_bridge) {
+ resource_size_t align, io, mmio, mmio_pref;
+
+ /*
+ * Distribute available extra resources equally
+ * between hotplug-capable downstream ports
+ * taking alignment into account.
+ *
+ * Here hotplug_bridges is always != 0.
+ */
+ align = pci_resource_alignment(bridge, io_res);
+ io = div64_ul(available_io, hotplug_bridges);
+ io = min(ALIGN(io, align), remaining_io);
+ remaining_io -= io;
+
+ align = pci_resource_alignment(bridge, mmio_res);
+ mmio = div64_ul(available_mmio, hotplug_bridges);
+ mmio = min(ALIGN(mmio, align), remaining_mmio);
+ remaining_mmio -= mmio;
+
+ align = pci_resource_alignment(bridge, mmio_pref_res);
+ mmio_pref = div64_ul(available_mmio_pref,
+ hotplug_bridges);
+ mmio_pref = min(ALIGN(mmio_pref, align),
+ remaining_mmio_pref);
+ remaining_mmio_pref -= mmio_pref;
+
+ pci_bus_distribute_available_resources(b, add_list, io,
+ mmio, mmio_pref);
+ }
+ }
+}
+
+static void
+pci_bridge_distribute_available_resources(struct pci_dev *bridge,
+ struct list_head *add_list)
+{
+ resource_size_t available_io, available_mmio, available_mmio_pref;
+ const struct resource *res;
+
+ if (!bridge->is_hotplug_bridge)
+ return;
+
+ /* Take the initial extra resources from the hotplug port */
+ res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
+ available_io = resource_size(res);
+ res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
+ available_mmio = resource_size(res);
+ res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
+ available_mmio_pref = resource_size(res);
+
+ pci_bus_distribute_available_resources(bridge->subordinate,
+ add_list, available_io, available_mmio, available_mmio_pref);
+}
+
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
{
struct pci_bus *parent = bridge->subordinate;
LIST_HEAD(fail_head);
struct pci_dev_resource *fail_res;
int retval;
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
again:
__pci_bus_size_bridges(parent, &add_list);
+
+ /*
+ * Distribute remaining resources (if any) equally between
+ * hotplug bridges below. This makes it possible to extend the
+ * hierarchy later without running out of resources.
+ */
+ pci_bridge_distribute_available_resources(bridge, &add_list);
+
__pci_bridge_assign_resources(bridge, &add_list, &fail_head);
BUG_ON(!list_empty(&add_list));
tried_times++;
*/
list_for_each_entry(fail_res, &fail_head, list)
pci_bus_release_bridge_resources(fail_res->dev->bus,
- fail_res->flags & type_mask,
+ fail_res->flags & PCI_RES_TYPE_MASK,
whole_subtree);
/* restore size and flags */
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
+ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
+ {
+ struct pci_dev_resource *dev_res;
+ struct pci_dev *next;
+ LIST_HEAD(saved);
+ LIST_HEAD(added);
+ LIST_HEAD(failed);
+ unsigned int i;
+ int ret;
+
+ /* Walk to the root hub, releasing bridge BARs when possible */
+ next = bridge;
+ do {
+ bridge = next;
+ for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END;
+ i++) {
+ struct resource *res = &bridge->resource[i];
+
+ if ((res->flags ^ type) & PCI_RES_TYPE_MASK)
+ continue;
+
+ /* Ignore BARs which are still in use */
+ if (res->child)
+ continue;
+
+ ret = add_to_list(&saved, bridge, res, 0, 0);
+ if (ret)
+ goto cleanup;
+
+ dev_info(&bridge->dev, "BAR %d: releasing %pR\n",
+ i, res);
+
+ if (res->parent)
+ release_resource(res);
+ res->start = 0;
+ res->end = 0;
+ break;
+ }
+ if (i == PCI_BRIDGE_RESOURCE_END)
+ break;
+
+ next = bridge->bus ? bridge->bus->self : NULL;
+ } while (next);
+
+ if (list_empty(&saved))
+ return -ENOENT;
+
+ __pci_bus_size_bridges(bridge->subordinate, &added);
+ __pci_bridge_assign_resources(bridge, &added, &failed);
+ BUG_ON(!list_empty(&added));
+
+ if (!list_empty(&failed)) {
+ ret = -ENOSPC;
+ goto cleanup;
+ }
+
+ list_for_each_entry(dev_res, &saved, list) {
+ /* Skip the bridge we just assigned resources for. */
+ if (bridge == dev_res->dev)
+ continue;
+
+ bridge = dev_res->dev;
+ pci_setup_bridge(bridge->subordinate);
+ }
+
+ free_list(&saved);
+ return 0;
+
+ cleanup:
+ /* restore size and flags */
+ list_for_each_entry(dev_res, &failed, list) {
+ struct resource *res = dev_res->res;
+
+ res->start = dev_res->start;
+ res->end = dev_res->end;
+ res->flags = dev_res->flags;
+ }
+ free_list(&failed);
+
+ /* Revert to the old configuration */
+ list_for_each_entry(dev_res, &saved, list) {
+ struct resource *res = dev_res->res;
+
+ bridge = dev_res->dev;
+ i = res - bridge->resource;
+
+ res->start = dev_res->start;
+ res->end = dev_res->end;
+ res->flags = dev_res->flags;
+
+ pci_claim_resource(bridge, i);
+ pci_setup_bridge(bridge->subordinate);
+ }
+ free_list(&saved);
+
+ return ret;
+ }
+
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
{
struct pci_dev *dev;
want additional resources */
down_read(&pci_bus_sem);
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (pci_is_bridge(dev) && pci_has_subordinate(dev))
- __pci_bus_size_bridges(dev->subordinate,
- &add_list);
+ for_each_pci_bridge(dev, bus)
+ if (pci_has_subordinate(dev))
+ __pci_bus_size_bridges(dev->subordinate, &add_list);
up_read(&pci_bus_sem);
__pci_bus_assign_resources(bus, &add_list, NULL);
BUG_ON(!list_empty(&add_list));
#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First UNC is Fatal */
#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
+#define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */
#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */
/* Virtual Channel */
#define PCI_SATA_SIZEOF_LONG 16
/* Resizable BARs */
+ #define PCI_REBAR_CAP 4 /* capability register */
+ #define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */
#define PCI_REBAR_CTRL 8 /* control register */
- #define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */
- #define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */
+ #define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */
+ #define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */
+ #define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */
+ #define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */
/* Dynamic Power Allocation */
#define PCI_DPA_CAP 4 /* capability register */
/* Downstream Port Containment */
#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
+#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */
#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
#define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */
#define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */
-/* L1 PM Substates */
-#define PCI_L1SS_CAP 4 /* capability register */
-#define PCI_L1SS_CAP_PCIPM_L1_2 1 /* PCI PM L1.2 Support */
-#define PCI_L1SS_CAP_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
-#define PCI_L1SS_CAP_ASPM_L1_2 4 /* ASPM L1.2 Support */
-#define PCI_L1SS_CAP_ASPM_L1_1 8 /* ASPM L1.1 Support */
-#define PCI_L1SS_CAP_L1_PM_SS 16 /* L1 PM Substates Support */
-#define PCI_L1SS_CTL1 8 /* Control Register 1 */
-#define PCI_L1SS_CTL1_PCIPM_L1_2 1 /* PCI PM L1.2 Enable */
-#define PCI_L1SS_CTL1_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
-#define PCI_L1SS_CTL1_ASPM_L1_2 4 /* ASPM L1.2 Support */
-#define PCI_L1SS_CTL1_ASPM_L1_1 8 /* ASPM L1.1 Support */
-#define PCI_L1SS_CTL1_L1SS_MASK 0x0000000F
-#define PCI_L1SS_CTL2 0xC /* Control Register 2 */
+/* ASPM L1 PM Substates */
+#define PCI_L1SS_CAP 0x04 /* Capabilities Register */
+#define PCI_L1SS_CAP_PCIPM_L1_2 0x00000001 /* PCI-PM L1.2 Supported */
+#define PCI_L1SS_CAP_PCIPM_L1_1 0x00000002 /* PCI-PM L1.1 Supported */
+#define PCI_L1SS_CAP_ASPM_L1_2 0x00000004 /* ASPM L1.2 Supported */
+#define PCI_L1SS_CAP_ASPM_L1_1 0x00000008 /* ASPM L1.1 Supported */
+#define PCI_L1SS_CAP_L1_PM_SS 0x00000010 /* L1 PM Substates Supported */
+#define PCI_L1SS_CAP_CM_RESTORE_TIME 0x0000ff00 /* Port Common_Mode_Restore_Time */
+#define PCI_L1SS_CAP_P_PWR_ON_SCALE 0x00030000 /* Port T_POWER_ON scale */
+#define PCI_L1SS_CAP_P_PWR_ON_VALUE 0x00f80000 /* Port T_POWER_ON value */
+#define PCI_L1SS_CTL1 0x08 /* Control 1 Register */
+#define PCI_L1SS_CTL1_PCIPM_L1_2 0x00000001 /* PCI-PM L1.2 Enable */
+#define PCI_L1SS_CTL1_PCIPM_L1_1 0x00000002 /* PCI-PM L1.1 Enable */
+#define PCI_L1SS_CTL1_ASPM_L1_2 0x00000004 /* ASPM L1.2 Enable */
+#define PCI_L1SS_CTL1_ASPM_L1_1 0x00000008 /* ASPM L1.1 Enable */
+#define PCI_L1SS_CTL1_L1SS_MASK 0x0000000f
+#define PCI_L1SS_CTL1_CM_RESTORE_TIME 0x0000ff00 /* Common_Mode_Restore_Time */
+#define PCI_L1SS_CTL1_LTR_L12_TH_VALUE 0x03ff0000 /* LTR_L1.2_THRESHOLD_Value */
+#define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */
+#define PCI_L1SS_CTL2 0x0c /* Control 2 Register */
#endif /* LINUX_PCI_REGS_H */