Various fixes for PCI _OSC handling so HotPlug works again.

- Rename the default implementation of 'pcib_request_feature' and add
  a pcib_request_feature() wrapper function (as is often done for
  new-bus APIs implemented via kobj) that accepts a single function.
  Previously the call to pcib_request_feature() ended up invoking the
  method on the great-great-grandparent of the bridge device instead
  of the grandparent.  For a bridge that was a direct child of pci0 on
  x86 this resulted in the method skipping over the Host-PCI bridge
  driver and being invoked against nexus0
- When invoking _OSC from a Host-PCI bridge driver, invoke
  device_get_softc() against the Host-PCI bridge device instead of the
  child bridge that is requesting HotPlug.  Using the wrong softc data
  resulted in garbage being passed for the ACPI handle causing the
  _OSC call to fail.
- While here, perform some other cleanups to _OSC handling in the ACPI
  Host-PCI bridge driver:
  - Don't invoke _OSC when requesting a control that has already been
    granted by the firmware.
  - Don't set the first word of the capability array before invoking
    _OSC.  This word is always set explicitly by acpi_EvaluateOSC()
    since it is UUID-independent.
  - Don't modify the set of granted controls unless _OSC doesn't exist
    (which is treated as always successful), or the _OSC method
    doesn't fail.
  - Don't require an _OSC status of 0 for success.  _OSC always
    returns the updated control mask even if it returns a non-zero
    status in the first word.
  - Whine if _OSC ever tries to revoke a previously-granted control.
    (It is not supposed to do that.)
- While here, add constants for the _OSC status word in acpivar.h
  (though currently unused).

Reported by:	adrian
Reviewed by:	imp
MFC after:	1 week
Tested on:	Lenovo x220
Differential Revision:	https://reviews.freebsd.org/D10520
This commit is contained in:
John Baldwin 2017-04-27 16:32:42 +00:00
parent 32455e8912
commit 1ffd07bde6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=317510
4 changed files with 44 additions and 16 deletions

View File

@ -313,32 +313,42 @@ acpi_pcib_osc(struct acpi_hpcib_softc *sc, uint32_t osc_ctl)
0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66
};
/* Status Field */
cap_set[PCI_OSC_STATUS] = 0;
/*
* Don't invoke _OSC if a control is already granted.
* However, always invoke _OSC during attach when 0 is passed.
*/
if (osc_ctl != 0 && (sc->ap_osc_ctl & osc_ctl) == osc_ctl)
return (0);
/* Support Field: Extended PCI Config Space, MSI */
cap_set[PCI_OSC_SUPPORT] = PCIM_OSC_SUPPORT_EXT_PCI_CONF |
PCIM_OSC_SUPPORT_MSI;
/* Control Field */
sc->ap_osc_ctl |= osc_ctl;
cap_set[PCI_OSC_CTL] = sc->ap_osc_ctl;
cap_set[PCI_OSC_CTL] = sc->ap_osc_ctl | osc_ctl;
status = acpi_EvaluateOSC(sc->ap_handle, pci_host_bridge_uuid, 1,
nitems(cap_set), cap_set, cap_set, false);
if (ACPI_FAILURE(status)) {
if (status == AE_NOT_FOUND)
if (status == AE_NOT_FOUND) {
sc->ap_osc_ctl |= osc_ctl;
return (0);
}
device_printf(sc->ap_dev, "_OSC failed: %s\n",
AcpiFormatException(status));
return (EIO);
}
if (cap_set[PCI_OSC_STATUS] == 0)
sc->ap_osc_ctl = cap_set[PCI_OSC_CTL];
if (cap_set[PCI_OSC_STATUS] != 0 ||
(cap_set[PCI_OSC_CTL] & osc_ctl) != osc_ctl)
/*
* _OSC may return an error in the status word, but will
* update the control mask always. _OSC should not revoke
* previously-granted controls.
*/
if ((cap_set[PCI_OSC_CTL] & sc->ap_osc_ctl) != sc->ap_osc_ctl)
device_printf(sc->ap_dev, "_OSC revoked %#x\n",
(cap_set[PCI_OSC_CTL] & sc->ap_osc_ctl) ^ sc->ap_osc_ctl);
sc->ap_osc_ctl = cap_set[PCI_OSC_CTL];
if ((sc->ap_osc_ctl & osc_ctl) != osc_ctl)
return (EIO);
return (0);
@ -727,7 +737,7 @@ acpi_pcib_request_feature(device_t pcib, device_t dev, enum pci_feature feature)
uint32_t osc_ctl;
struct acpi_hpcib_softc *sc;
sc = device_get_softc(dev);
sc = device_get_softc(pcib);
switch (feature) {
case PCI_FEATURE_HP:

View File

@ -301,6 +301,12 @@ void acpi_EnterDebugger(void);
device_printf(dev, x); \
} while (0)
/* Values for the first status word returned by _OSC. */
#define ACPI_OSC_FAILURE (1 << 1)
#define ACPI_OSC_BAD_UUID (1 << 2)
#define ACPI_OSC_BAD_REVISION (1 << 3)
#define ACPI_OSC_CAPS_MASKED (1 << 4)
/* Values for the device _STA (status) method. */
#define ACPI_STA_PRESENT (1 << 0)
#define ACPI_STA_ENABLED (1 << 1)

View File

@ -76,7 +76,7 @@ static void pcib_pcie_ab_timeout(void *arg);
static void pcib_pcie_cc_timeout(void *arg);
static void pcib_pcie_dll_timeout(void *arg);
#endif
static int pcib_request_feature(device_t pcib, device_t dev,
static int pcib_request_feature_default(device_t pcib, device_t dev,
enum pci_feature feature);
static device_method_t pcib_methods[] = {
@ -121,7 +121,7 @@ static device_method_t pcib_methods[] = {
DEVMETHOD(pcib_try_enable_ari, pcib_try_enable_ari),
DEVMETHOD(pcib_ari_enabled, pcib_ari_enabled),
DEVMETHOD(pcib_decode_rid, pcib_ari_decode_rid),
DEVMETHOD(pcib_request_feature, pcib_request_feature),
DEVMETHOD(pcib_request_feature, pcib_request_feature_default),
DEVMETHOD_END
};
@ -964,8 +964,7 @@ pcib_probe_hotplug(struct pcib_softc *sc)
* Now that we're sure we want to do hot plug, ask the
* firmware, if any, if that's OK.
*/
if (pcib_request_feature(device_get_parent(device_get_parent(dev)), dev,
PCI_FEATURE_HP) != 0) {
if (pcib_request_feature(dev, PCI_FEATURE_HP) != 0) {
if (bootverbose)
device_printf(dev, "Unable to activate hot plug feature.\n");
return;
@ -2863,6 +2862,17 @@ pcib_request_feature_allow(device_t pcib, device_t dev,
return (0);
}
int
pcib_request_feature(device_t dev, enum pci_feature feature)
{
/*
* Invoke PCIB_REQUEST_FEATURE of this bridge first in case
* the firmware overrides the method of PCI-PCI bridges.
*/
return (PCIB_REQUEST_FEATURE(dev, dev, feature));
}
/*
* Pass the request to use this PCI feature up the tree. Either there's a
* firmware like ACPI that's using this feature that will approve (or deny) the
@ -2871,7 +2881,8 @@ pcib_request_feature_allow(device_t pcib, device_t dev,
* to make use of the feature or render it harmless.
*/
static int
pcib_request_feature(device_t pcib, device_t dev, enum pci_feature feature)
pcib_request_feature_default(device_t pcib, device_t dev,
enum pci_feature feature)
{
device_t bus;

View File

@ -193,6 +193,7 @@ int pcib_get_id(device_t pcib, device_t dev, enum pci_id_type type,
uintptr_t *id);
void pcib_decode_rid(device_t pcib, uint16_t rid, int *bus,
int *slot, int *func);
int pcib_request_feature(device_t dev, enum pci_feature feature);
int pcib_request_feature_allow(device_t pcib, device_t dev, enum pci_feature feature);
#endif