Skip to content

Commit 59dc332

Browse files
committed
PCI: VMD: ACPI: Make ACPI companion lookup work for VMD bus
On some systems, in order to get to the deepest low-power state of the platform (which may be necessary to save significant enough amounts of energy while suspended to idle. for example), devices on the PCI bus exposed by the VMD driver need to be power-managed via ACPI. However, the layout of the ACPI namespace below the VMD controller device object does not reflect the layout of the PCI bus under the VMD host bridge, so in order to identify the ACPI companion objects for the devices on that bus, it is necessary to use a special _ADR encoding on the ACPI side. In other words, acpi_pci_find_companion() does not work for these devices, so it needs to be amended with a special lookup logic specific to the VMD bus. Address this issue by allowing the VMD driver to temporarily install an ACPI companion lookup hook containing the code matching the devices on the VMD PCI bus with the corresponding objects in the ACPI namespace. Signed-off-by: Rafael J. Wysocki <[email protected]> Acked-by: Jon Derrick <[email protected]>
1 parent 6f1e8b1 commit 59dc332

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

drivers/pci/controller/vmd.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/module.h>
1212
#include <linux/msi.h>
1313
#include <linux/pci.h>
14+
#include <linux/pci-acpi.h>
1415
#include <linux/pci-ecam.h>
1516
#include <linux/srcu.h>
1617
#include <linux/rculist.h>
@@ -447,6 +448,56 @@ static struct pci_ops vmd_ops = {
447448
.write = vmd_pci_write,
448449
};
449450

451+
#ifdef CONFIG_ACPI
452+
static struct acpi_device *vmd_acpi_find_companion(struct pci_dev *pci_dev)
453+
{
454+
struct pci_host_bridge *bridge;
455+
u32 busnr, addr;
456+
457+
if (pci_dev->bus->ops != &vmd_ops)
458+
return NULL;
459+
460+
bridge = pci_find_host_bridge(pci_dev->bus);
461+
busnr = pci_dev->bus->number - bridge->bus->number;
462+
/*
463+
* The address computation below is only applicable to relative bus
464+
* numbers below 32.
465+
*/
466+
if (busnr > 31)
467+
return NULL;
468+
469+
addr = (busnr << 24) | ((u32)pci_dev->devfn << 16) | 0x8000FFFFU;
470+
471+
dev_dbg(&pci_dev->dev, "Looking for ACPI companion (address 0x%x)\n",
472+
addr);
473+
474+
return acpi_find_child_device(ACPI_COMPANION(bridge->dev.parent), addr,
475+
false);
476+
}
477+
478+
static bool hook_installed;
479+
480+
static void vmd_acpi_begin(void)
481+
{
482+
if (pci_acpi_set_companion_lookup_hook(vmd_acpi_find_companion))
483+
return;
484+
485+
hook_installed = true;
486+
}
487+
488+
static void vmd_acpi_end(void)
489+
{
490+
if (!hook_installed)
491+
return;
492+
493+
pci_acpi_clear_companion_lookup_hook();
494+
hook_installed = false;
495+
}
496+
#else
497+
static inline void vmd_acpi_begin(void) { }
498+
static inline void vmd_acpi_end(void) { }
499+
#endif /* CONFIG_ACPI */
500+
450501
static void vmd_attach_resources(struct vmd_dev *vmd)
451502
{
452503
vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1];
@@ -747,6 +798,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
747798
if (vmd->irq_domain)
748799
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
749800

801+
vmd_acpi_begin();
802+
750803
pci_scan_child_bus(vmd->bus);
751804
pci_assign_unassigned_bus_resources(vmd->bus);
752805

@@ -760,6 +813,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
760813

761814
pci_bus_add_devices(vmd->bus);
762815

816+
vmd_acpi_end();
817+
763818
WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
764819
"domain"), "Can't create symlink to domain\n");
765820
return 0;

drivers/pci/host-bridge.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus)
2323

2424
return to_pci_host_bridge(root_bus->bridge);
2525
}
26+
EXPORT_SYMBOL_GPL(pci_find_host_bridge);
2627

2728
struct device *pci_get_host_bridge_device(struct pci_dev *dev)
2829
{

drivers/pci/pci-acpi.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/pci-acpi.h>
1818
#include <linux/pm_runtime.h>
1919
#include <linux/pm_qos.h>
20+
#include <linux/rwsem.h>
2021
#include "pci.h"
2122

2223
/*
@@ -1159,13 +1160,86 @@ void acpi_pci_remove_bus(struct pci_bus *bus)
11591160
}
11601161

11611162
/* ACPI bus type */
1163+
1164+
1165+
static DECLARE_RWSEM(pci_acpi_companion_lookup_sem);
1166+
static struct acpi_device *(*pci_acpi_find_companion_hook)(struct pci_dev *);
1167+
1168+
/**
1169+
* pci_acpi_set_companion_lookup_hook - Set ACPI companion lookup callback.
1170+
* @func: ACPI companion lookup callback pointer or NULL.
1171+
*
1172+
* Set a special ACPI companion lookup callback for PCI devices whose companion
1173+
* objects in the ACPI namespace have _ADR with non-standard bus-device-function
1174+
* encodings.
1175+
*
1176+
* Return 0 on success or a negative error code on failure (in which case no
1177+
* changes are made).
1178+
*
1179+
* The caller is responsible for the appropriate ordering of the invocations of
1180+
* this function with respect to the enumeration of the PCI devices needing the
1181+
* callback installed by it.
1182+
*/
1183+
int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *))
1184+
{
1185+
int ret;
1186+
1187+
if (!func)
1188+
return -EINVAL;
1189+
1190+
down_write(&pci_acpi_companion_lookup_sem);
1191+
1192+
if (pci_acpi_find_companion_hook) {
1193+
ret = -EBUSY;
1194+
} else {
1195+
pci_acpi_find_companion_hook = func;
1196+
ret = 0;
1197+
}
1198+
1199+
up_write(&pci_acpi_companion_lookup_sem);
1200+
1201+
return ret;
1202+
}
1203+
EXPORT_SYMBOL_GPL(pci_acpi_set_companion_lookup_hook);
1204+
1205+
/**
1206+
* pci_acpi_clear_companion_lookup_hook - Clear ACPI companion lookup callback.
1207+
*
1208+
* Clear the special ACPI companion lookup callback previously set by
1209+
* pci_acpi_set_companion_lookup_hook(). Block until the last running instance
1210+
* of the callback returns before clearing it.
1211+
*
1212+
* The caller is responsible for the appropriate ordering of the invocations of
1213+
* this function with respect to the enumeration of the PCI devices needing the
1214+
* callback cleared by it.
1215+
*/
1216+
void pci_acpi_clear_companion_lookup_hook(void)
1217+
{
1218+
down_write(&pci_acpi_companion_lookup_sem);
1219+
1220+
pci_acpi_find_companion_hook = NULL;
1221+
1222+
up_write(&pci_acpi_companion_lookup_sem);
1223+
}
1224+
EXPORT_SYMBOL_GPL(pci_acpi_clear_companion_lookup_hook);
1225+
11621226
static struct acpi_device *acpi_pci_find_companion(struct device *dev)
11631227
{
11641228
struct pci_dev *pci_dev = to_pci_dev(dev);
11651229
struct acpi_device *adev;
11661230
bool check_children;
11671231
u64 addr;
11681232

1233+
down_read(&pci_acpi_companion_lookup_sem);
1234+
1235+
adev = pci_acpi_find_companion_hook ?
1236+
pci_acpi_find_companion_hook(pci_dev) : NULL;
1237+
1238+
up_read(&pci_acpi_companion_lookup_sem);
1239+
1240+
if (adev)
1241+
return adev;
1242+
11691243
check_children = pci_is_bridge(pci_dev);
11701244
/* Please ref to ACPI spec for the syntax of _ADR */
11711245
addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);

include/linux/pci-acpi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ static inline void pci_acpi_add_edr_notifier(struct pci_dev *pdev) { }
122122
static inline void pci_acpi_remove_edr_notifier(struct pci_dev *pdev) { }
123123
#endif /* CONFIG_PCIE_EDR */
124124

125+
int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *));
126+
void pci_acpi_clear_companion_lookup_hook(void);
127+
125128
#else /* CONFIG_ACPI */
126129
static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
127130
static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }

0 commit comments

Comments
 (0)