On Fri, Aug 20, 2021 at 06:00:22PM +0200, Pali Rohár wrote: > According to PCIe Base specification 3.0, when transitioning from a > non-DL_Up Status to a DL_Up Status, the Port must initiate the > transmission of a Set_Slot_Power_Limit Message to the other component > on the Link to convey the value programmed in the Slot Power Limit > Scale and Value fields of the Slot Capabilities register. This > Transmission is optional if the Slot Capabilities register has not > yet been initialized. > > As PCIe Root Bridge is emulated by kernel emulate readback of Slot Power > Limit Scale and Value bits in Slot Capabilities register. > > Also send that Set_Slot_Power_Limit message via Message Generation Control > Register in Link Up handler when link changes from down to up state and > slot power limit value was defined. > > Slot power limit value is read from DT property 'slot-power-limit' which > value is in mW unit. When this DT property is not specified then it is > treated as "Slot Capabilities register has not yet been initialized". > > Signed-off-by: Pali Rohár <pali@xxxxxxxxxx> > --- > .../devicetree/bindings/pci/aardvark-pci.txt | 2 + > drivers/pci/controller/pci-aardvark.c | 66 ++++++++++++++++++- > 2 files changed, 67 insertions(+), 1 deletion(-) > > diff --git a/Documentation/devicetree/bindings/pci/aardvark-pci.txt b/Documentation/devicetree/bindings/pci/aardvark-pci.txt > index 2b8ca920a7fa..bb658f261db0 100644 > --- a/Documentation/devicetree/bindings/pci/aardvark-pci.txt > +++ b/Documentation/devicetree/bindings/pci/aardvark-pci.txt > @@ -20,6 +20,7 @@ contain the following properties: > define the mapping of the PCIe interface to interrupt numbers. > - bus-range: PCI bus numbers covered > - phys: the PCIe PHY handle > + - slot-power-limit: see pci.txt > - max-link-speed: see pci.txt > - reset-gpios: see pci.txt > > @@ -52,6 +53,7 @@ Example: > <0 0 0 3 &pcie_intc 2>, > <0 0 0 4 &pcie_intc 3>; > phys = <&comphy1 0>; > + slot-power-limit: <10000>; > pcie_intc: interrupt-controller { > interrupt-controller; > #interrupt-cells = <1>; > diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c > index f94898f6072f..cf704c199c15 100644 > --- a/drivers/pci/controller/pci-aardvark.c > +++ b/drivers/pci/controller/pci-aardvark.c > @@ -166,6 +166,11 @@ > #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) > #define DEBUG_MUX_CTRL_REG (LMI_BASE_ADDR + 0x208) > #define DIS_ORD_CHK BIT(30) > +#define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220) > +#define SEND_SET_SLOT_POWER_LIMIT BIT(13) > +#define SEND_PME_TURN_OFF BIT(14) > +#define SLOT_POWER_LIMIT_DATA_SHIFT 16 > +#define SLOT_POWER_LIMIT_DATA_MASK GENMASK(25, 16) > > /* PCIe core controller registers */ > #define CTRL_CORE_BASE_ADDR 0x18000 > @@ -267,6 +272,7 @@ static bool advk_pcie_link_up(struct advk_pcie *pcie) > { > u32 val, ltssm_state; > u16 slotsta, slotctl; > + u32 slotpwr; > bool link_up; > > val = advk_readl(pcie, CFG_REG); > @@ -277,7 +283,25 @@ static bool advk_pcie_link_up(struct advk_pcie *pcie) > pcie->link_up = true; > slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); > slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl); > + slotpwr = (le32_to_cpu(pcie->bridge.pcie_conf.slotcap) & > + (PCI_EXP_SLTCAP_SPLV | PCI_EXP_SLTCAP_SPLS)) >> 7; > pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); > + if (!(slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE) && slotpwr) { > + /* > + * According to PCIe Base specification 3.0, when transitioning from a > + * non-DL_Up Status to a DL_Up Status, the Port must initiate the > + * transmission of a Set_Slot_Power_Limit Message to the other component > + * on the Link to convey the value programmed in the Slot Power Limit > + * Scale and Value fields of the Slot Capabilities register. This > + * Transmission is optional if the Slot Capabilities register has not > + * yet been initialized. > + */ > + val = advk_readl(pcie, PME_MSG_GEN_CTRL); > + val &= ~SLOT_POWER_LIMIT_DATA_MASK; > + val |= slotpwr << SLOT_POWER_LIMIT_DATA_SHIFT; > + val |= SEND_SET_SLOT_POWER_LIMIT; > + advk_writel(pcie, val, PME_MSG_GEN_CTRL); > + } > if ((slotctl & PCI_EXP_SLTCTL_DLLSCE) && (slotctl & PCI_EXP_SLTCTL_HPIE)) > mod_timer(&pcie->link_up_irq_timer, jiffies + 1); > } > @@ -956,6 +980,9 @@ static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { > static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) > { > struct pci_bridge_emul *bridge = &pcie->bridge; > + struct device *dev = &pcie->pdev->dev; > + u8 slot_power_limit_scale, slot_power_limit_value; > + u32 slot_power_limit; > int ret; > > bridge->conf.vendor = > @@ -988,6 +1015,40 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) > /* Indicates supports for Completion Retry Status */ > bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS); > > + if (of_property_read_u32(dev->of_node, "slot-power-limit", &slot_power_limit)) > + slot_power_limit = 0; > + > + if (slot_power_limit) > + dev_info(dev, "Slot power limit %u.%uW\n", slot_power_limit / 1000, > + (slot_power_limit / 100) % 10); Add a wrapper function for this. > + > + /* Calculate Slot Power Limit Value and Slot Power Limit Scale */ > + if (slot_power_limit == 0) { > + slot_power_limit_scale = 0; > + slot_power_limit_value = 0x00; > + } else if (slot_power_limit <= 255) { > + slot_power_limit_scale = 3; > + slot_power_limit_value = slot_power_limit; > + } else if (slot_power_limit <= 255*10) { > + slot_power_limit_scale = 2; > + slot_power_limit_value = slot_power_limit / 10; > + } else if (slot_power_limit <= 255*100) { > + slot_power_limit_scale = 1; > + slot_power_limit_value = slot_power_limit / 100; > + } else if (slot_power_limit <= 239*1000) { > + slot_power_limit_scale = 0; > + slot_power_limit_value = slot_power_limit / 1000; > + } else if (slot_power_limit <= 250*1000) { > + slot_power_limit_scale = 0; > + slot_power_limit_value = 0xF0; > + } else if (slot_power_limit <= 275*1000) { > + slot_power_limit_scale = 0; > + slot_power_limit_value = 0xF1; > + } else { > + slot_power_limit_scale = 0; > + slot_power_limit_value = 0xF2; > + } This all looks like it should be common code. > + > /* > * Mark bridge as Hot-Plug Capable to allow delivering Data Link Layer > * State Changed interrupt. No Command Completed Support is set because > @@ -996,7 +1057,10 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) > * bit permanently as there is no support for unplugging PCIe card from > * the slot. Assume that PCIe card is always connected in slot. > */ > - bridge->pcie_conf.slotcap = cpu_to_le32(PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC); > + bridge->pcie_conf.slotcap = cpu_to_le32(PCI_EXP_SLTCAP_NCCS | > + PCI_EXP_SLTCAP_HPC | While not new, how does the driver know the board is hotplug capable. IIRC, we have a property for that. > + slot_power_limit_value << 7 | > + slot_power_limit_scale << 15); > bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); > > return 0; > -- > 2.20.1 > >