Re: [RFC PATCH 2/3] PCI: aardvark: Add support for sending Set_Slot_Power_Limit message

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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
> 
> 



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux