mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'pci/controller/dwc-qcom'
- Set max OPP during resume so DBI register accesses don't fail with NoC errors (Qiang Yu) - Add pci_host_common_d3cold_possible() to determine whether downstream devices are already in D3hot and wakeup-enabled devices are capable of generating PME from D3cold (Krishna Chaitanya Chundru) - Add a .get_ltssm() callback to get the LTSSM status without DBI, since DBI may be inaccessible after PME_Turn_Off (Krishna Chaitanya Chundru) - Power down PHY via PARF_PHY_CTRL before disabling rails/clocks to avoid power leakage (Krishna Chaitanya Chundru) - Decide whether suspend should put the link in L2 and power down using pci_host_common_d3cold_possible() instead of checking whether ASPM L1 is enabled (Krishna Chaitanya Chundru) - Add qcom D3cold support to tear down interconnect bandwidth and OPP votes (Krishna Chaitanya Chundru) - Handle unsupported mixed PERST#/PHY DT configurations, e.g., PHY in RP node while PERST# is in the RC node, but warn about the DT issue (Qiang Yu) - Add pcie_encode_t_power_on() to encode L1SS T_POWER_ON fields (Krishna Chaitanya Chundru) - Add dw_pcie_program_t_power_on() to program T_POWER_ON (Krishna Chaitanya Chundru) - Program qcom T_POWER_ON based on DT 't-power-on-us' property in case hardware advertises incorrect values (Krishna Chaitanya Chundru) - Disable ASPM L0s for SA8775P (Shawn Guo) - Initialize DWC MSI lock for firmware-managed ECAM hosts, which don't use the dw_pcie_host_init() path that initializes the lock (Yadu M G) * pci/controller/dwc-qcom: PCI: qcom: Initialize DWC MSI lock for firmware-managed ECAM hosts PCI: qcom: Disable ASPM L0s for SA8775P PCI: qcom: Program T_POWER_ON PCI: dwc: Add dw_pcie_program_t_power_on() to program T_POWER_ON PCI/ASPM: Add pcie_encode_t_power_on() helper to encode L1SS T_POWER_ON fields PCI: qcom: Handle mixed PERST#/PHY DT configuration PCI: qcom: Add D3cold support PCI: dwc: Use common D3cold eligibility helper in suspend path PCI: qcom: Power down PHY via PARF_PHY_CTRL before disabling rails/clocks PCI: qcom: Add .get_ltssm() callback to query LTSSM status PCI: host-common: Add pci_host_common_d3cold_possible() helper PCI: qcom: Set max OPP before DBI access during resume # Conflicts: # drivers/pci/controller/pci-host-common.c
This commit is contained in:
@@ -16,9 +16,11 @@
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "../pci-host-common.h"
|
||||
#include "../../pci.h"
|
||||
#include "pcie-designware.h"
|
||||
|
||||
@@ -1218,18 +1220,14 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
|
||||
|
||||
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
|
||||
{
|
||||
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
|
||||
bool pme_capable = false;
|
||||
int ret = 0;
|
||||
u32 val;
|
||||
|
||||
if (!dw_pcie_link_up(pci))
|
||||
goto stop_link;
|
||||
|
||||
/*
|
||||
* If L1SS is supported, then do not put the link into L2 as some
|
||||
* devices such as NVMe expect low resume latency.
|
||||
*/
|
||||
if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
|
||||
if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable))
|
||||
return 0;
|
||||
|
||||
if (pci->pp.ops->pme_turn_off) {
|
||||
@@ -1273,6 +1271,15 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
|
||||
udelay(1);
|
||||
|
||||
stop_link:
|
||||
/*
|
||||
* TODO: "pme_capable" means some downstream device is wakeup-
|
||||
* enabled and is capable of generating PME from D3cold, which
|
||||
* requires auxiliary power. Instead of always skipping power off
|
||||
* if PME is supported from D3cold, query the pwrctrl core and skip
|
||||
* power off only if device supports PME from D3cold and Vaux is
|
||||
* not supported.
|
||||
*/
|
||||
pci->pp.skip_pwrctrl_off = pme_capable;
|
||||
dw_pcie_stop_link(pci);
|
||||
if (pci->pp.ops->deinit)
|
||||
pci->pp.ops->deinit(&pci->pp);
|
||||
@@ -1290,8 +1297,6 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
|
||||
if (!pci->suspended)
|
||||
return 0;
|
||||
|
||||
pci->suspended = false;
|
||||
|
||||
if (pci->pp.ops->init) {
|
||||
ret = pci->pp.ops->init(&pci->pp);
|
||||
if (ret) {
|
||||
@@ -1313,6 +1318,8 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
|
||||
if (pci->pp.ops->post_init)
|
||||
pci->pp.ops->post_init(&pci->pp);
|
||||
|
||||
pci->suspended = false;
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_link:
|
||||
|
||||
@@ -1253,6 +1253,34 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
|
||||
dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
|
||||
}
|
||||
|
||||
/* TODO: Need to handle multi Root Ports */
|
||||
void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on)
|
||||
{
|
||||
u8 scale, value;
|
||||
u16 offset;
|
||||
u32 val;
|
||||
|
||||
if (!t_power_on)
|
||||
return;
|
||||
|
||||
offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
|
||||
if (!offset)
|
||||
return;
|
||||
|
||||
pcie_encode_t_power_on(t_power_on, &scale, &value);
|
||||
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, offset + PCI_L1SS_CAP);
|
||||
val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE);
|
||||
FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_SCALE, &val, scale);
|
||||
FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, &val, value);
|
||||
|
||||
dw_pcie_writel_dbi(pci, offset + PCI_L1SS_CAP, val);
|
||||
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
}
|
||||
|
||||
void dw_pcie_setup(struct dw_pcie *pci)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
@@ -451,6 +451,7 @@ struct dw_pcie_rp {
|
||||
bool ecam_enabled;
|
||||
bool native_ecam;
|
||||
bool skip_l23_ready;
|
||||
bool skip_pwrctrl_off;
|
||||
};
|
||||
|
||||
struct dw_pcie_ep_ops {
|
||||
@@ -608,6 +609,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
u8 bar, size_t size);
|
||||
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
|
||||
void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
|
||||
void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on);
|
||||
void dw_pcie_setup(struct dw_pcie *pci);
|
||||
void dw_pcie_iatu_detect(struct dw_pcie *pci);
|
||||
int dw_pcie_edma_detect(struct dw_pcie *pci);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -186,6 +186,10 @@ err_cleanup:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_parse_ports);
|
||||
|
||||
#define PCI_HOST_D3COLD_ALLOWED BIT(0)
|
||||
#define PCI_HOST_PME_D3COLD_CAPABLE BIT(1)
|
||||
|
||||
|
||||
static void gen_pci_unmap_cfg(void *ptr)
|
||||
{
|
||||
pci_ecam_free((struct pci_config_window *)ptr);
|
||||
@@ -279,5 +283,64 @@ void pci_host_common_remove(struct platform_device *pdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_remove);
|
||||
|
||||
static int __pci_host_common_d3cold_possible(struct pci_dev *pdev,
|
||||
void *userdata)
|
||||
{
|
||||
u32 *flags = userdata;
|
||||
|
||||
if (!pdev->dev.driver && !pci_is_enabled(pdev))
|
||||
return 0;
|
||||
|
||||
if (pdev->current_state != PCI_D3hot)
|
||||
goto exit;
|
||||
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
if (!pci_pme_capable(pdev, PCI_D3cold))
|
||||
goto exit;
|
||||
else
|
||||
*flags |= PCI_HOST_PME_D3COLD_CAPABLE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
*flags &= ~PCI_HOST_D3COLD_ALLOWED;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_host_common_d3cold_possible - Determine whether the host bridge can
|
||||
* transition the devices into D3cold.
|
||||
*
|
||||
* @bridge: PCI host bridge to check
|
||||
* @pme_capable: Pointer to update if there is any device capable of generating
|
||||
* PME from D3cold.
|
||||
*
|
||||
* Walk downstream PCIe endpoint devices and determine whether the host bridge
|
||||
* is permitted to transition the devices into D3cold.
|
||||
*
|
||||
* Devices under host bridge can enter D3cold only if all active PCIe
|
||||
* endpoints are in PCI_D3hot and any wakeup-enabled endpoint is capable of
|
||||
* generating PME from D3cold. Inactive endpoints are ignored.
|
||||
*
|
||||
* The @pme_capable output allows PCIe controller drivers to apply
|
||||
* platform-specific handling to preserve wakeup functionality.
|
||||
*
|
||||
* Return: %true if the host bridge may enter D3cold, otherwise %false.
|
||||
*/
|
||||
bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
|
||||
bool *pme_capable)
|
||||
{
|
||||
u32 flags = PCI_HOST_D3COLD_ALLOWED;
|
||||
|
||||
pci_walk_bus(bridge->bus, __pci_host_common_d3cold_possible, &flags);
|
||||
|
||||
*pme_capable = !!(flags & PCI_HOST_PME_D3COLD_CAPABLE);
|
||||
|
||||
return !!(flags & PCI_HOST_D3COLD_ALLOWED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible);
|
||||
|
||||
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -48,4 +48,7 @@ void pci_host_common_remove(struct platform_device *pdev);
|
||||
|
||||
struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
|
||||
struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops);
|
||||
|
||||
bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
|
||||
bool *pme_capable);
|
||||
#endif
|
||||
|
||||
@@ -1094,6 +1094,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked);
|
||||
void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
|
||||
void pci_configure_ltr(struct pci_dev *pdev);
|
||||
void pci_bridge_reconfigure_ltr(struct pci_dev *pdev);
|
||||
void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value);
|
||||
#else
|
||||
static inline void pcie_aspm_remove_cap(struct pci_dev *pdev, u32 lnkcap) { }
|
||||
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { }
|
||||
@@ -1102,6 +1103,11 @@ static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked)
|
||||
static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { }
|
||||
static inline void pci_configure_ltr(struct pci_dev *pdev) { }
|
||||
static inline void pci_bridge_reconfigure_ltr(struct pci_dev *pdev) { }
|
||||
static inline void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value)
|
||||
{
|
||||
*scale = 0;
|
||||
*value = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIE_ECRC
|
||||
|
||||
@@ -525,6 +525,46 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields
|
||||
* @t_power_on_us: T_POWER_ON time in microseconds
|
||||
* @scale: Encoded T_POWER_ON Scale (0..2)
|
||||
* @value: Encoded T_POWER_ON Value
|
||||
*
|
||||
* T_POWER_ON is encoded as:
|
||||
* T_POWER_ON(us) = scale_unit(us) * value
|
||||
*
|
||||
* where scale_unit is selected by @scale:
|
||||
* 0: 2us
|
||||
* 1: 10us
|
||||
* 2: 100us
|
||||
*
|
||||
* If @t_power_on_us exceeds the maximum representable value, the result
|
||||
* is clamped to the largest encodable T_POWER_ON.
|
||||
*
|
||||
* See PCIe r7.0, sec 7.8.3.2.
|
||||
*/
|
||||
void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value)
|
||||
{
|
||||
u8 maxv = FIELD_MAX(PCI_L1SS_CAP_P_PWR_ON_VALUE);
|
||||
|
||||
/* T_POWER_ON_Value ("value") is a 5-bit field with max value of 31. */
|
||||
if (t_power_on_us <= 2 * maxv) {
|
||||
*scale = 0; /* Value times 2us */
|
||||
*value = DIV_ROUND_UP(t_power_on_us, 2);
|
||||
} else if (t_power_on_us <= 10 * maxv) {
|
||||
*scale = 1; /* Value times 10us */
|
||||
*value = DIV_ROUND_UP(t_power_on_us, 10);
|
||||
} else if (t_power_on_us <= 100 * maxv) {
|
||||
*scale = 2; /* value times 100us */
|
||||
*value = DIV_ROUND_UP(t_power_on_us, 100);
|
||||
} else {
|
||||
*scale = 2;
|
||||
*value = maxv;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(pcie_encode_t_power_on);
|
||||
|
||||
/*
|
||||
* Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1
|
||||
* register. Ports enter L1.2 when the most recent LTR value is greater
|
||||
|
||||
Reference in New Issue
Block a user