Subject: [PATCH v5 0/5] PCI: EPC: Add support to wake up host from D3 states

Here we propose this patch series to add support in PCI endpoint
driver to wake up host from D3 states.

As endpoint cannot send any data/MSI when the D-state is in
D3cold or D3hot. Endpoint needs to bring the device back to D0
to send any kind of data.

For this endpoint needs to send inband PME the device is in D3 state or
toggle wake when the device is D3 cold and vaux is not supplied.

Based on the D-state the EPF driver decides to wake host either by
toggling wake or by sending PME.

When the MHI state is in M3 MHI driver will wakeup the host using the
wakeup op.

This change is dependent on this series PCI: endpoint: add D-state change notifier
support

https://lore.kernel.org/linux-pci/[email protected]/

---
Changes from v4:
- removed the enum to select to send PME or toggle wake and use bool variable in
the api itself as suggested by mani.
Changes from v3:
- changed the bool return type to int for waking the host in mhi ep driver
as suggested by dan and bjorn.
- Changed commit logs as suggested by bjorn.
Changes from v2:
- Addressed review comments made by mani.
Changes from v1:
- Moved from RFC patch to regular patch
- Inclueded EPF patch and added a new op patch to notify D-state change.
---

Krishna chaitanya chundru (5):
PCI: endpoint: Add wakeup host API to EPC core
PCI: dwc: Add wakeup host op to pci_epc_ops
PCI: qcom-ep: Add wake up host op to dw_pcie_ep_ops
PCI: epf-mhi: Add wakeup host op
bus: mhi: ep: wake up host if the MHI state is in M3

Documentation/PCI/endpoint/pci-endpoint.rst | 6 +++++
drivers/bus/mhi/ep/main.c | 28 +++++++++++++++++++++++
drivers/pci/controller/dwc/pcie-designware-ep.c | 12 ++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 2 ++
drivers/pci/controller/dwc/pcie-qcom-ep.c | 26 +++++++++++++++++++++
drivers/pci/endpoint/functions/pci-epf-mhi.c | 12 ++++++++++
drivers/pci/endpoint/pci-epc-core.c | 30 +++++++++++++++++++++++++
include/linux/mhi_ep.h | 1 +
include/linux/pci-epc.h | 5 +++++
9 files changed, 122 insertions(+)

--
2.7.4



Subject: [PATCH v5 2/5] PCI: dwc: Add wakeup host op to pci_epc_ops

Add wakeup host op to wake up host from D3cold or D3hot.

Signed-off-by: Krishna chaitanya chundru <[email protected]>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 12 ++++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 2 ++
2 files changed, 14 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index f9182f8..e2733a4 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -463,6 +463,17 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
return ep->ops->get_features(ep);
}

+static bool dw_pcie_ep_wakeup_host(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ bool send_pme)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+
+ if (!ep->ops->wakeup_host)
+ return false;
+
+ return ep->ops->wakeup_host(ep, func_no, send_pme);
+}
+
static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
@@ -477,6 +488,7 @@ static const struct pci_epc_ops epc_ops = {
.start = dw_pcie_ep_start,
.stop = dw_pcie_ep_stop,
.get_features = dw_pcie_ep_get_features,
+ .wakeup_host = dw_pcie_ep_wakeup_host,
};

int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 6156606..fc47e74 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -330,6 +330,8 @@ struct dw_pcie_ep_ops {
* driver.
*/
unsigned int (*func_conf_select)(struct dw_pcie_ep *ep, u8 func_no);
+
+ bool (*wakeup_host)(struct dw_pcie_ep *ep, u8 func_no, bool send_pme);
};

struct dw_pcie_ep_func {
--
2.7.4


Subject: [PATCH v5 3/5] PCI: qcom-ep: Add wake up host op to dw_pcie_ep_ops

Add wakeup host op to dw_pcie_ep_ops to wake up host.
If the wakeup type is PME, then trigger inband PME by writing to the PARF
PARF_PM_CTRL register, otherwise toggle #WAKE.

Signed-off-by: Krishna chaitanya chundru <[email protected]>
---
drivers/pci/controller/dwc/pcie-qcom-ep.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 0c69a61..e78a74a1 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -89,6 +89,7 @@
/* PARF_PM_CTRL register fields */
#define PARF_PM_CTRL_REQ_EXIT_L1 BIT(1)
#define PARF_PM_CTRL_READY_ENTR_L23 BIT(2)
+#define PARF_PM_CTRL_XMT_PME BIT(4)
#define PARF_PM_CTRL_REQ_NOT_ENTR_L1 BIT(5)

/* PARF_MHI_CLOCK_RESET_CTRL fields */
@@ -730,10 +731,35 @@ static void qcom_pcie_ep_init(struct dw_pcie_ep *ep)
dw_pcie_ep_reset_bar(pci, bar);
}

+static bool qcom_pcie_ep_wakeup_host(struct dw_pcie_ep *ep, u8 func_no, bool send_pme)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
+ struct device *dev = pci->dev;
+ u32 val;
+
+ if (send_pme) {
+ dev_dbg(dev, "Waking up the host using PME\n");
+ val = readl_relaxed(pcie_ep->parf + PARF_PM_CTRL);
+ writel_relaxed(val | PARF_PM_CTRL_XMT_PME, pcie_ep->parf + PARF_PM_CTRL);
+ writel_relaxed(val, pcie_ep->parf + PARF_PM_CTRL);
+
+ } else {
+ dev_dbg(dev, "Waking up the host by toggling WAKE#\n");
+ gpiod_set_value_cansleep(pcie_ep->wake, 1);
+ usleep_range(WAKE_DELAY_US, WAKE_DELAY_US + 500);
+ gpiod_set_value_cansleep(pcie_ep->wake, 0);
+
+ }
+
+ return true;
+}
+
static const struct dw_pcie_ep_ops pci_ep_ops = {
.ep_init = qcom_pcie_ep_init,
.raise_irq = qcom_pcie_ep_raise_irq,
.get_features = qcom_pcie_epc_get_features,
+ .wakeup_host = qcom_pcie_ep_wakeup_host,
};

static int qcom_pcie_ep_probe(struct platform_device *pdev)
--
2.7.4


Subject: [PATCH v5 5/5] bus: mhi: ep: wake up host if the MHI state is in M3

If the MHI state is in M3 then most probably the host kept the
device in D3 hot or D3 cold, due to that endpoint transactions will not
be read by the host, endpoint needs to wakes up the host to bring the
host to D0 which eventually bring back the MHI state to M0.

Signed-off-by: Krishna chaitanya chundru <[email protected]>
---
drivers/bus/mhi/ep/main.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)

diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 6008818..9035e02 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -25,6 +25,26 @@ static DEFINE_IDA(mhi_ep_cntrl_ida);
static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
static int mhi_ep_destroy_device(struct device *dev, void *data);

+static int mhi_ep_wake_host(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ enum mhi_state state;
+ bool mhi_reset;
+ u32 count = 0;
+
+ mhi_cntrl->wakeup_host(mhi_cntrl);
+
+ /* Wait for Host to set the M0 state */
+ while (count++ < M0_WAIT_COUNT) {
+ msleep(M0_WAIT_DELAY_MS);
+
+ mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset);
+ if (state == MHI_STATE_M0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
struct mhi_ring_element *el, bool bei)
{
@@ -466,6 +486,14 @@ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb)

mutex_lock(&mhi_chan->lock);

+ if (mhi_cntrl->mhi_state == MHI_STATE_M3) {
+ ret = mhi_ep_wake_host(mhi_cntrl);
+ if (ret) {
+ dev_err(dev, "Failed to wakeup host\n");
+ goto err_exit;
+ }
+ }
+
do {
/* Don't process the transfer ring if the channel is not in RUNNING state */
if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
--
2.7.4


Subject: [PATCH v5 4/5] PCI: epf-mhi: Add wakeup host op

Add wakeup host op for MHI EPF.
If the D-state is in D3cold toggle wake signal, otherwise send PME.

Signed-off-by: Krishna chaitanya chundru <[email protected]>
---
drivers/pci/endpoint/functions/pci-epf-mhi.c | 12 ++++++++++++
include/linux/mhi_ep.h | 1 +
2 files changed, 13 insertions(+)

diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
index ee91bfc..840d5d3 100644
--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
+++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
@@ -237,6 +237,17 @@ static int pci_epf_mhi_write_to_host(struct mhi_ep_cntrl *mhi_cntrl,
return 0;
}

+static int pci_epf_mhi_wakeup_host(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl);
+ struct pci_epf *epf = epf_mhi->epf;
+ struct pci_epc *epc = epf->epc;
+
+ return pci_epc_wakeup_host(epc, epf->func_no, epf->vfunc_no,
+ (mhi_cntrl->dstate == PCI_D3cold) ? false : true);
+
+}
+
static int pci_epf_mhi_core_init(struct pci_epf *epf)
{
struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf);
@@ -293,6 +304,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf)
mhi_cntrl->unmap_free = pci_epf_mhi_unmap_free;
mhi_cntrl->read_from_host = pci_epf_mhi_read_from_host;
mhi_cntrl->write_to_host = pci_epf_mhi_write_to_host;
+ mhi_cntrl->wakeup_host = pci_epf_mhi_wakeup_host;

/* Register the MHI EP controller */
ret = mhi_ep_register_controller(mhi_cntrl, info->config);
diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
index c3a0685..e353c429 100644
--- a/include/linux/mhi_ep.h
+++ b/include/linux/mhi_ep.h
@@ -137,6 +137,7 @@ struct mhi_ep_cntrl {
void __iomem *virt, size_t size);
int (*read_from_host)(struct mhi_ep_cntrl *mhi_cntrl, u64 from, void *to, size_t size);
int (*write_to_host)(struct mhi_ep_cntrl *mhi_cntrl, void *from, u64 to, size_t size);
+ int (*wakeup_host)(struct mhi_ep_cntrl *mhi_cntrl);

enum mhi_state mhi_state;

--
2.7.4