[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250814221407.GA351079@bhelgaas>
Date: Thu, 14 Aug 2025 17:14:07 -0500
From: Bjorn Helgaas <helgaas@...nel.org>
To: hans.zhang@...tech.com
Cc: bhelgaas@...gle.com, lpieralisi@...nel.org, kw@...ux.com,
mani@...nel.org, robh@...nel.org, kwilczynski@...nel.org,
krzk+dt@...nel.org, conor+dt@...nel.org, mpillai@...ence.com,
fugang.duan@...tech.com, guoyin.chen@...tech.com,
peter.chen@...tech.com, cix-kernel-upstream@...tech.com,
linux-pci@...r.kernel.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH v7 07/13] PCI: cadence: Add support for High Performance
Arch(HPA) controller
On Wed, Aug 13, 2025 at 12:23:25PM +0800, hans.zhang@...tech.com wrote:
> From: Manikandan K Pillai <mpillai@...ence.com>
>
> Add support for Cadence PCIe RP and EP configuration for High
> Performance Architecture(HPA) controllers
Add space (also in subject).
> Signed-off-by: Manikandan K Pillai <mpillai@...ence.com>
> Co-developed-by: Hans Zhang <hans.zhang@...tech.com>
> Signed-off-by: Hans Zhang <hans.zhang@...tech.com>
> ---
> drivers/pci/controller/cadence/Makefile | 10 +-
> .../controller/cadence/pcie-cadence-ep-hpa.c | 528 ++++++++++++++++
> .../cadence/pcie-cadence-host-hpa.c | 586 ++++++++++++++++++
> .../pci/controller/cadence/pcie-cadence-hpa.c | 207 +++++++
> .../controller/cadence/pcie-cadence-plat.c | 20 +-
> drivers/pci/controller/cadence/pcie-cadence.c | 11 +
> drivers/pci/controller/cadence/pcie-cadence.h | 91 ++-
> 7 files changed, 1438 insertions(+), 15 deletions(-)
> create mode 100644 drivers/pci/controller/cadence/pcie-cadence-ep-hpa.c
> create mode 100644 drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
> create mode 100644 drivers/pci/controller/cadence/pcie-cadence-hpa.c
>
> diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile
> index b104562fb86a..de4ddae7aca4 100644
> --- a/drivers/pci/controller/cadence/Makefile
> +++ b/drivers/pci/controller/cadence/Makefile
> @@ -1,6 +1,10 @@
> # SPDX-License-Identifier: GPL-2.0
> -obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence-common.o pcie-cadence.o
> -obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host-common.o pcie-cadence-host.o
> -obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep-common.o pcie-cadence-ep.o
> +pcie-cadence-mod-y := pcie-cadence-hpa.o pcie-cadence-common.o pcie-cadence.o
> +pcie-cadence-host-mod-y := pcie-cadence-host-common.o pcie-cadence-host.o pcie-cadence-host-hpa.o
> +pcie-cadence-ep-mod-y := pcie-cadence-ep-common.o pcie-cadence-ep.o pcie-cadence-ep-hpa.o
> +
> +obj-$(CONFIG_PCIE_CADENCE) = pcie-cadence-mod.o
> +obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host-mod.o
> +obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep-mod.o
> obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o
> obj-$(CONFIG_PCI_J721E) += pci-j721e.o
> diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-ep-hpa.c
> new file mode 100644
> index 000000000000..36f757820f61
> --- /dev/null
> +++ b/drivers/pci/controller/cadence/pcie-cadence-ep-hpa.c
> @@ -0,0 +1,528 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017 Cadence
> +// Cadence PCIe endpoint controller driver.
> +// Author: Manikandan K Pillai <mpillai@...ence.com>
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/pci-epc.h>
> +#include <linux/platform_device.h>
> +#include <linux/sizes.h>
> +
> +#include "pcie-cadence.h"
> +#include "pcie-cadence-ep-common.h"
> +
> +static int cdns_pcie_hpa_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn,
> + phys_addr_t addr, u64 pci_addr, size_t size)
> +{
> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
> + struct cdns_pcie *pcie = &ep->pcie;
> + u32 r;
> +
> + r = find_first_zero_bit(&ep->ob_region_map, BITS_PER_LONG);
> + if (r >= ep->max_regions - 1) {
> + dev_err(&epc->dev, "no free outbound region\n");
> + return -EINVAL;
> + }
> +
> + fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
> + cdns_pcie_hpa_set_outbound_region(pcie, 0, fn, r, false, addr, pci_addr, size);
> +
> + set_bit(r, &ep->ob_region_map);
> + ep->ob_addr[r] = addr;
> +
> + return 0;
> +}
> +
> +static void cdns_pcie_hpa_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn,
> + phys_addr_t addr)
> +{
> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
> + struct cdns_pcie *pcie = &ep->pcie;
> + u32 r;
> +
> + for (r = 0; r < ep->max_regions - 1; r++)
> + if (ep->ob_addr[r] == addr)
> + break;
> +
> + if (r == ep->max_regions - 1)
> + return;
> +
> + cdns_pcie_hpa_reset_outbound_region(pcie, r);
> +
> + ep->ob_addr[r] = 0;
> + clear_bit(r, &ep->ob_region_map);
> +}
> +
> +static void cdns_pcie_hpa_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx,
> + bool is_asserted)
"is_asserted" sounds like a statement about whether INTx is already
asserted. But IIUC "is_asserted" tells this function *whether* to
assert INTx. If you call it "assert" it will be clearer.
> +{
> + struct cdns_pcie *pcie = &ep->pcie;
> + unsigned long flags;
> + u32 offset;
> + u16 status;
> + u8 msg_code;
> +
> + intx &= 3;
> +
> + /* Set the outbound region if needed. */
One sentence comments like this typically omit the period.
> + if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY ||
> + ep->irq_pci_fn != fn)) {
> + /* First region was reserved for IRQ writes. */
And this.
> + cdns_pcie_hpa_set_outbound_region_for_normal_msg(pcie, 0, fn, 0, ep->irq_phys_addr);
> + ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY;
> + ep->irq_pci_fn = fn;
> + }
> +
> + if (is_asserted) {
> + ep->irq_pending |= BIT(intx);
> + msg_code = PCIE_MSG_CODE_ASSERT_INTA + intx;
> + } else {
> + ep->irq_pending &= ~BIT(intx);
> + msg_code = PCIE_MSG_CODE_DEASSERT_INTA + intx;
> + }
> +
> + spin_lock_irqsave(&ep->lock, flags);
> + status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS);
> + if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) {
> + status ^= PCI_STATUS_INTERRUPT;
> + cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status);
> + }
> + spin_unlock_irqrestore(&ep->lock, flags);
> +
> + offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) |
> + CDNS_PCIE_NORMAL_MSG_CODE(msg_code);
> + writel(0, ep->irq_cpu_addr + offset);
> +}
> +
> +static int cdns_pcie_hpa_ep_send_intx_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
> + u8 intx)
> +{
> + u16 cmd;
> +
> + cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND);
> + if (cmd & PCI_COMMAND_INTX_DISABLE)
> + return -EINVAL;
> +
> + cdns_pcie_hpa_ep_assert_intx(ep, fn, intx, true);
> +
> + /* The mdelay() value was taken from dra7xx_pcie_raise_intx_irq() */
Why is this function named "cdns_pcie_hpa_ep_send_intx_irq()" instead
of something like "cdns_pcie_raise_intx_irq()"?
If using a different name adds some value, fine. If it's pointless
difference, not so great.
> + mdelay(1);
> + cdns_pcie_hpa_ep_assert_intx(ep, fn, intx, false);
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
> + u8 interrupt_num)
I see dw_pcie_ep_raise_msi_irq() and dra7xx_pcie_raise_msi_irq()
elsewhere. If there's a pattern we can copy, please copy it.
> +{
> + struct cdns_pcie *pcie = &ep->pcie;
> + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
> + u16 flags, mme, data, data_mask;
> + u8 msi_count;
> + u64 pci_addr, pci_addr_mask = 0xff;
> +
> + fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
> +
> + /* Check whether the MSI feature has been enabled by the PCI host. */
> + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
> + if (!(flags & PCI_MSI_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + /* Get the number of enabled MSIs */
> + mme = FIELD_GET(PCI_MSI_FLAGS_QSIZE, flags);
> + msi_count = 1 << mme;
> + if (!interrupt_num || interrupt_num > msi_count)
> + return -EINVAL;
> +
> + /* Compute the data value to be written. */
> + data_mask = msi_count - 1;
> + data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
> + data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask);
> +
> + /* Get the PCI address where to write the data into. */
> + pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
> + pci_addr <<= 32;
> + pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
> + pci_addr &= GENMASK_ULL(63, 2);
> +
> + /* Set the outbound region if needed. */
> + if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) ||
> + ep->irq_pci_fn != fn)) {
> + /* First region was reserved for IRQ writes. */
> + cdns_pcie_hpa_set_outbound_region(pcie, 0, fn, 0,
> + false,
> + ep->irq_phys_addr,
> + pci_addr & ~pci_addr_mask,
> + pci_addr_mask + 1);
> + ep->irq_pci_addr = (pci_addr & ~pci_addr_mask);
> + ep->irq_pci_fn = fn;
> + }
> + writel(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask));
> +
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
> + u16 interrupt_num)
> +{
> + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
> + u32 tbl_offset, msg_data, reg;
> + struct cdns_pcie *pcie = &ep->pcie;
> + struct pci_epf_msix_tbl *msix_tbl;
> + struct cdns_pcie_epf *epf;
> + u64 pci_addr_mask = 0xff;
> + u64 msg_addr;
> + u16 flags;
> + u8 bir;
> +
> + epf = &ep->epf[fn];
> + if (vfn > 0)
> + epf = &epf->epf[vfn - 1];
> +
> + fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
> +
> + /* Check whether the MSI-X feature has been enabled by the PCI host. */
> + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS);
> + if (!(flags & PCI_MSIX_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + reg = cap + PCI_MSIX_TABLE;
> + tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg);
> + bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset);
> + tbl_offset &= PCI_MSIX_TABLE_OFFSET;
> +
> + msix_tbl = epf->epf_bar[bir]->addr + tbl_offset;
> + msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
> + msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
> +
> + /* Set the outbound region if needed. */
> + if (ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) ||
> + ep->irq_pci_fn != fn) {
> + /* First region was reserved for IRQ writes. */
> + cdns_pcie_hpa_set_outbound_region(pcie, 0, fn, 0,
> + false,
> + ep->irq_phys_addr,
> + msg_addr & ~pci_addr_mask,
> + pci_addr_mask + 1);
> + ep->irq_pci_addr = (msg_addr & ~pci_addr_mask);
> + ep->irq_pci_fn = fn;
> + }
> + writel(msg_data, ep->irq_cpu_addr + (msg_addr & pci_addr_mask));
> +
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn,
> + unsigned int type, u16 interrupt_num)
> +{
> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
> + struct cdns_pcie *pcie = &ep->pcie;
> + struct device *dev = pcie->dev;
> +
> + switch (type) {
> + case PCI_IRQ_INTX:
> + if (vfn > 0) {
> + dev_err(dev, "Cannot raise INTX interrupts for VF\n");
> + return -EINVAL;
> + }
> + return cdns_pcie_hpa_ep_send_intx_irq(ep, fn, vfn, 0);
> +
> + case PCI_IRQ_MSI:
> + return cdns_pcie_hpa_ep_send_msi_irq(ep, fn, vfn, interrupt_num);
> +
> + case PCI_IRQ_MSIX:
> + return cdns_pcie_hpa_ep_send_msix_irq(ep, fn, vfn, interrupt_num);
> +
> + default:
> + break;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int cdns_pcie_hpa_ep_start(struct pci_epc *epc)
> +{
> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
> + struct cdns_pcie *pcie = &ep->pcie;
> + struct device *dev = pcie->dev;
> + int max_epfs = sizeof(epc->function_num_map) * 8;
> + int ret, epf, last_fn;
> + u32 reg, value;
> +
> + /*
> + * BIT(0) is hardwired to 1, hence function 0 is always enabled
> + * and can't be disabled anyway.
> + */
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG,
> + CDNS_PCIE_HPA_LM_EP_FUNC_CFG, epc->function_num_map);
> +
> + /*
> + * Next function field in ARI_CAP_AND_CTR register for last function
> + * should be 0. Clear Next Function Number field for the last
> + * function used.
> + */
> + last_fn = find_last_bit(&epc->function_num_map, BITS_PER_LONG);
> + reg = CDNS_PCIE_CORE_PF_I_ARI_CAP_AND_CTRL(last_fn);
> + value = cdns_pcie_readl(pcie, reg);
> + value &= ~CDNS_PCIE_ARI_CAP_NFN_MASK;
> + cdns_pcie_writel(pcie, reg, value);
> +
> + if (ep->quirk_disable_flr) {
> + for (epf = 0; epf < max_epfs; epf++) {
> + if (!(epc->function_num_map & BIT(epf)))
> + continue;
> +
> + value = cdns_pcie_ep_fn_readl(pcie, epf,
> + CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET +
> + PCI_EXP_DEVCAP);
> + value &= ~PCI_EXP_DEVCAP_FLR;
> + cdns_pcie_ep_fn_writel(pcie, epf,
> + CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET +
> + PCI_EXP_DEVCAP, value);
> + }
> + }
> +
> + ret = cdns_pcie_start_link(pcie);
> + if (ret) {
> + dev_err(dev, "Failed to start link\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn,
> + struct pci_epf_bar *epf_bar)
> +{
> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
> + struct cdns_pcie_epf *epf = &ep->epf[fn];
> + struct cdns_pcie *pcie = &ep->pcie;
> + dma_addr_t bar_phys = epf_bar->phys_addr;
> + enum pci_barno bar = epf_bar->barno;
> + int flags = epf_bar->flags;
> + u32 addr0, addr1, reg, cfg, b, aperture, ctrl;
> + u64 sz;
> +
> + /* BAR size is 2^(aperture + 7) */
> + sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE);
> +
> + /*
> + * roundup_pow_of_two() returns an unsigned long, which is not suited
> + * for 64bit values.
> + */
> + sz = 1ULL << fls64(sz - 1);
> +
> + /* 128B -> 0, 256B -> 1, 512B -> 2, ... */
> + aperture = ilog2(sz) - 7;
> +
> + if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_IO_32BITS;
> + } else {
> + bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH);
> + bool is_64bits = !!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64);
> +
> + if (is_64bits && (bar & 1))
> + return -EINVAL;
> +
> + if (is_64bits && is_prefetch)
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS;
> + else if (is_prefetch)
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS;
> + else if (is_64bits)
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_MEM_64BITS;
> + else
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_MEM_32BITS;
> + }
> +
> + addr0 = lower_32_bits(bar_phys);
> + addr1 = upper_32_bits(bar_phys);
> +
> + if (vfn == 1)
> + reg = CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG(bar, fn);
> + else
> + reg = CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG(bar, fn);
> + b = (bar < BAR_4) ? bar : bar - BAR_4;
> +
> + if (vfn == 0 || vfn == 1) {
> + cfg = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_CFG_CTRL_REG, reg);
> + cfg &= ~(CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
> + CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
> + cfg |= (CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) |
> + CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, reg, cfg);
> + }
> +
> + fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER_COMMON,
> + CDNS_PCIE_HPA_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), addr0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER_COMMON,
> + CDNS_PCIE_HPA_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), addr1);
> +
> + if (vfn > 0)
> + epf = &epf->epf[vfn - 1];
> + epf->epf_bar[bar] = epf_bar;
> +
> + return 0;
> +}
> +
> +static void cdns_pcie_hpa_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn,
> + struct pci_epf_bar *epf_bar)
> +{
> + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
> + struct cdns_pcie_epf *epf = &ep->epf[fn];
> + struct cdns_pcie *pcie = &ep->pcie;
> + enum pci_barno bar = epf_bar->barno;
> + u32 reg, cfg, b, ctrl;
> +
> + if (vfn == 1)
> + reg = CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG(bar, fn);
> + else
> + reg = CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG(bar, fn);
> + b = (bar < BAR_4) ? bar : bar - BAR_4;
> +
> + if (vfn == 0 || vfn == 1) {
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_DISABLED;
> + cfg = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_CFG_CTRL_REG, reg);
> + cfg &= ~(CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
> + CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
> + cfg |= CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, reg, cfg);
> + }
> +
> + fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER_COMMON,
> + CDNS_PCIE_HPA_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER_COMMON,
> + CDNS_PCIE_HPA_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
> +
> + if (vfn > 0)
> + epf = &epf->epf[vfn - 1];
> + epf->epf_bar[bar] = NULL;
> +}
> +
> +static const struct pci_epc_ops cdns_pcie_hpa_epc_ops = {
> + .write_header = cdns_pcie_ep_write_header,
> + .set_bar = cdns_pcie_hpa_ep_set_bar,
> + .clear_bar = cdns_pcie_hpa_ep_clear_bar,
> + .map_addr = cdns_pcie_hpa_ep_map_addr,
> + .unmap_addr = cdns_pcie_hpa_ep_unmap_addr,
> + .set_msi = cdns_pcie_ep_set_msi,
> + .get_msi = cdns_pcie_ep_get_msi,
> + .set_msix = cdns_pcie_ep_set_msix,
> + .get_msix = cdns_pcie_ep_get_msix,
> + .raise_irq = cdns_pcie_hpa_ep_raise_irq,
> + .map_msi_irq = cdns_pcie_ep_map_msi_irq,
> + .start = cdns_pcie_hpa_ep_start,
> + .get_features = cdns_pcie_ep_get_features,
> +};
> +
> +int cdns_pcie_hpa_ep_setup(struct cdns_pcie_ep *ep)
> +{
> + struct device *dev = ep->pcie.dev;
> + struct platform_device *pdev = to_platform_device(dev);
> + struct device_node *np = dev->of_node;
> + struct cdns_pcie *pcie = &ep->pcie;
> + struct cdns_pcie_epf *epf;
> + struct resource *res;
> + struct pci_epc *epc;
> + int ret;
> + int i;
> +
> + pcie->is_rc = false;
> +
> + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg");
> + if (IS_ERR(pcie->reg_base)) {
> + dev_err(dev, "missing \"reg\"\n");
> + return PTR_ERR(pcie->reg_base);
> + }
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
> + if (!res) {
> + dev_err(dev, "missing \"mem\"\n");
> + return -EINVAL;
> + }
> + pcie->mem_res = res;
> +
> + ep->max_regions = CDNS_PCIE_MAX_OB;
> + of_property_read_u32(np, "cdns,max-outbound-regions", &ep->max_regions);
> +
> + ep->ob_addr = devm_kcalloc(dev,
> + ep->max_regions, sizeof(*ep->ob_addr),
> + GFP_KERNEL);
> + if (!ep->ob_addr)
> + return -ENOMEM;
> +
> + epc = devm_pci_epc_create(dev, &cdns_pcie_hpa_epc_ops);
> + if (IS_ERR(epc)) {
> + dev_err(dev, "failed to create epc device\n");
> + return PTR_ERR(epc);
> + }
> +
> + epc_set_drvdata(epc, ep);
> +
> + if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0)
> + epc->max_functions = 1;
> +
> + ep->epf = devm_kcalloc(dev, epc->max_functions, sizeof(*ep->epf),
> + GFP_KERNEL);
> + if (!ep->epf)
> + return -ENOMEM;
> +
> + epc->max_vfs = devm_kcalloc(dev, epc->max_functions,
> + sizeof(*epc->max_vfs), GFP_KERNEL);
> + if (!epc->max_vfs)
> + return -ENOMEM;
> +
> + ret = of_property_read_u8_array(np, "max-virtual-functions",
> + epc->max_vfs, epc->max_functions);
> + if (ret == 0) {
> + for (i = 0; i < epc->max_functions; i++) {
> + epf = &ep->epf[i];
> + if (epc->max_vfs[i] == 0)
> + continue;
> + epf->epf = devm_kcalloc(dev, epc->max_vfs[i],
> + sizeof(*ep->epf), GFP_KERNEL);
> + if (!epf->epf)
> + return -ENOMEM;
> + }
> + }
> +
> + ret = pci_epc_mem_init(epc, pcie->mem_res->start,
> + resource_size(pcie->mem_res), PAGE_SIZE);
> + if (ret < 0) {
> + dev_err(dev, "failed to initialize the memory space\n");
> + return ret;
> + }
> +
> + ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr,
> + SZ_128K);
> + if (!ep->irq_cpu_addr) {
> + dev_err(dev, "failed to reserve memory space for MSI\n");
> + ret = -ENOMEM;
> + goto free_epc_mem;
> + }
> + ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE;
> + /* Reserve region 0 for IRQs */
> + set_bit(0, &ep->ob_region_map);
> +
> + if (ep->quirk_detect_quiet_flag)
> + cdns_pcie_hpa_detect_quiet_min_delay_set(&ep->pcie);
> +
> + spin_lock_init(&ep->lock);
> +
> + pci_epc_init_notify(epc);
> +
> + return 0;
> +
> + free_epc_mem:
> + pci_epc_mem_exit(epc);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_ep_setup);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Cadence PCIe controller driver");
> +MODULE_AUTHOR("Manikandan K Pillai <mpillai@...ence.com>");
> diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
> new file mode 100644
> index 000000000000..f930b745bc33
> --- /dev/null
> +++ b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
> @@ -0,0 +1,586 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017 Cadence
> +// Cadence PCIe host controller driver.
> +// Author: Manikandan K Pillai <mpillai@...ence.com>
> +
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/list_sort.h>
> +#include <linux/of_address.h>
> +#include <linux/of_pci.h>
> +#include <linux/platform_device.h>
> +
> +#include "pcie-cadence.h"
> +#include "pcie-cadence-host-common.h"
> +
> +static u8 bar_aperture_mask[] = {
> + [RP_BAR0] = 0x1F,
> + [RP_BAR1] = 0xF,
> +};
> +
> +void __iomem *cdns_pci_hpa_map_bus(struct pci_bus *bus, unsigned int devfn,
> + int where)
> +{
> + struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
> + struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge);
> + struct cdns_pcie *pcie = &rc->pcie;
> + unsigned int busn = bus->number;
> + u32 addr0, desc0, desc1, ctrl0;
> + u32 regval;
> +
> + if (pci_is_root_bus(bus)) {
> + /*
> + * Only the root port (devfn == 0) is connected to this bus.
> + * All other PCI devices are behind some bridge hence on another
> + * bus.
> + */
> + if (devfn)
> + return NULL;
> +
> + return pcie->reg_base + (where & 0xfff);
> + }
> +
> + /* Clear AXI link-down status */
> + regval = cdns_pcie_hpa_readl(pcie, REG_BANK_AXI_SLAVE, CDNS_PCIE_HPA_AT_LINKDOWN);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE, CDNS_PCIE_HPA_AT_LINKDOWN,
> + (regval & ~GENMASK(0, 0)));
> +
> + desc0 = 0;
> + desc1 = 0;
> + ctrl0 = 0;
> +
> + /* Update Output registers for AXI region 0. */
> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS(12) |
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) |
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_BUS(busn);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(0), addr0);
> +
> + desc1 = cdns_pcie_hpa_readl(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0));
> + desc1 &= ~CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN_MASK;
> + desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
> + ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
> +
> + if (busn == bridge->busnr + 1)
> + desc0 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0;
> + else
> + desc0 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1;
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC0(0), desc0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0), desc1);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(0), ctrl0);
> +
> + return rc->cfg_base + (where & 0xfff);
> +}
> +
> +int cdns_pcie_hpa_host_wait_for_link(struct cdns_pcie *pcie)
> +{
> + struct device *dev = pcie->dev;
> + int retries;
> +
> + /* Check if the link is up or not */
> + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
> + if (cdns_pcie_link_up(pcie)) {
> + dev_info(dev, "Link up\n");
> + return 0;
> + }
> + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
> + }
> + return -ETIMEDOUT;
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_wait_for_link);
> +
> +int cdns_pcie_hpa_host_start_link(struct cdns_pcie_rc *rc)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + int ret;
> +
> + ret = cdns_pcie_host_wait_for_link(pcie);
> +
> + /*
> + * Retrain link for Gen2 training defect
> + * if quirk flag is set.
Wrap to fill 78 columns like other comments here.
> + */
> + if (!ret && rc->quirk_retrain_flag)
> + ret = cdns_pcie_retrain(pcie);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_start_link);
> +
> +static struct pci_ops cdns_pcie_hpa_host_ops = {
> + .map_bus = cdns_pci_hpa_map_bus,
> + .read = pci_generic_config_read,
> + .write = pci_generic_config_write,
> +};
> +
> +static void cdns_pcie_hpa_host_enable_ptm_response(struct cdns_pcie *pcie)
> +{
> + u32 val;
> +
> + val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_LM_PTM_CTRL);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_LM_PTM_CTRL,
> + val | CDNS_PCIE_HPA_LM_TPM_CTRL_PTMRSEN);
> +}
> +
> +static int cdns_pcie_hpa_host_bar_ib_config(struct cdns_pcie_rc *rc,
> + enum cdns_pcie_rp_bar bar,
> + u64 cpu_addr, u64 size,
> + unsigned long flags)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + u32 addr0, addr1, aperture, value;
> +
> + if (!rc->avail_ib_bar[bar])
> + return -EBUSY;
> +
> + rc->avail_ib_bar[bar] = false;
> +
> + aperture = ilog2(size);
> + addr0 = CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0_NBITS(aperture) |
> + (lower_32_bits(cpu_addr) & GENMASK(31, 8));
> + addr1 = upper_32_bits(cpu_addr);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER,
> + CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0(bar), addr0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER,
> + CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR1(bar), addr1);
> +
> + if (bar == RP_NO_BAR)
> + return 0;
> +
> + value = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_HPA_LM_RC_BAR_CFG);
> + value &= ~(HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) |
> + HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) |
> + HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) |
> + HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) |
> + HPA_LM_RC_BAR_CFG_APERTURE(bar, bar_aperture_mask[bar] + 2));
> + if (size + cpu_addr >= SZ_4G) {
> + if (!(flags & IORESOURCE_PREFETCH))
> + value |= HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar);
> + value |= HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar);
> + } else {
> + if (!(flags & IORESOURCE_PREFETCH))
> + value |= HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar);
> + value |= HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar);
> + }
> +
> + value |= HPA_LM_RC_BAR_CFG_APERTURE(bar, aperture);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_HPA_LM_RC_BAR_CFG, value);
> +
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_host_bar_config(struct cdns_pcie_rc *rc,
> + struct resource_entry *entry)
> +{
> + u64 cpu_addr, pci_addr, size, winsize;
> + struct cdns_pcie *pcie = &rc->pcie;
> + struct device *dev = pcie->dev;
> + enum cdns_pcie_rp_bar bar;
> + unsigned long flags;
> + int ret;
> +
> + cpu_addr = entry->res->start;
> + pci_addr = entry->res->start - entry->offset;
> + flags = entry->res->flags;
> + size = resource_size(entry->res);
> +
> + if (entry->offset) {
> + dev_err(dev, "PCI addr: %llx must be equal to CPU addr: %llx\n",
> + pci_addr, cpu_addr);
> + return -EINVAL;
> + }
> +
> + while (size > 0) {
> + /*
> + * Try to find a minimum BAR whose size is greater than
> + * or equal to the remaining resource_entry size. This will
> + * fail if the size of each of the available BARs is less than
> + * the remaining resource_entry size.
> + * If a minimum BAR is found, IB ATU will be configured and
> + * exited.
Add blank lines between paragraphs. Also below.
> + */
> + bar = cdns_pcie_host_find_min_bar(rc, size);
> + if (bar != RP_BAR_UNDEFINED) {
> + ret = cdns_pcie_hpa_host_bar_ib_config(rc, bar, cpu_addr,
> + size, flags);
> + if (ret)
> + dev_err(dev, "IB BAR: %d config failed\n", bar);
> + return ret;
> + }
> +
> + /*
> + * If the control reaches here, it would mean the remaining
> + * resource_entry size cannot be fitted in a single BAR. So we
> + * find a maximum BAR whose size is less than or equal to the
> + * remaining resource_entry size and split the resource entry
> + * so that part of resource entry is fitted inside the maximum
> + * BAR. The remaining size would be fitted during the next
> + * iteration of the loop.
> + * If a maximum BAR is not found, there is no way we can fit
> + * this resource_entry, so we error out.
> + */
> + bar = cdns_pcie_host_find_max_bar(rc, size);
> + if (bar == RP_BAR_UNDEFINED) {
> + dev_err(dev, "No free BAR to map cpu_addr %llx\n",
> + cpu_addr);
> + return -EINVAL;
> + }
> +
> + winsize = bar_max_size[bar];
> + ret = cdns_pcie_hpa_host_bar_ib_config(rc, bar, cpu_addr, winsize, flags);
> + if (ret) {
> + dev_err(dev, "IB BAR: %d config failed\n", bar);
> + return ret;
> + }
> +
> + size -= winsize;
> + cpu_addr += winsize;
> + }
> +
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_host_map_dma_ranges(struct cdns_pcie_rc *rc)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + struct device *dev = pcie->dev;
> + struct device_node *np = dev->of_node;
> + struct pci_host_bridge *bridge;
> + struct resource_entry *entry;
> + u32 no_bar_nbits = 32;
> + int err;
> +
> + bridge = pci_host_bridge_from_priv(rc);
> + if (!bridge)
> + return -ENOMEM;
> +
> + if (list_empty(&bridge->dma_ranges)) {
> + of_property_read_u32(np, "cdns,no-bar-match-nbits",
> + &no_bar_nbits);
> + err = cdns_pcie_hpa_host_bar_ib_config(rc, RP_NO_BAR, 0x0,
> + (u64)1 << no_bar_nbits, 0);
> + if (err)
> + dev_err(dev, "IB BAR: %d config failed\n", RP_NO_BAR);
> + return err;
> + }
> +
> + list_sort(NULL, &bridge->dma_ranges, cdns_pcie_host_dma_ranges_cmp);
> +
> + resource_list_for_each_entry(entry, &bridge->dma_ranges) {
> + err = cdns_pcie_hpa_host_bar_config(rc, entry);
> + if (err) {
> + dev_err(dev, "Fail to configure IB using dma-ranges\n");
> + return err;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int cdns_pcie_hpa_host_init_root_port(struct cdns_pcie_rc *rc)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + u32 value, ctrl;
> +
> + /*
> + * Set the root complex BAR configuration register:
> + * - disable both BAR0 and BAR1.
> + * - enable Prefetchable Memory Base and Limit registers in type 1
> + * config space (64 bits).
> + * - enable IO Base and Limit registers in type 1 config
> + * space (32 bits).
> + */
> +
> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_DISABLED;
> + value = CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) |
> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) |
> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE |
> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS |
> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_ENABLE |
> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_32BITS;
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG,
> + CDNS_PCIE_HPA_LM_RC_BAR_CFG, value);
> +
> + if (rc->vendor_id != 0xffff)
> + cdns_pcie_hpa_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id);
> +
> + if (rc->device_id != 0xffff)
> + cdns_pcie_hpa_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id);
> +
> + cdns_pcie_hpa_rp_writeb(pcie, PCI_CLASS_REVISION, 0);
> + cdns_pcie_hpa_rp_writeb(pcie, PCI_CLASS_PROG, 0);
> + cdns_pcie_hpa_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
> +
> + return 0;
> +}
> +
> +static void cdns_pcie_hpa_create_region_for_ecam(struct cdns_pcie_rc *rc)
> +{
> + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
> + struct resource *cfg_res = rc->cfg_res;
> + struct cdns_pcie *pcie = &rc->pcie;
> + u32 value, root_port_req_id_reg, pcie_bus_number_reg;
> + u32 ecam_addr_0, region_size_0, request_id_0;
> + int busnr = 0, secbus = 0, subbus = 0;
> + struct resource_entry *entry;
> + resource_size_t size;
> + u32 axi_address_low;
> + int nbits;
> + u64 sz;
> +
> + entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
> + if (entry) {
> + busnr = entry->res->start;
> + secbus = (busnr < 0xff) ? (busnr + 1) : 0xff;
> + subbus = entry->res->end;
> + }
> + size = resource_size(cfg_res);
> + sz = 1ULL << fls64(size - 1);
> + nbits = ilog2(sz);
> + if (nbits < 8)
> + nbits = 8;
> +
> + root_port_req_id_reg = ((busnr & 0xff) << 8);
> + pcie_bus_number_reg = ((subbus & 0xff) << 16) | ((secbus & 0xff) << 8) |
> + (busnr & 0xff);
> + ecam_addr_0 = cfg_res->start;
> + region_size_0 = nbits - 1;
> + request_id_0 = ((busnr & 0xff) << 8);
> +
> +#define CDNS_PCIE_HPA_TAG_MANAGEMENT (0x0)
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_TAG_MANAGEMENT, 0x200000);
> +
> + /* Taking slave err as OKAY */
> +#define CDNS_PCIE_HPA_SLAVE_RESP (0x100)
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE, CDNS_PCIE_HPA_SLAVE_RESP,
> + 0x0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_SLAVE_RESP + 0x4, 0x0);
> +
> + /* Program the register "i_root_port_req_id_reg" with RP's BDF */
> +#define I_ROOT_PORT_REQ_ID_REG (0x141c)
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG, I_ROOT_PORT_REQ_ID_REG,
> + root_port_req_id_reg);
> +
> + /**
> + * Program the register "i_pcie_bus_numbers" with Primary(RP's bus number),
> + * secondary and subordinate bus numbers
> + */
> +#define I_PCIE_BUS_NUMBERS (CDNS_PCIE_HPA_RP_BASE + 0x18)
> + cdns_pcie_hpa_writel(pcie, REG_BANK_RP, I_PCIE_BUS_NUMBERS,
> + pcie_bus_number_reg);
> +
> + /* Program the register "lm_hal_sbsa_ctrl[0]" to enable the sbsa */
> +#define LM_HAL_SBSA_CTRL (0x1170)
> + value = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, LM_HAL_SBSA_CTRL);
> + value |= BIT(0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG, LM_HAL_SBSA_CTRL, value);
> +
> + /* Program region[0] for ECAM */
> + axi_address_low = (ecam_addr_0 & 0xfff00000) | region_size_0;
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(0),
> + axi_address_low);
> +
> + /* rc0-high-axi-address */
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(0), 0x0);
> + /* Type-1 CFG */
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC0(0), 0x05000000);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0),
> + (request_id_0 << 16));
> +
> + /* All AXI bits pass through PCIe */
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(0), 0x1b);
> + /* PCIe address-high */
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(0), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(0), 0x06000000);
> +}
> +
> +static void cdns_pcie_hpa_create_region_for_cfg(struct cdns_pcie_rc *rc)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
> + struct resource *cfg_res = rc->cfg_res;
> + struct resource_entry *entry;
> + u64 cpu_addr = cfg_res->start;
> + u32 addr0, addr1, desc1;
> + int busnr = 0;
> +
> + entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
> + if (entry)
> + busnr = entry->res->start;
> +
> + /*
> + * Reserve region 0 for PCI configure space accesses:
> + * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated dynamically by
> + * cdns_pci_map_bus(), other region registers are set here once for all.
> + */
> + addr1 = 0;
> + desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(0), addr1);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0), desc1);
> +
> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(12) |
> + (lower_32_bits(cpu_addr) & GENMASK(31, 8));
> + addr1 = upper_32_bits(cpu_addr);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(0), addr0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(0), addr1);
> +}
> +
> +static int cdns_pcie_hpa_host_init_address_translation(struct cdns_pcie_rc *rc)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
> + struct resource_entry *entry;
> + int r = 0, busnr = 0;
> +
> + if (rc->ecam_support_flag)
> + cdns_pcie_hpa_create_region_for_ecam(rc);
> + else
> + cdns_pcie_hpa_create_region_for_cfg(rc);
> +
> + entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
> + if (entry)
> + busnr = entry->res->start;
> +
> + r++;
> + if (pcie->msg_res)
> + cdns_pcie_hpa_set_outbound_region_for_normal_msg(pcie, busnr, 0, r,
> + pcie->msg_res->start);
> +
> + r++;
> + resource_list_for_each_entry(entry, &bridge->windows) {
> + struct resource *res = entry->res;
> + u64 pci_addr = res->start - entry->offset;
> +
> + if (resource_type(res) == IORESOURCE_IO)
> + cdns_pcie_hpa_set_outbound_region(pcie, busnr, 0, r,
> + true,
> + pci_pio_to_address(res->start),
> + pci_addr,
> + resource_size(res));
> + else
> + cdns_pcie_hpa_set_outbound_region(pcie, busnr, 0, r,
> + false,
> + res->start,
> + pci_addr,
> + resource_size(res));
> +
> + r++;
> + }
> +
> + if (rc->no_inbound_flag)
> + return 0;
> + else
> + return cdns_pcie_hpa_host_map_dma_ranges(rc);
> +}
> +
> +int cdns_pcie_hpa_host_init(struct cdns_pcie_rc *rc)
> +{
> + int err;
> +
> + err = cdns_pcie_hpa_host_init_root_port(rc);
> + if (err)
> + return err;
> +
> + return cdns_pcie_hpa_host_init_address_translation(rc);
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_init);
> +
> +int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc)
> +{
> + struct cdns_pcie *pcie = &rc->pcie;
> + struct device *dev = rc->pcie.dev;
> + int ret;
> +
> + if (rc->quirk_detect_quiet_flag)
> + cdns_pcie_hpa_detect_quiet_min_delay_set(&rc->pcie);
> +
> + cdns_pcie_hpa_host_enable_ptm_response(pcie);
> +
> + ret = cdns_pcie_start_link(pcie);
> + if (ret) {
> + dev_err(dev, "Failed to start link\n");
> + return ret;
> + }
> +
> + ret = cdns_pcie_hpa_host_start_link(rc);
> + if (ret)
> + dev_dbg(dev, "PCIe link never came up\n");
I see this whole function is cloned from cdns_pcie_host_link_setup(),
but I can't remember why we need two _start_link() functions here.
Seems confusing.
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_link_setup);
> +
> +int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
> +{
> + struct device *dev = rc->pcie.dev;
> + struct platform_device *pdev = to_platform_device(dev);
> + struct pci_host_bridge *bridge;
> + enum cdns_pcie_rp_bar bar;
> + struct cdns_pcie *pcie;
> + struct resource *res;
> + int ret;
> +
> + bridge = pci_host_bridge_from_priv(rc);
> + if (!bridge)
> + return -ENOMEM;
> +
> + pcie = &rc->pcie;
> + pcie->is_rc = true;
> +
> + if (!pcie->reg_base) {
> + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg");
> + if (IS_ERR(pcie->reg_base)) {
> + dev_err(dev, "missing \"reg\"\n");
> + return PTR_ERR(pcie->reg_base);
> + }
> + }
> +
> + /* ECAM config space is remapped at glue layer */
> + if (!rc->cfg_base) {
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
> + rc->cfg_base = devm_pci_remap_cfg_resource(dev, res);
> + if (IS_ERR(rc->cfg_base))
> + return PTR_ERR(rc->cfg_base);
> + rc->cfg_res = res;
> + }
> +
> + ret = cdns_pcie_hpa_host_link_setup(rc);
> + if (ret)
> + return ret;
> +
> + for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++)
> + rc->avail_ib_bar[bar] = true;
> +
> + ret = cdns_pcie_hpa_host_init(rc);
> + if (ret)
> + return ret;
> +
> + if (!bridge->ops)
> + bridge->ops = &cdns_pcie_hpa_host_ops;
> +
> + return pci_host_probe(bridge);
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_setup);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Cadence PCIe controller driver");
> +MODULE_AUTHOR("Manikandan K Pillai <mpillai@...ence.com>");
> diff --git a/drivers/pci/controller/cadence/pcie-cadence-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-hpa.c
> new file mode 100644
> index 000000000000..1152d2dcae77
> --- /dev/null
> +++ b/drivers/pci/controller/cadence/pcie-cadence-hpa.c
> @@ -0,0 +1,207 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017 Cadence
> +// Cadence PCIe controller driver
> +// Author: Manikandan K Pillai <mpillai@...ence.com>
> +
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +
> +#include "pcie-cadence.h"
> +
> +bool cdns_pcie_hpa_link_up(struct cdns_pcie *pcie)
> +{
> + u32 pl_reg_val;
> +
> + pl_reg_val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_PHY_DBG_STS_REG0);
> + if (pl_reg_val & GENMASK(0, 0))
> + return true;
> + return false;
> +}
> +
> +int cdns_pcie_hpa_start_link(struct cdns_pcie *pcie)
> +{
> + u32 pl_reg_val;
> +
> + pl_reg_val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_PHY_LAYER_CFG0);
> + pl_reg_val |= CDNS_PCIE_HPA_LINK_TRNG_EN_MASK;
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_PHY_LAYER_CFG0, pl_reg_val);
> + return 0;
> +}
> +
> +void cdns_pcie_hpa_stop_link(struct cdns_pcie *pcie)
> +{
> + u32 pl_reg_val;
> +
> + pl_reg_val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_PHY_LAYER_CFG0);
> + pl_reg_val &= ~CDNS_PCIE_HPA_LINK_TRNG_EN_MASK;
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_PHY_LAYER_CFG0, pl_reg_val);
> +}
> +
> +void cdns_pcie_hpa_detect_quiet_min_delay_set(struct cdns_pcie *pcie)
> +{
> + u32 delay = 0x3;
> + u32 ltssm_control_cap;
> +
> + /*
> + * Set the LTSSM Detect Quiet state min. delay to 2ms.
> + */
> + ltssm_control_cap = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG,
> + CDNS_PCIE_HPA_PHY_LAYER_CFG0);
> + ltssm_control_cap = ((ltssm_control_cap &
> + ~CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY_MASK) |
> + CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY(delay));
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG,
> + CDNS_PCIE_HPA_PHY_LAYER_CFG0, ltssm_control_cap);
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_detect_quiet_min_delay_set);
> +
> +void cdns_pcie_hpa_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
> + u32 r, bool is_io,
> + u64 cpu_addr, u64 pci_addr, size_t size)
> +{
> + /*
> + * roundup_pow_of_two() returns an unsigned long, which is not suited
> + * for 64bit values.
> + */
> + u64 sz = 1ULL << fls64(size - 1);
> + int nbits = ilog2(sz);
> + u32 addr0, addr1, desc0, desc1, ctrl0;
> +
> + if (nbits < 8)
> + nbits = 8;
> +
> + /* Set the PCI address */
> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) |
> + (lower_32_bits(pci_addr) & GENMASK(31, 8));
> + addr1 = upper_32_bits(pci_addr);
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(r), addr0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(r), addr1);
> +
> + /* Set the PCIe header descriptor */
> + if (is_io)
> + desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_IO;
> + else
> + desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MEM;
> + desc1 = 0;
> + ctrl0 = 0;
> +
> + /*
> + * Whether Bit [26] is set or not inside DESC0 register of the outbound
> + * PCIe descriptor, the PCI function number must be set into
> + * Bits [31:24] of DESC1 anyway.
> + *
> + * In Root Complex mode, the function number is always 0 but in Endpoint
> + * mode, the PCIe controller may support more than one function. This
> + * function number needs to be set properly into the outbound PCIe
> + * descriptor.
> + *
> + * Besides, setting Bit [26] is mandatory when in Root Complex mode:
> + * then the driver must provide the bus, resp. device, number in
> + * Bits [31:24] of DESC1, resp. Bits[23:16] of DESC0. Like the function
> + * number, the device number is always 0 in Root Complex mode.
> + *
> + * However when in Endpoint mode, we can clear Bit [26] of DESC0, hence
> + * the PCIe controller will use the captured values for the bus and
> + * device numbers.
> + */
> + if (pcie->is_rc) {
> + /* The device and function numbers are always 0. */
> + desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr) |
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
> + ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
> + } else {
> + /*
> + * Use captured values for bus and device numbers but still
> + * need to set the function number.
> + */
> + desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(fn);
> + }
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r), desc0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r), desc1);
> +
> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) |
> + (lower_32_bits(cpu_addr) & GENMASK(31, 8));
> + addr1 = upper_32_bits(cpu_addr);
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(r), addr0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(r), addr1);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(r), ctrl0);
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_set_outbound_region);
> +
> +void cdns_pcie_hpa_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie,
> + u8 busnr, u8 fn,
> + u32 r, u64 cpu_addr)
> +{
> + u32 addr0, addr1, desc0, desc1, ctrl0;
> +
> + desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG;
> + desc1 = 0;
> + ctrl0 = 0;
> +
> + /*
> + * See cdns_pcie_set_outbound_region() comments above.
> + */
> + if (pcie->is_rc) {
> + desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr) |
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
> + ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
> + } else {
> + desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(fn);
> + }
> +
> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(17) |
> + (lower_32_bits(cpu_addr) & GENMASK(31, 8));
> + addr1 = upper_32_bits(cpu_addr);
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(r), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(r), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r), desc0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r), desc1);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(r), addr0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(r), addr1);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(r), ctrl0);
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_set_outbound_region_for_normal_msg);
> +
> +void cdns_pcie_hpa_reset_outbound_region(struct cdns_pcie *pcie, u32 r)
> +{
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(r), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(r), 0);
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r), 0);
> +
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(r), 0);
> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(r), 0);
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_hpa_reset_outbound_region);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Cadence PCIe controller driver");
> +MODULE_AUTHOR("Manikandan K Pillai <mpillai@...ence.com>");
> diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c
> index ced030cc0fda..6ce18a0cf564 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence-plat.c
> +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c
> @@ -12,8 +12,7 @@
> #include <linux/pm_runtime.h>
> #include "pcie-cadence.h"
>
> -#define CDNS_PLAT_CPU_TO_BUS_ADDR 0x0FFFFFFF
> -
> +void cdns_pcie_disable_phy(struct cdns_pcie *pcie);
> /**
> * struct cdns_plat_pcie - private data for this PCIe platform driver
> * @pcie: Cadence PCIe controller
> @@ -24,13 +23,8 @@ struct cdns_plat_pcie {
>
> static const struct of_device_id cdns_plat_pcie_of_match[];
>
> -static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr)
> -{
> - return cpu_addr & CDNS_PLAT_CPU_TO_BUS_ADDR;
> -}
> -
> static const struct cdns_pcie_ops cdns_plat_ops = {
> - .cpu_addr_fixup = cdns_plat_cpu_addr_fixup,
This stuff doesn't look HPA related. This looks cpu_addr_fixup stuff
that should be in a separate patch.
Most of the stuff below isn't HPA related either. I'm confused.
> + .link_up = cdns_pcie_linkup,
> };
>
> static int cdns_plat_pcie_probe(struct platform_device *pdev)
> @@ -68,6 +62,11 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
> rc = pci_host_bridge_priv(bridge);
> rc->pcie.dev = dev;
> rc->pcie.ops = &cdns_plat_ops;
> + rc->pcie.is_rc = data->is_rc;
> +
> + /* Store the register bank offsets pointer */
> + rc->pcie.cdns_pcie_reg_offsets = data;
> +
> cdns_plat_pcie->pcie = &rc->pcie;
>
> ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
> @@ -95,6 +94,11 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
>
> ep->pcie.dev = dev;
> ep->pcie.ops = &cdns_plat_ops;
> + ep->pcie.is_rc = data->is_rc;
> +
> + /* Store the register bank offset pointer */
> + ep->pcie.cdns_pcie_reg_offsets = data;
> +
> cdns_plat_pcie->pcie = &ep->pcie;
>
> ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
> diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c
> index 51c9bc4eb174..aaf921ea70e0 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence.c
> +++ b/drivers/pci/controller/cadence/pcie-cadence.c
> @@ -9,6 +9,17 @@
>
> #include "pcie-cadence.h"
>
> +bool cdns_pcie_linkup(struct cdns_pcie *pcie)
> +{
> + u32 pl_reg_val;
> +
> + pl_reg_val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE);
> + if (pl_reg_val & GENMASK(0, 0))
> + return true;
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(cdns_pcie_linkup);
> +
> void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie)
> {
> u32 delay = 0x3;
> diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
> index 8048bef215d0..3e0fd6bb21b1 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence.h
> +++ b/drivers/pci/controller/cadence/pcie-cadence.h
> @@ -7,10 +7,12 @@
> #define _PCIE_CADENCE_H
>
> #include <linux/kernel.h>
> +#include <linux/module.h>
> #include <linux/pci.h>
> #include <linux/pci-epf.h>
> #include <linux/phy/phy.h>
> #include <linux/bitfield.h>
> +
Spurious whitespace change. Should have been done when the #includes
below were added.
> #include "pcie-cadence-lga-regs.h"
> #include "pcie-cadence-hpa-regs.h"
>
> @@ -29,6 +31,26 @@ struct cdns_pcie_rp_ib_bar {
> struct cdns_pcie;
> struct cdns_pcie_rc;
>
> +enum cdns_pcie_msg_routing {
> + /* Route to Root Complex */
> + MSG_ROUTING_TO_RC,
> +
> + /* Use Address Routing */
> + MSG_ROUTING_BY_ADDR,
> +
> + /* Use ID Routing */
> + MSG_ROUTING_BY_ID,
> +
> + /* Route as Broadcast Message from Root Complex */
> + MSG_ROUTING_BCAST,
> +
> + /* Local message; terminate at receiver (INTx messages) */
> + MSG_ROUTING_LOCAL,
> +
> + /* Gather & route to Root Complex (PME_TO_Ack message) */
> + MSG_ROUTING_GATHER,
> +};
Looks like duplicates of PCIE_MSG_TYPE_R_RC, etc from
drivers/pci/pci.h
> enum cdns_pcie_reg_bank {
> REG_BANK_RP,
> REG_BANK_IP_REG,
> @@ -43,9 +65,9 @@ enum cdns_pcie_reg_bank {
> };
>
> struct cdns_pcie_ops {
> - int (*start_link)(struct cdns_pcie *pcie);
> - void (*stop_link)(struct cdns_pcie *pcie);
> - bool (*link_up)(struct cdns_pcie *pcie);
> + int (*start_link)(struct cdns_pcie *pcie);
> + void (*stop_link)(struct cdns_pcie *pcie);
> + bool (*link_up)(struct cdns_pcie *pcie);
> u64 (*cpu_addr_fixup)(struct cdns_pcie *pcie, u64 cpu_addr);
> };
>
> @@ -77,6 +99,7 @@ struct cdns_plat_pcie_of_data {
> * struct cdns_pcie - private data for Cadence PCIe controller drivers
> * @reg_base: IO mapped register base
> * @mem_res: start/end offsets in the physical system memory to map PCI accesses
> + * @msg_res: Region for send message to map PCI accesses
> * @dev: PCIe controller
> * @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint.
> * @phy_count: number of supported PHY devices
> @@ -89,6 +112,7 @@ struct cdns_plat_pcie_of_data {
> struct cdns_pcie {
> void __iomem *reg_base;
> struct resource *mem_res;
> + struct resource *msg_res;
> struct device *dev;
> bool is_rc;
> int phy_count;
> @@ -111,6 +135,7 @@ struct cdns_pcie {
> * available
> * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2
> * @quirk_detect_quiet_flag: LTSSM Detect Quiet min delay set as quirk
> + * @ecam_support_flag: Whether the ECAM flag is supported
> */
> struct cdns_pcie_rc {
> struct cdns_pcie pcie;
> @@ -121,6 +146,8 @@ struct cdns_pcie_rc {
> bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
> unsigned int quirk_retrain_flag:1;
> unsigned int quirk_detect_quiet_flag:1;
> + unsigned int ecam_support_flag:1;
> + unsigned int no_inbound_flag:1;
> };
>
> /**
> @@ -304,6 +331,29 @@ static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg)
> return cdns_pcie_read_sz(addr, 0x2);
> }
>
> +static inline void cdns_pcie_hpa_rp_writeb(struct cdns_pcie *pcie,
> + u32 reg, u8 value)
> +{
> + void __iomem *addr = pcie->reg_base + CDNS_PCIE_HPA_RP_BASE + reg;
> +
> + cdns_pcie_write_sz(addr, 0x1, value);
> +}
> +
> +static inline void cdns_pcie_hpa_rp_writew(struct cdns_pcie *pcie,
> + u32 reg, u16 value)
> +{
> + void __iomem *addr = pcie->reg_base + CDNS_PCIE_HPA_RP_BASE + reg;
> +
> + cdns_pcie_write_sz(addr, 0x2, value);
> +}
> +
> +static inline u16 cdns_pcie_hpa_rp_readw(struct cdns_pcie *pcie, u32 reg)
> +{
> + void __iomem *addr = pcie->reg_base + CDNS_PCIE_HPA_RP_BASE + reg;
> +
> + return cdns_pcie_read_sz(addr, 0x2);
> +}
> +
> /* Endpoint Function register access */
> static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn,
> u32 reg, u8 value)
> @@ -368,6 +418,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc);
> void cdns_pcie_host_disable(struct cdns_pcie_rc *rc);
> void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
> int where);
> +int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc);
> #else
> static inline int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
> {
> @@ -384,6 +435,11 @@ static inline int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
> return 0;
> }
>
> +static inline int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
> +{
> + return 0;
> +}
> +
> static inline void cdns_pcie_host_disable(struct cdns_pcie_rc *rc)
> {
> }
> @@ -398,17 +454,25 @@ static inline void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int d
> #if IS_ENABLED(CONFIG_PCIE_CADENCE_EP)
> int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep);
> void cdns_pcie_ep_disable(struct cdns_pcie_ep *ep);
> +int cdns_pcie_hpa_ep_setup(struct cdns_pcie_ep *ep);
> #else
> static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
> {
> return 0;
> }
>
> +static inline int cdns_pcie_hpa_ep_setup(struct cdns_pcie_ep *ep)
> +{
> + return 0;
> +}
> +
> static inline void cdns_pcie_ep_disable(struct cdns_pcie_ep *ep)
> {
> }
> #endif
> -
> +bool cdns_pcie_linkup(struct cdns_pcie *pcie);
> +int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie);
> +int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc);
> void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie);
> void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
> u32 r, bool is_io,
> @@ -421,6 +485,25 @@ void cdns_pcie_disable_phy(struct cdns_pcie *pcie);
> int cdns_pcie_enable_phy(struct cdns_pcie *pcie);
> int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie);
>
> +void cdns_pcie_hpa_detect_quiet_min_delay_set(struct cdns_pcie *pcie);
> +void cdns_pcie_hpa_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
> + u32 r, bool is_io,
> + u64 cpu_addr, u64 pci_addr, size_t size);
> +void cdns_pcie_hpa_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie,
> + u8 busnr, u8 fn,
> + u32 r, u64 cpu_addr);
> +void cdns_pcie_hpa_reset_outbound_region(struct cdns_pcie *pcie, u32 r);
> +int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc);
> +int cdns_pcie_hpa_host_init(struct cdns_pcie_rc *rc);
> +void __iomem *cdns_pci_hpa_map_bus(struct pci_bus *bus, unsigned int devfn,
> + int where);
> +int cdns_pcie_hpa_host_wait_for_link(struct cdns_pcie *pcie);
> +int cdns_pcie_hpa_host_start_link(struct cdns_pcie_rc *rc);
> +
> +int cdns_pcie_hpa_start_link(struct cdns_pcie *pcie);
> +void cdns_pcie_hpa_stop_link(struct cdns_pcie *pcie);
> +bool cdns_pcie_hpa_link_up(struct cdns_pcie *pcie);
> +
> extern const struct dev_pm_ops cdns_pcie_pm_ops;
>
> #endif /* _PCIE_CADENCE_H */
> --
> 2.49.0
>
Powered by blists - more mailing lists