[v2,6/6] PCI: dwc: Add common send PME_Turn_Off message method

Message ID 20240201-pme_msg-v2-6-6767052fe6a4@nxp.com
State New
Headers
Series PCI: dwc: Add common pme_turn_off message by using outbound iATU |

Commit Message

Frank Li Feb. 1, 2024, 4:13 p.m. UTC
  Set outbound ATU map memory write to send PCI message. So one MMIO write
can trigger a PCI message, such as PME_Turn_Off.

Add common dw_pcie_send_pme_turn_off_by_atu() function.

Call dw_pcie_send_pme_turn_off_by_atu() to send out PME_Turn_Off message in
general dw_pcie_suspend_noirq() if there are not platform callback
pme_turn_off() exist.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/pci/controller/dwc/pcie-designware-host.c | 51 +++++++++++++++++++++--
 drivers/pci/controller/dwc/pcie-designware.c      |  8 ++++
 drivers/pci/controller/dwc/pcie-designware.h      |  3 ++
 3 files changed, 58 insertions(+), 4 deletions(-)
  

Comments

Bjorn Helgaas Feb. 1, 2024, 6:03 p.m. UTC | #1
On Thu, Feb 01, 2024 at 11:13:30AM -0500, Frank Li wrote:
> Set outbound ATU map memory write to send PCI message. So one MMIO write
> can trigger a PCI message, such as PME_Turn_Off.
> 
> Add common dw_pcie_send_pme_turn_off_by_atu() function.
> ...

> -	if (!pci->pp.ops->pme_turn_off)
> -		return 0;
> +	if (pci->pp.ops->pme_turn_off)
> +		pci->pp.ops->pme_turn_off(&pci->pp);
> +	else
> +		ret = dw_pcie_send_pme_turn_off_by_atu(pci);

I think it's nice if function names match the function pointer names.

E.g., we currently already have:

  .pme_turn_off = ls_pcie_send_turnoff_msg,
  .pme_turn_off = ls1021a_pcie_send_turnoff_msg,
  .pme_turn_off = ls1043a_pcie_send_turnoff_msg,

which is slightly annoying because it's always useful to compare
implementations, but "git grep pme_turn_off" doesn't find the actual
functions, so I wish these were named "ls_pcie_pme_turn_off()", etc.

You don't have to fix those existing layerscape ones now, but I think
the same applies to dw_pcie_send_pme_turn_off_by_atu(): it would be
nice if it were named something like "dw_pcie_pme_turn_off()" so
grep/cscope would find it easily.

Bjorn
  
Frank Li Feb. 1, 2024, 7:40 p.m. UTC | #2
On Thu, Feb 01, 2024 at 12:03:49PM -0600, Bjorn Helgaas wrote:
> On Thu, Feb 01, 2024 at 11:13:30AM -0500, Frank Li wrote:
> > Set outbound ATU map memory write to send PCI message. So one MMIO write
> > can trigger a PCI message, such as PME_Turn_Off.
> > 
> > Add common dw_pcie_send_pme_turn_off_by_atu() function.
> > ...
> 
> > -	if (!pci->pp.ops->pme_turn_off)
> > -		return 0;
> > +	if (pci->pp.ops->pme_turn_off)
> > +		pci->pp.ops->pme_turn_off(&pci->pp);
> > +	else
> > +		ret = dw_pcie_send_pme_turn_off_by_atu(pci);
> 
> I think it's nice if function names match the function pointer names.
> 
> E.g., we currently already have:
> 
>   .pme_turn_off = ls_pcie_send_turnoff_msg,
>   .pme_turn_off = ls1021a_pcie_send_turnoff_msg,
>   .pme_turn_off = ls1043a_pcie_send_turnoff_msg,
> 
> which is slightly annoying because it's always useful to compare
> implementations, but "git grep pme_turn_off" doesn't find the actual
> functions, so I wish these were named "ls_pcie_pme_turn_off()", etc.
> 
> You don't have to fix those existing layerscape ones now, but I think
> the same applies to dw_pcie_send_pme_turn_off_by_atu(): it would be
> nice if it were named something like "dw_pcie_pme_turn_off()" so
> grep/cscope would find it easily.

Okay, these funcition can be removed because this patch. But It takes some
time to test it at these platform. Add need dts update also. There are
still long road.

Frank

> 
> Bjorn
  

Patch

diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 267687ab33cbc..2a281060f3aad 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -728,6 +728,8 @@  static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
 		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
 			 pci->num_ob_windows);
 
+	pci->msg_atu_index = i;
+
 	i = 0;
 	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
 		if (resource_type(entry->res) != IORESOURCE_MEM)
@@ -833,11 +835,49 @@  int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
 }
 EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
 
+static int dw_pcie_send_pme_turn_off_by_atu(struct dw_pcie *pci)
+{
+	struct dw_pcie_ob_atu_cfg atu = { 0 };
+	void __iomem *m;
+	int ret = 0;
+
+	if (pci->num_ob_windows <= pci->msg_atu_index)
+		return -EINVAL;
+
+	atu.code = PCI_MSG_CODE_PME_TURN_OFF;
+	atu.routing = PCI_MSG_TYPE_R_BC;
+	atu.type = PCIE_ATU_TYPE_MSG;
+	atu.size = pci->msg_io_size;
+
+	if (!atu.size) {
+		dev_dbg(pci->dev,
+			"atu memory map windows is zero, please check 'msg' reg in dts\n");
+		return -ENOMEM;
+	}
+
+	atu.cpu_addr = pci->msg_io_base;
+
+	ret = dw_pcie_prog_outbound_atu(pci, &atu);
+	if (ret)
+		return ret;
+
+	m = ioremap(atu.cpu_addr, PAGE_SIZE);
+	if (!m)
+		return -ENOMEM;
+
+	/* A dummy write is converted to a Msg TLP */
+	writel(0, m);
+
+	iounmap(m);
+
+	return ret;
+}
+
 int dw_pcie_suspend_noirq(struct dw_pcie *pci)
 {
 	u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
 	u32 val;
-	int ret;
+	int ret = 0;
 
 	/*
 	 * If L1SS is supported, then do not put the link into L2 as some
@@ -849,10 +889,13 @@  int dw_pcie_suspend_noirq(struct dw_pcie *pci)
 	if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT)
 		return 0;
 
-	if (!pci->pp.ops->pme_turn_off)
-		return 0;
+	if (pci->pp.ops->pme_turn_off)
+		pci->pp.ops->pme_turn_off(&pci->pp);
+	else
+		ret = dw_pcie_send_pme_turn_off_by_atu(pci);
 
-	pci->pp.ops->pme_turn_off(&pci->pp);
+	if (ret)
+		return ret;
 
 	ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE,
 				PCIE_PME_TO_L2_TIMEOUT_US/10,
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index ba909fade9db1..eb24362009bb6 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -155,6 +155,14 @@  int dw_pcie_get_resources(struct dw_pcie *pci)
 		}
 	}
 
+	if (!pci->msg_io_base) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msg");
+		if (res) {
+			pci->msg_io_base = res->start;
+			pci->msg_io_size = res->end - res->start + 1;
+		}
+	}
+
 	/* LLDD is supposed to manually switch the clocks and resets state */
 	if (dw_pcie_cap_is(pci, REQ_RES)) {
 		ret = dw_pcie_get_clocks(pci);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 703b50bc5e0f1..866ab44df9fd1 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -424,6 +424,9 @@  struct dw_pcie {
 	struct reset_control_bulk_data	core_rsts[DW_PCIE_NUM_CORE_RSTS];
 	struct gpio_desc		*pe_rst;
 	bool			suspended;
+	int			msg_atu_index;
+	phys_addr_t		msg_io_base;
+	size_t			msg_io_size;
 };
 
 #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)