aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/setup-res.c
diff options
context:
space:
mode:
authorChristian König <christian.koenig@amd.com>2017-10-24 15:40:26 -0400
committerBjorn Helgaas <bhelgaas@google.com>2017-10-25 17:07:31 -0400
commit8bb705e3e79d84e77edd4499e74483dd96a4626c (patch)
tree6e8ec3230481d768d7a5a53e12d6975fb1bd9e9f /drivers/pci/setup-res.c
parent276b738deb5bf856b9f6049fcd92a967f52643d7 (diff)
PCI: Add pci_resize_resource() for resizing BARs
Add a pci_resize_resource() interface to allow device drivers to resize BARs of their devices. This is useful for devices with large local storage, e.g., graphics devices. These devices often only expose 256MB BARs initially to be compatible with 32-bit systems. This function only tries to reprogram the windows of the bridge directly above the requesting device and only the BAR of the same type (usually mem, 64bit, prefetchable). This is done to avoid disturbing other drivers by changing the BARs of their devices. Drivers should use the following sequence to resize their BARs: 1. Disable memory decoding of the device using the PCI cfg dword. 2. Use pci_release_resource() to release all BARs which can move during the resize, including the one you want to resize. 3. Call pci_resize_resource() for each BAR you want to resize. 4. Call pci_assign_unassigned_bus_resources() to reassign new locations for all BARs which are not resized, but could move. 5. If everything worked as expected, enable memory decoding in the device again using the PCI cfg dword. Signed-off-by: Christian König <christian.koenig@amd.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/setup-res.c')
-rw-r--r--drivers/pci/setup-res.c58
1 files changed, 58 insertions, 0 deletions
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index e576e1a8d978..bf0089ef2177 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -396,6 +396,64 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz
396 return 0; 396 return 0;
397} 397}
398 398
399void pci_release_resource(struct pci_dev *dev, int resno)
400{
401 struct resource *res = dev->resource + resno;
402
403 dev_info(&dev->dev, "BAR %d: releasing %pR\n", resno, res);
404 release_resource(res);
405 res->end = resource_size(res) - 1;
406 res->start = 0;
407 res->flags |= IORESOURCE_UNSET;
408}
409EXPORT_SYMBOL(pci_release_resource);
410
411int pci_resize_resource(struct pci_dev *dev, int resno, int size)
412{
413 struct resource *res = dev->resource + resno;
414 int old, ret;
415 u32 sizes;
416 u16 cmd;
417
418 /* Make sure the resource isn't assigned before resizing it. */
419 if (!(res->flags & IORESOURCE_UNSET))
420 return -EBUSY;
421
422 pci_read_config_word(dev, PCI_COMMAND, &cmd);
423 if (cmd & PCI_COMMAND_MEMORY)
424 return -EBUSY;
425
426 sizes = pci_rebar_get_possible_sizes(dev, resno);
427 if (!sizes)
428 return -ENOTSUPP;
429
430 if (!(sizes & BIT(size)))
431 return -EINVAL;
432
433 old = pci_rebar_get_current_size(dev, resno);
434 if (old < 0)
435 return old;
436
437 ret = pci_rebar_set_size(dev, resno, size);
438 if (ret)
439 return ret;
440
441 res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
442
443 /* Check if the new config works by trying to assign everything. */
444 ret = pci_reassign_bridge_resources(dev->bus->self, res->flags);
445 if (ret)
446 goto error_resize;
447
448 return 0;
449
450error_resize:
451 pci_rebar_set_size(dev, resno, old);
452 res->end = res->start + pci_rebar_size_to_bytes(old) - 1;
453 return ret;
454}
455EXPORT_SYMBOL(pci_resize_resource);
456
399int pci_enable_resources(struct pci_dev *dev, int mask) 457int pci_enable_resources(struct pci_dev *dev, int mask)
400{ 458{
401 u16 cmd, old_cmd; 459 u16 cmd, old_cmd;