mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-30 20:35:03 +01:00
pci: Expose PME support by power state
The presence of the PCI power management capability does not imply that
a function can signal PME# from every power state. Drivers which
advertise wake based only on pci_has_pm() can consequently expose wake
modes that cannot work.
Add pci_has_pme() to query the PME_Support bitmap for a specific state.
Use it to implement LinuxKPI pci_pme_capable(), removing its duplicate
PME_Support decoder.
Validated the helper against PCI PMC capability values from 82571EB,
82573L, 82579LM, I210, I225, and I226-V controllers. The 82571 and
82573 reported PMC 0xc822, while the I226-V reported 0xc823. In both
values, bits 15, 14, and 11 advertise PME from D3cold, D3hot, and D0;
the low-bit difference is only the PM capability version.
Sponsored by: BBOX.io
(cherry picked from commit c4e24e95c3)
This commit is contained in:
1 parent
8e1ad850df
commit
d831cb2ad3
5 files changed
+41
-19
No files matched your search
@@ -1810,6 +1810,7 @@ MLINKS+=pci.9 is_pci_device.9 \
|
||||
pci.9 pci_get_vpd_ident.9 \
|
||||
pci.9 pci_get_vpd_readonly.9 \
|
||||
pci.9 pci_has_pm.9 \
|
||||
pci.9 pci_has_pme.9 \
|
||||
pci.9 pci_iov_attach.9 \
|
||||
pci.9 pci_iov_attach_name.9 \
|
||||
pci.9 pci_iov_detach.9 \
|
||||
|
||||
+18
-1
@@ -23,7 +23,7 @@
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd August 15, 2026
|
||||
.Dd August 30, 2026
|
||||
.Dt PCI 9
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -54,6 +54,7 @@
|
||||
.Nm pci_get_vpd_ident ,
|
||||
.Nm pci_get_vpd_readonly ,
|
||||
.Nm pci_has_pm ,
|
||||
.Nm pci_has_pme ,
|
||||
.Nm pci_iov_attach ,
|
||||
.Nm pci_iov_attach_name ,
|
||||
.Nm pci_iov_detach ,
|
||||
@@ -134,6 +135,8 @@
|
||||
.Fn pci_get_vpd_readonly "device_t dev" "const char *kw" "const char **vptr"
|
||||
.Ft bool
|
||||
.Fn pci_has_pm "device_t dev"
|
||||
.Ft bool
|
||||
.Fn pci_has_pme "device_t dev" "int state"
|
||||
.Ft int
|
||||
.Fn pci_msi_count "device_t dev"
|
||||
.Ft int
|
||||
@@ -391,6 +394,20 @@ function returns true if
|
||||
supports power management.
|
||||
.Pp
|
||||
The
|
||||
.Fn pci_has_pme
|
||||
function returns true if
|
||||
.Fa dev
|
||||
can generate a power management event from the PCI power state
|
||||
.Fa state .
|
||||
The state must be one of
|
||||
.Dv PCI_POWERSTATE_D0 ,
|
||||
.Dv PCI_POWERSTATE_D1 ,
|
||||
.Dv PCI_POWERSTATE_D2 ,
|
||||
.Dv PCI_POWERSTATE_D3_HOT ,
|
||||
or
|
||||
.Dv PCI_POWERSTATE_D3_COLD .
|
||||
.Pp
|
||||
The
|
||||
.Fn pci_find_extcap
|
||||
function is used to locate the first instance of a PCI-express
|
||||
extended capability register set for the device
|
||||
|
||||
@@ -632,26 +632,10 @@ pci_find_ext_capability(struct pci_dev *pdev, int capid)
|
||||
return (reg);
|
||||
}
|
||||
|
||||
#define PCIM_PCAP_PME_SHIFT 11
|
||||
static __inline bool
|
||||
pci_pme_capable(struct pci_dev *pdev, uint32_t flag)
|
||||
pci_pme_capable(struct pci_dev *pdev, pci_power_t state)
|
||||
{
|
||||
struct pci_devinfo *dinfo;
|
||||
pcicfgregs *cfg;
|
||||
|
||||
if (flag > (PCIM_PCAP_D3PME_COLD >> PCIM_PCAP_PME_SHIFT))
|
||||
return (false);
|
||||
|
||||
dinfo = device_get_ivars(pdev->dev.bsddev);
|
||||
cfg = &dinfo->cfg;
|
||||
|
||||
if (cfg->pp.pp_cap == 0)
|
||||
return (false);
|
||||
|
||||
if ((cfg->pp.pp_cap & (1 << (PCIM_PCAP_PME_SHIFT + flag))) != 0)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
return (pci_has_pme(pdev->dev.bsddev, state));
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
||||
@@ -3002,6 +3002,25 @@ pci_has_pm(device_t dev)
|
||||
return (cfg->pp.pp_cap != 0);
|
||||
}
|
||||
|
||||
bool
|
||||
pci_has_pme(device_t dev, int state)
|
||||
{
|
||||
static const uint16_t pme_mask[PCI_POWERSTATE_COUNT] = {
|
||||
[PCI_POWERSTATE_D0] = PCIM_PCAP_D0PME,
|
||||
[PCI_POWERSTATE_D1] = PCIM_PCAP_D1PME,
|
||||
[PCI_POWERSTATE_D2] = PCIM_PCAP_D2PME,
|
||||
[PCI_POWERSTATE_D3_HOT] = PCIM_PCAP_D3PME_HOT,
|
||||
[PCI_POWERSTATE_D3_COLD] = PCIM_PCAP_D3PME_COLD,
|
||||
};
|
||||
struct pci_devinfo *dinfo = device_get_ivars(dev);
|
||||
pcicfgregs *cfg = &dinfo->cfg;
|
||||
|
||||
if (state < PCI_POWERSTATE_D0 || state > PCI_POWERSTATE_MAX)
|
||||
return (false);
|
||||
return (cfg->pp.pp_location != 0 &&
|
||||
(cfg->pp.pp_cap & pme_mask[state]) != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some convenience functions for PCI device drivers.
|
||||
*/
|
||||
|
||||
@@ -691,6 +691,7 @@ int pci_power_reset(device_t dev);
|
||||
void pci_clear_pme(device_t dev);
|
||||
void pci_enable_pme(device_t dev);
|
||||
bool pci_has_pm(device_t dev);
|
||||
bool pci_has_pme(device_t dev, int state);
|
||||
uint32_t pcie_read_config(device_t dev, int reg, int width);
|
||||
void pcie_write_config(device_t dev, int reg, uint32_t value, int width);
|
||||
uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask,
|
||||
|
||||
Reference in new issue
Block a user