Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a13ebeb9 authored by Sai Chaitanya Kaveti's avatar Sai Chaitanya Kaveti
Browse files

msm: ep_pcie: Sending PME for device initiated D3cold exit



According to the PCIe specification, PME message is to be sent for
device initiated D3cold to D0 transition. To support this sending PME
immediately after link training while transitioning from D3cold to D0.

As retention flops are not available for sdxlemur, sticky bits in the
registers are not retained during low power state. PME enable bit is
being cleared because of this. To handle this added a flag to check the
status of pme_en after D3 event and set pme_en during link training if
the flag is set.

Change-Id: I474a675a35566c2ff051374c9d212c47d08d622d
Signed-off-by: default avatarSai Chaitanya Kaveti <quic_skaveti@quicinc.com>
parent 2cbfc9cf
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -94,6 +94,8 @@
#define PCIE20_SUBSYSTEM               0x2c
#define PCIE20_CAP_ID_NXT_PTR          0x40
#define PCIE20_CON_STATUS              0x44
#define PCIE20_MASK_PME_EN             BIT(8)
#define PCIE20_MASK_PME_STATUS         BIT(15)
#define PCIE20_MSI_CAP_ID_NEXT_CTRL    0x50
#define PCIE20_MSI_LOWER               0x54
#define PCIE20_MSI_UPPER               0x58
@@ -360,6 +362,7 @@ struct ep_pcie_dev_t {
	bool			     mhi_soc_reset_en;
	bool			     aoss_rst_clear;
	bool			     avoid_reboot_in_d3hot;
	bool			     pme_in_wake_from_d3cold;
	u32                          dbi_base_reg;
	u32                          slv_space_reg;
	u32                          phy_status_reg;
@@ -411,6 +414,8 @@ struct ep_pcie_dev_t {
	int                          perst_irq;
	atomic_t                     host_wake_pending;
	bool			     conf_ipa_msi_iatu;
	bool			     wake_from_d3cold;
	bool			     pme_en_d3;

	struct ep_pcie_register_event *event_reg;
	struct work_struct           handle_bme_work;
+83 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/interconnect.h>
#include <linux/iopoll.h>

#include "ep_pcie_com.h"
#include <asm/dma-iommu.h>
@@ -46,6 +47,9 @@
#define ICC_AVG_BW				500
#define ICC_PEAK_BW				800
#define PERST_RAW_RESET_STATUS			BIT(11)
#define PCIE_PME_COUNT				2
#define PCIE_PME_SLEEP_DELAY_US		10000
#define PCIE_PME_RESEND_DELAY_US		100000

/* debug mask sys interface */
static int ep_pcie_debug_mask;
@@ -120,6 +124,7 @@ static const struct ep_pcie_irq_info_t ep_pcie_irq_info[EP_PCIE_MAX_IRQ] = {
};

static int ep_pcie_core_wakeup_host_internal(enum ep_pcie_event event);
static void ep_pcie_core_issue_inband_pme(void);

int ep_pcie_get_debug_mask(void)
{
@@ -790,6 +795,17 @@ static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured)
		ep_pcie_write_mask(dev->dm_core + PCIE20_CAP_ID_NXT_PTR, 0,
						BIT(31)|BIT(30)|BIT(27));

		if (dev->pme_in_wake_from_d3cold && dev->pme_en_d3) {
			/*
			 * PME_ENABLE is cleared after d3cold state if
			 * retention flops are not present in the target.
			 * Set PME_ENABLE, if it is seen as set during
			 * d3hot to send PME message
			 */
			ep_pcie_write_mask(dev->dm_core + PCIE20_CON_STATUS, 0, PCIE20_MASK_PME_EN);
			dev->pme_en_d3 = false;
		}

		/* Set the Endpoint L0s Acceptable Latency to 1us (max) */
		ep_pcie_write_reg_field(dev->dm_core,
			PCIE20_DEVICE_CAPABILITIES,
@@ -1675,6 +1691,46 @@ static void ep_pcie_core_toggle_wake_gpio(bool is_on)

}

static void ep_pcie_core_issue_pme_for_wake_from_d3cold(void)
{
	struct ep_pcie_dev_t *dev = &ep_pcie_dev;
	u32 pme_count = 0, pme_reg;
	bool pme_en, pme_status;

	pme_reg = readl_relaxed(dev->dm_core + PCIE20_CON_STATUS);
	pme_en = pme_reg & PCIE20_MASK_PME_EN;
	pme_status = pme_reg & PCIE20_MASK_PME_STATUS;

	EP_PCIE_INFO(dev, "PCIe V%d: pme_reg: 0x%x, pme_en: %d, pme_status: %d\n",
			dev->rev, pme_reg, pme_en, pme_status);

	if (pme_en) {
		do {
			/* Sending PME for device initiated d3_cold exit */
			ep_pcie_core_issue_inband_pme();
			/*Polling for pme_status to be 0 for 100ms*/
			readx_poll_timeout(readl_relaxed, dev->dm_core + PCIE20_CON_STATUS, pme_reg,
				!(pme_reg & PCIE20_MASK_PME_STATUS), PCIE_PME_SLEEP_DELAY_US,
				PCIE_PME_RESEND_DELAY_US);

			pme_status = pme_reg & PCIE20_MASK_PME_STATUS;
			EP_PCIE_DBG(dev, "PCIe V%d: pme_status:%d\n", dev->rev, pme_status);

			pme_count++;

			if (!pme_status)
				break;

		} while (pme_count < PCIE_PME_COUNT);

		if (pme_status && pme_count == PCIE_PME_COUNT) {
			EP_PCIE_INFO(dev,
			"PCIe V%d: Host failed to acknowledge PME for device-initiated wake\n",
			dev->rev);
		}
	}
}

int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt)
{
	int ret = 0;
@@ -1951,6 +2007,16 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt)
			dev->rev);
	}

	if (dev->pme_in_wake_from_d3cold && dev->wake_from_d3cold) {
		/*
		 *  Sending PME after link training for device
		 *  initiated D3cold to D0 transition, based on
		 *  PCIe specification
		 */
		ep_pcie_core_issue_pme_for_wake_from_d3cold();
		dev->wake_from_d3cold = false;
	}

checkbme:
	/* Clear AOSS_CC_RESET_STATUS::PERST_RAW_RESET_STATUS when linking up */
	if (dev->aoss_rst_clear)
@@ -2277,6 +2343,9 @@ static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data)
			dev->rev, dev->d3_counter);
		ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(1));

		dev->pme_en_d3 = readl_relaxed(dev->dm_core +
						PCIE20_CON_STATUS) & PCIE20_MASK_PME_EN;

		if (dev->enumerated)
			ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT);
		else
@@ -3147,6 +3216,13 @@ static int ep_pcie_core_wakeup_host_internal(enum ep_pcie_event event)
	if (!atomic_read(&dev->perst_deast)) {
		/*D3 cold handling*/
		ep_pcie_core_toggle_wake_gpio(true);
		if (dev->pme_in_wake_from_d3cold) {
			/*
			 * Set below flag for device initiated
			 * d3cold wake to send PME message
			 */
			dev->wake_from_d3cold = true;
		}
	} else if (dev->l23_ready) {
		EP_PCIE_ERR(dev,
			"PCIe V%d: request to assert WAKE# when in D3hot\n",
@@ -3423,6 +3499,13 @@ static int ep_pcie_probe(struct platform_device *pdev)
	"PCIe V%d: PME during reboot/panic (in D3hot) is %s needed\n",
	ep_pcie_dev.rev, ep_pcie_dev.avoid_reboot_in_d3hot ? "" : "not");

	ep_pcie_dev.pme_in_wake_from_d3cold =
		of_property_read_bool((&pdev->dev)->of_node,
				"qcom,pme-in-wake-from-d3cold");
	EP_PCIE_DBG(&ep_pcie_dev,
	"PCIe V%d: PME is%s sent during wake from d3cold\n",
	ep_pcie_dev.rev, ep_pcie_dev.pme_in_wake_from_d3cold ? "" : " not");

	ret = of_property_read_u32((&pdev->dev)->of_node,
				"qcom,mhi-soc-reset-offset",
				&ep_pcie_dev.mhi_soc_reset_offset);