162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (c) 2017 Cadence
362306a36Sopenharmony_ci// Cadence PCIe endpoint controller driver.
462306a36Sopenharmony_ci// Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/of.h>
962306a36Sopenharmony_ci#include <linux/pci-epc.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/sizes.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "pcie-cadence.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define CDNS_PCIE_EP_MIN_APERTURE		128	/* 128 bytes */
1662306a36Sopenharmony_ci#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE		0x1
1762306a36Sopenharmony_ci#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY	0x3
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic u8 cdns_pcie_get_fn_from_vfn(struct cdns_pcie *pcie, u8 fn, u8 vfn)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
2262306a36Sopenharmony_ci	u32 first_vf_offset, stride;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (vfn == 0)
2562306a36Sopenharmony_ci		return fn;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_SRIOV_VF_OFFSET);
2862306a36Sopenharmony_ci	stride = cdns_pcie_ep_fn_readw(pcie, fn, cap +  PCI_SRIOV_VF_STRIDE);
2962306a36Sopenharmony_ci	fn = fn + first_vf_offset + ((vfn - 1) * stride);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	return fn;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn,
3562306a36Sopenharmony_ci				     struct pci_epf_header *hdr)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
3862306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
3962306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
4062306a36Sopenharmony_ci	u32 reg;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (vfn > 1) {
4362306a36Sopenharmony_ci		dev_err(&epc->dev, "Only Virtual Function #1 has deviceID\n");
4462306a36Sopenharmony_ci		return -EINVAL;
4562306a36Sopenharmony_ci	} else if (vfn == 1) {
4662306a36Sopenharmony_ci		reg = cap + PCI_SRIOV_VF_DID;
4762306a36Sopenharmony_ci		cdns_pcie_ep_fn_writew(pcie, fn, reg, hdr->deviceid);
4862306a36Sopenharmony_ci		return 0;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid);
5262306a36Sopenharmony_ci	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid);
5362306a36Sopenharmony_ci	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CLASS_PROG, hdr->progif_code);
5462306a36Sopenharmony_ci	cdns_pcie_ep_fn_writew(pcie, fn, PCI_CLASS_DEVICE,
5562306a36Sopenharmony_ci			       hdr->subclass_code | hdr->baseclass_code << 8);
5662306a36Sopenharmony_ci	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CACHE_LINE_SIZE,
5762306a36Sopenharmony_ci			       hdr->cache_line_size);
5862306a36Sopenharmony_ci	cdns_pcie_ep_fn_writew(pcie, fn, PCI_SUBSYSTEM_ID, hdr->subsys_id);
5962306a36Sopenharmony_ci	cdns_pcie_ep_fn_writeb(pcie, fn, PCI_INTERRUPT_PIN, hdr->interrupt_pin);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/*
6262306a36Sopenharmony_ci	 * Vendor ID can only be modified from function 0, all other functions
6362306a36Sopenharmony_ci	 * use the same vendor ID as function 0.
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	if (fn == 0) {
6662306a36Sopenharmony_ci		/* Update the vendor IDs. */
6762306a36Sopenharmony_ci		u32 id = CDNS_PCIE_LM_ID_VENDOR(hdr->vendorid) |
6862306a36Sopenharmony_ci			 CDNS_PCIE_LM_ID_SUBSYS(hdr->subsys_vendor_id);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id);
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn,
7762306a36Sopenharmony_ci				struct pci_epf_bar *epf_bar)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
8062306a36Sopenharmony_ci	struct cdns_pcie_epf *epf = &ep->epf[fn];
8162306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
8262306a36Sopenharmony_ci	dma_addr_t bar_phys = epf_bar->phys_addr;
8362306a36Sopenharmony_ci	enum pci_barno bar = epf_bar->barno;
8462306a36Sopenharmony_ci	int flags = epf_bar->flags;
8562306a36Sopenharmony_ci	u32 addr0, addr1, reg, cfg, b, aperture, ctrl;
8662306a36Sopenharmony_ci	u64 sz;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* BAR size is 2^(aperture + 7) */
8962306a36Sopenharmony_ci	sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE);
9062306a36Sopenharmony_ci	/*
9162306a36Sopenharmony_ci	 * roundup_pow_of_two() returns an unsigned long, which is not suited
9262306a36Sopenharmony_ci	 * for 64bit values.
9362306a36Sopenharmony_ci	 */
9462306a36Sopenharmony_ci	sz = 1ULL << fls64(sz - 1);
9562306a36Sopenharmony_ci	aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
9862306a36Sopenharmony_ci		ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS;
9962306a36Sopenharmony_ci	} else {
10062306a36Sopenharmony_ci		bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH);
10162306a36Sopenharmony_ci		bool is_64bits = sz > SZ_2G;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		if (is_64bits && (bar & 1))
10462306a36Sopenharmony_ci			return -EINVAL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
10762306a36Sopenharmony_ci			epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		if (is_64bits && is_prefetch)
11062306a36Sopenharmony_ci			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS;
11162306a36Sopenharmony_ci		else if (is_prefetch)
11262306a36Sopenharmony_ci			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS;
11362306a36Sopenharmony_ci		else if (is_64bits)
11462306a36Sopenharmony_ci			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS;
11562306a36Sopenharmony_ci		else
11662306a36Sopenharmony_ci			ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	addr0 = lower_32_bits(bar_phys);
12062306a36Sopenharmony_ci	addr1 = upper_32_bits(bar_phys);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (vfn == 1)
12362306a36Sopenharmony_ci		reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG(bar, fn);
12462306a36Sopenharmony_ci	else
12562306a36Sopenharmony_ci		reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG(bar, fn);
12662306a36Sopenharmony_ci	b = (bar < BAR_4) ? bar : bar - BAR_4;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (vfn == 0 || vfn == 1) {
12962306a36Sopenharmony_ci		cfg = cdns_pcie_readl(pcie, reg);
13062306a36Sopenharmony_ci		cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
13162306a36Sopenharmony_ci			 CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
13262306a36Sopenharmony_ci		cfg |= (CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) |
13362306a36Sopenharmony_ci			CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
13462306a36Sopenharmony_ci		cdns_pcie_writel(pcie, reg, cfg);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
13862306a36Sopenharmony_ci	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar),
13962306a36Sopenharmony_ci			 addr0);
14062306a36Sopenharmony_ci	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar),
14162306a36Sopenharmony_ci			 addr1);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (vfn > 0)
14462306a36Sopenharmony_ci		epf = &epf->epf[vfn - 1];
14562306a36Sopenharmony_ci	epf->epf_bar[bar] = epf_bar;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn,
15162306a36Sopenharmony_ci				   struct pci_epf_bar *epf_bar)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
15462306a36Sopenharmony_ci	struct cdns_pcie_epf *epf = &ep->epf[fn];
15562306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
15662306a36Sopenharmony_ci	enum pci_barno bar = epf_bar->barno;
15762306a36Sopenharmony_ci	u32 reg, cfg, b, ctrl;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (vfn == 1)
16062306a36Sopenharmony_ci		reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG(bar, fn);
16162306a36Sopenharmony_ci	else
16262306a36Sopenharmony_ci		reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG(bar, fn);
16362306a36Sopenharmony_ci	b = (bar < BAR_4) ? bar : bar - BAR_4;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (vfn == 0 || vfn == 1) {
16662306a36Sopenharmony_ci		ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED;
16762306a36Sopenharmony_ci		cfg = cdns_pcie_readl(pcie, reg);
16862306a36Sopenharmony_ci		cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
16962306a36Sopenharmony_ci			 CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
17062306a36Sopenharmony_ci		cfg |= CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl);
17162306a36Sopenharmony_ci		cdns_pcie_writel(pcie, reg, cfg);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
17562306a36Sopenharmony_ci	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
17662306a36Sopenharmony_ci	cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (vfn > 0)
17962306a36Sopenharmony_ci		epf = &epf->epf[vfn - 1];
18062306a36Sopenharmony_ci	epf->epf_bar[bar] = NULL;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn,
18462306a36Sopenharmony_ci				 phys_addr_t addr, u64 pci_addr, size_t size)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
18762306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
18862306a36Sopenharmony_ci	u32 r;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	r = find_first_zero_bit(&ep->ob_region_map, BITS_PER_LONG);
19162306a36Sopenharmony_ci	if (r >= ep->max_regions - 1) {
19262306a36Sopenharmony_ci		dev_err(&epc->dev, "no free outbound region\n");
19362306a36Sopenharmony_ci		return -EINVAL;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
19762306a36Sopenharmony_ci	cdns_pcie_set_outbound_region(pcie, 0, fn, r, false, addr, pci_addr, size);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	set_bit(r, &ep->ob_region_map);
20062306a36Sopenharmony_ci	ep->ob_addr[r] = addr;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn,
20662306a36Sopenharmony_ci				    phys_addr_t addr)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
20962306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
21062306a36Sopenharmony_ci	u32 r;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	for (r = 0; r < ep->max_regions - 1; r++)
21362306a36Sopenharmony_ci		if (ep->ob_addr[r] == addr)
21462306a36Sopenharmony_ci			break;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (r == ep->max_regions - 1)
21762306a36Sopenharmony_ci		return;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	cdns_pcie_reset_outbound_region(pcie, r);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ep->ob_addr[r] = 0;
22262306a36Sopenharmony_ci	clear_bit(r, &ep->ob_region_map);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 mmc)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
22862306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
22962306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
23062306a36Sopenharmony_ci	u16 flags;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * Set the Multiple Message Capable bitfield into the Message Control
23662306a36Sopenharmony_ci	 * register.
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
23962306a36Sopenharmony_ci	flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1);
24062306a36Sopenharmony_ci	flags |= PCI_MSI_FLAGS_64BIT;
24162306a36Sopenharmony_ci	flags &= ~PCI_MSI_FLAGS_MASKBIT;
24262306a36Sopenharmony_ci	cdns_pcie_ep_fn_writew(pcie, fn, cap + PCI_MSI_FLAGS, flags);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
25062306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
25162306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
25262306a36Sopenharmony_ci	u16 flags, mme;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Validate that the MSI feature is actually enabled. */
25762306a36Sopenharmony_ci	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
25862306a36Sopenharmony_ci	if (!(flags & PCI_MSI_FLAGS_ENABLE))
25962306a36Sopenharmony_ci		return -EINVAL;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/*
26262306a36Sopenharmony_ci	 * Get the Multiple Message Enable bitfield from the Message Control
26362306a36Sopenharmony_ci	 * register.
26462306a36Sopenharmony_ci	 */
26562306a36Sopenharmony_ci	mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return mme;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
27362306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
27462306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
27562306a36Sopenharmony_ci	u32 val, reg;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	func_no = cdns_pcie_get_fn_from_vfn(pcie, func_no, vfunc_no);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	reg = cap + PCI_MSIX_FLAGS;
28062306a36Sopenharmony_ci	val = cdns_pcie_ep_fn_readw(pcie, func_no, reg);
28162306a36Sopenharmony_ci	if (!(val & PCI_MSIX_FLAGS_ENABLE))
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	val &= PCI_MSIX_FLAGS_QSIZE;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return val;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u8 vfn,
29062306a36Sopenharmony_ci				 u16 interrupts, enum pci_barno bir,
29162306a36Sopenharmony_ci				 u32 offset)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
29462306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
29562306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
29662306a36Sopenharmony_ci	u32 val, reg;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	reg = cap + PCI_MSIX_FLAGS;
30162306a36Sopenharmony_ci	val = cdns_pcie_ep_fn_readw(pcie, fn, reg);
30262306a36Sopenharmony_ci	val &= ~PCI_MSIX_FLAGS_QSIZE;
30362306a36Sopenharmony_ci	val |= interrupts;
30462306a36Sopenharmony_ci	cdns_pcie_ep_fn_writew(pcie, fn, reg, val);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* Set MSIX BAR and offset */
30762306a36Sopenharmony_ci	reg = cap + PCI_MSIX_TABLE;
30862306a36Sopenharmony_ci	val = offset | bir;
30962306a36Sopenharmony_ci	cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Set PBA BAR and offset.  BAR must match MSIX BAR */
31262306a36Sopenharmony_ci	reg = cap + PCI_MSIX_PBA;
31362306a36Sopenharmony_ci	val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
31462306a36Sopenharmony_ci	cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx,
32062306a36Sopenharmony_ci				     bool is_asserted)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
32362306a36Sopenharmony_ci	unsigned long flags;
32462306a36Sopenharmony_ci	u32 offset;
32562306a36Sopenharmony_ci	u16 status;
32662306a36Sopenharmony_ci	u8 msg_code;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	intx &= 3;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Set the outbound region if needed. */
33162306a36Sopenharmony_ci	if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY ||
33262306a36Sopenharmony_ci		     ep->irq_pci_fn != fn)) {
33362306a36Sopenharmony_ci		/* First region was reserved for IRQ writes. */
33462306a36Sopenharmony_ci		cdns_pcie_set_outbound_region_for_normal_msg(pcie, 0, fn, 0,
33562306a36Sopenharmony_ci							     ep->irq_phys_addr);
33662306a36Sopenharmony_ci		ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY;
33762306a36Sopenharmony_ci		ep->irq_pci_fn = fn;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (is_asserted) {
34162306a36Sopenharmony_ci		ep->irq_pending |= BIT(intx);
34262306a36Sopenharmony_ci		msg_code = MSG_CODE_ASSERT_INTA + intx;
34362306a36Sopenharmony_ci	} else {
34462306a36Sopenharmony_ci		ep->irq_pending &= ~BIT(intx);
34562306a36Sopenharmony_ci		msg_code = MSG_CODE_DEASSERT_INTA + intx;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	spin_lock_irqsave(&ep->lock, flags);
34962306a36Sopenharmony_ci	status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS);
35062306a36Sopenharmony_ci	if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) {
35162306a36Sopenharmony_ci		status ^= PCI_STATUS_INTERRUPT;
35262306a36Sopenharmony_ci		cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ep->lock, flags);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) |
35762306a36Sopenharmony_ci		 CDNS_PCIE_NORMAL_MSG_CODE(msg_code) |
35862306a36Sopenharmony_ci		 CDNS_PCIE_MSG_NO_DATA;
35962306a36Sopenharmony_ci	writel(0, ep->irq_cpu_addr + offset);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
36362306a36Sopenharmony_ci					u8 intx)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	u16 cmd;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND);
36862306a36Sopenharmony_ci	if (cmd & PCI_COMMAND_INTX_DISABLE)
36962306a36Sopenharmony_ci		return -EINVAL;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	cdns_pcie_ep_assert_intx(ep, fn, intx, true);
37262306a36Sopenharmony_ci	/*
37362306a36Sopenharmony_ci	 * The mdelay() value was taken from dra7xx_pcie_raise_legacy_irq()
37462306a36Sopenharmony_ci	 */
37562306a36Sopenharmony_ci	mdelay(1);
37662306a36Sopenharmony_ci	cdns_pcie_ep_assert_intx(ep, fn, intx, false);
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
38162306a36Sopenharmony_ci				     u8 interrupt_num)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
38462306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
38562306a36Sopenharmony_ci	u16 flags, mme, data, data_mask;
38662306a36Sopenharmony_ci	u8 msi_count;
38762306a36Sopenharmony_ci	u64 pci_addr, pci_addr_mask = 0xff;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Check whether the MSI feature has been enabled by the PCI host. */
39262306a36Sopenharmony_ci	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
39362306a36Sopenharmony_ci	if (!(flags & PCI_MSI_FLAGS_ENABLE))
39462306a36Sopenharmony_ci		return -EINVAL;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Get the number of enabled MSIs */
39762306a36Sopenharmony_ci	mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
39862306a36Sopenharmony_ci	msi_count = 1 << mme;
39962306a36Sopenharmony_ci	if (!interrupt_num || interrupt_num > msi_count)
40062306a36Sopenharmony_ci		return -EINVAL;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Compute the data value to be written. */
40362306a36Sopenharmony_ci	data_mask = msi_count - 1;
40462306a36Sopenharmony_ci	data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
40562306a36Sopenharmony_ci	data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Get the PCI address where to write the data into. */
40862306a36Sopenharmony_ci	pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
40962306a36Sopenharmony_ci	pci_addr <<= 32;
41062306a36Sopenharmony_ci	pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
41162306a36Sopenharmony_ci	pci_addr &= GENMASK_ULL(63, 2);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Set the outbound region if needed. */
41462306a36Sopenharmony_ci	if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) ||
41562306a36Sopenharmony_ci		     ep->irq_pci_fn != fn)) {
41662306a36Sopenharmony_ci		/* First region was reserved for IRQ writes. */
41762306a36Sopenharmony_ci		cdns_pcie_set_outbound_region(pcie, 0, fn, 0,
41862306a36Sopenharmony_ci					      false,
41962306a36Sopenharmony_ci					      ep->irq_phys_addr,
42062306a36Sopenharmony_ci					      pci_addr & ~pci_addr_mask,
42162306a36Sopenharmony_ci					      pci_addr_mask + 1);
42262306a36Sopenharmony_ci		ep->irq_pci_addr = (pci_addr & ~pci_addr_mask);
42362306a36Sopenharmony_ci		ep->irq_pci_fn = fn;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci	writel(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask));
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn, u8 vfn,
43162306a36Sopenharmony_ci				    phys_addr_t addr, u8 interrupt_num,
43262306a36Sopenharmony_ci				    u32 entry_size, u32 *msi_data,
43362306a36Sopenharmony_ci				    u32 *msi_addr_offset)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
43662306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
43762306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
43862306a36Sopenharmony_ci	u64 pci_addr, pci_addr_mask = 0xff;
43962306a36Sopenharmony_ci	u16 flags, mme, data, data_mask;
44062306a36Sopenharmony_ci	u8 msi_count;
44162306a36Sopenharmony_ci	int ret;
44262306a36Sopenharmony_ci	int i;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* Check whether the MSI feature has been enabled by the PCI host. */
44762306a36Sopenharmony_ci	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
44862306a36Sopenharmony_ci	if (!(flags & PCI_MSI_FLAGS_ENABLE))
44962306a36Sopenharmony_ci		return -EINVAL;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* Get the number of enabled MSIs */
45262306a36Sopenharmony_ci	mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
45362306a36Sopenharmony_ci	msi_count = 1 << mme;
45462306a36Sopenharmony_ci	if (!interrupt_num || interrupt_num > msi_count)
45562306a36Sopenharmony_ci		return -EINVAL;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Compute the data value to be written. */
45862306a36Sopenharmony_ci	data_mask = msi_count - 1;
45962306a36Sopenharmony_ci	data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
46062306a36Sopenharmony_ci	data = data & ~data_mask;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Get the PCI address where to write the data into. */
46362306a36Sopenharmony_ci	pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
46462306a36Sopenharmony_ci	pci_addr <<= 32;
46562306a36Sopenharmony_ci	pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
46662306a36Sopenharmony_ci	pci_addr &= GENMASK_ULL(63, 2);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	for (i = 0; i < interrupt_num; i++) {
46962306a36Sopenharmony_ci		ret = cdns_pcie_ep_map_addr(epc, fn, vfn, addr,
47062306a36Sopenharmony_ci					    pci_addr & ~pci_addr_mask,
47162306a36Sopenharmony_ci					    entry_size);
47262306a36Sopenharmony_ci		if (ret)
47362306a36Sopenharmony_ci			return ret;
47462306a36Sopenharmony_ci		addr = addr + entry_size;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	*msi_data = data;
47862306a36Sopenharmony_ci	*msi_addr_offset = pci_addr & pci_addr_mask;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
48462306a36Sopenharmony_ci				      u16 interrupt_num)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
48762306a36Sopenharmony_ci	u32 tbl_offset, msg_data, reg;
48862306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
48962306a36Sopenharmony_ci	struct pci_epf_msix_tbl *msix_tbl;
49062306a36Sopenharmony_ci	struct cdns_pcie_epf *epf;
49162306a36Sopenharmony_ci	u64 pci_addr_mask = 0xff;
49262306a36Sopenharmony_ci	u64 msg_addr;
49362306a36Sopenharmony_ci	u16 flags;
49462306a36Sopenharmony_ci	u8 bir;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	epf = &ep->epf[fn];
49762306a36Sopenharmony_ci	if (vfn > 0)
49862306a36Sopenharmony_ci		epf = &epf->epf[vfn - 1];
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Check whether the MSI-X feature has been enabled by the PCI host. */
50362306a36Sopenharmony_ci	flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS);
50462306a36Sopenharmony_ci	if (!(flags & PCI_MSIX_FLAGS_ENABLE))
50562306a36Sopenharmony_ci		return -EINVAL;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	reg = cap + PCI_MSIX_TABLE;
50862306a36Sopenharmony_ci	tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg);
50962306a36Sopenharmony_ci	bir = tbl_offset & PCI_MSIX_TABLE_BIR;
51062306a36Sopenharmony_ci	tbl_offset &= PCI_MSIX_TABLE_OFFSET;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	msix_tbl = epf->epf_bar[bir]->addr + tbl_offset;
51362306a36Sopenharmony_ci	msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
51462306a36Sopenharmony_ci	msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/* Set the outbound region if needed. */
51762306a36Sopenharmony_ci	if (ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) ||
51862306a36Sopenharmony_ci	    ep->irq_pci_fn != fn) {
51962306a36Sopenharmony_ci		/* First region was reserved for IRQ writes. */
52062306a36Sopenharmony_ci		cdns_pcie_set_outbound_region(pcie, 0, fn, 0,
52162306a36Sopenharmony_ci					      false,
52262306a36Sopenharmony_ci					      ep->irq_phys_addr,
52362306a36Sopenharmony_ci					      msg_addr & ~pci_addr_mask,
52462306a36Sopenharmony_ci					      pci_addr_mask + 1);
52562306a36Sopenharmony_ci		ep->irq_pci_addr = (msg_addr & ~pci_addr_mask);
52662306a36Sopenharmony_ci		ep->irq_pci_fn = fn;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	writel(msg_data, ep->irq_cpu_addr + (msg_addr & pci_addr_mask));
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn,
53462306a36Sopenharmony_ci				  enum pci_epc_irq_type type,
53562306a36Sopenharmony_ci				  u16 interrupt_num)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
53862306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
53962306a36Sopenharmony_ci	struct device *dev = pcie->dev;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	switch (type) {
54262306a36Sopenharmony_ci	case PCI_EPC_IRQ_LEGACY:
54362306a36Sopenharmony_ci		if (vfn > 0) {
54462306a36Sopenharmony_ci			dev_err(dev, "Cannot raise legacy interrupts for VF\n");
54562306a36Sopenharmony_ci			return -EINVAL;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci		return cdns_pcie_ep_send_legacy_irq(ep, fn, vfn, 0);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	case PCI_EPC_IRQ_MSI:
55062306a36Sopenharmony_ci		return cdns_pcie_ep_send_msi_irq(ep, fn, vfn, interrupt_num);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	case PCI_EPC_IRQ_MSIX:
55362306a36Sopenharmony_ci		return cdns_pcie_ep_send_msix_irq(ep, fn, vfn, interrupt_num);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	default:
55662306a36Sopenharmony_ci		break;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return -EINVAL;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int cdns_pcie_ep_start(struct pci_epc *epc)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
56562306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
56662306a36Sopenharmony_ci	struct device *dev = pcie->dev;
56762306a36Sopenharmony_ci	int max_epfs = sizeof(epc->function_num_map) * 8;
56862306a36Sopenharmony_ci	int ret, value, epf;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/*
57162306a36Sopenharmony_ci	 * BIT(0) is hardwired to 1, hence function 0 is always enabled
57262306a36Sopenharmony_ci	 * and can't be disabled anyway.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, epc->function_num_map);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (ep->quirk_disable_flr) {
57762306a36Sopenharmony_ci		for (epf = 0; epf < max_epfs; epf++) {
57862306a36Sopenharmony_ci			if (!(epc->function_num_map & BIT(epf)))
57962306a36Sopenharmony_ci				continue;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci			value = cdns_pcie_ep_fn_readl(pcie, epf,
58262306a36Sopenharmony_ci					CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET +
58362306a36Sopenharmony_ci					PCI_EXP_DEVCAP);
58462306a36Sopenharmony_ci			value &= ~PCI_EXP_DEVCAP_FLR;
58562306a36Sopenharmony_ci			cdns_pcie_ep_fn_writel(pcie, epf,
58662306a36Sopenharmony_ci					CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET +
58762306a36Sopenharmony_ci					PCI_EXP_DEVCAP, value);
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	ret = cdns_pcie_start_link(pcie);
59262306a36Sopenharmony_ci	if (ret) {
59362306a36Sopenharmony_ci		dev_err(dev, "Failed to start link\n");
59462306a36Sopenharmony_ci		return ret;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic const struct pci_epc_features cdns_pcie_epc_vf_features = {
60162306a36Sopenharmony_ci	.linkup_notifier = false,
60262306a36Sopenharmony_ci	.msi_capable = true,
60362306a36Sopenharmony_ci	.msix_capable = true,
60462306a36Sopenharmony_ci	.align = 65536,
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic const struct pci_epc_features cdns_pcie_epc_features = {
60862306a36Sopenharmony_ci	.linkup_notifier = false,
60962306a36Sopenharmony_ci	.msi_capable = true,
61062306a36Sopenharmony_ci	.msix_capable = true,
61162306a36Sopenharmony_ci	.align = 256,
61262306a36Sopenharmony_ci};
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic const struct pci_epc_features*
61562306a36Sopenharmony_cicdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	if (!vfunc_no)
61862306a36Sopenharmony_ci		return &cdns_pcie_epc_features;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return &cdns_pcie_epc_vf_features;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic const struct pci_epc_ops cdns_pcie_epc_ops = {
62462306a36Sopenharmony_ci	.write_header	= cdns_pcie_ep_write_header,
62562306a36Sopenharmony_ci	.set_bar	= cdns_pcie_ep_set_bar,
62662306a36Sopenharmony_ci	.clear_bar	= cdns_pcie_ep_clear_bar,
62762306a36Sopenharmony_ci	.map_addr	= cdns_pcie_ep_map_addr,
62862306a36Sopenharmony_ci	.unmap_addr	= cdns_pcie_ep_unmap_addr,
62962306a36Sopenharmony_ci	.set_msi	= cdns_pcie_ep_set_msi,
63062306a36Sopenharmony_ci	.get_msi	= cdns_pcie_ep_get_msi,
63162306a36Sopenharmony_ci	.set_msix	= cdns_pcie_ep_set_msix,
63262306a36Sopenharmony_ci	.get_msix	= cdns_pcie_ep_get_msix,
63362306a36Sopenharmony_ci	.raise_irq	= cdns_pcie_ep_raise_irq,
63462306a36Sopenharmony_ci	.map_msi_irq	= cdns_pcie_ep_map_msi_irq,
63562306a36Sopenharmony_ci	.start		= cdns_pcie_ep_start,
63662306a36Sopenharmony_ci	.get_features	= cdns_pcie_ep_get_features,
63762306a36Sopenharmony_ci};
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ciint cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	struct device *dev = ep->pcie.dev;
64362306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
64462306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
64562306a36Sopenharmony_ci	struct cdns_pcie *pcie = &ep->pcie;
64662306a36Sopenharmony_ci	struct cdns_pcie_epf *epf;
64762306a36Sopenharmony_ci	struct resource *res;
64862306a36Sopenharmony_ci	struct pci_epc *epc;
64962306a36Sopenharmony_ci	int ret;
65062306a36Sopenharmony_ci	int i;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	pcie->is_rc = false;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg");
65562306a36Sopenharmony_ci	if (IS_ERR(pcie->reg_base)) {
65662306a36Sopenharmony_ci		dev_err(dev, "missing \"reg\"\n");
65762306a36Sopenharmony_ci		return PTR_ERR(pcie->reg_base);
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
66162306a36Sopenharmony_ci	if (!res) {
66262306a36Sopenharmony_ci		dev_err(dev, "missing \"mem\"\n");
66362306a36Sopenharmony_ci		return -EINVAL;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci	pcie->mem_res = res;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	ep->max_regions = CDNS_PCIE_MAX_OB;
66862306a36Sopenharmony_ci	of_property_read_u32(np, "cdns,max-outbound-regions", &ep->max_regions);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	ep->ob_addr = devm_kcalloc(dev,
67162306a36Sopenharmony_ci				   ep->max_regions, sizeof(*ep->ob_addr),
67262306a36Sopenharmony_ci				   GFP_KERNEL);
67362306a36Sopenharmony_ci	if (!ep->ob_addr)
67462306a36Sopenharmony_ci		return -ENOMEM;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */
67762306a36Sopenharmony_ci	cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0));
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops);
68062306a36Sopenharmony_ci	if (IS_ERR(epc)) {
68162306a36Sopenharmony_ci		dev_err(dev, "failed to create epc device\n");
68262306a36Sopenharmony_ci		return PTR_ERR(epc);
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	epc_set_drvdata(epc, ep);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0)
68862306a36Sopenharmony_ci		epc->max_functions = 1;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	ep->epf = devm_kcalloc(dev, epc->max_functions, sizeof(*ep->epf),
69162306a36Sopenharmony_ci			       GFP_KERNEL);
69262306a36Sopenharmony_ci	if (!ep->epf)
69362306a36Sopenharmony_ci		return -ENOMEM;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	epc->max_vfs = devm_kcalloc(dev, epc->max_functions,
69662306a36Sopenharmony_ci				    sizeof(*epc->max_vfs), GFP_KERNEL);
69762306a36Sopenharmony_ci	if (!epc->max_vfs)
69862306a36Sopenharmony_ci		return -ENOMEM;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	ret = of_property_read_u8_array(np, "max-virtual-functions",
70162306a36Sopenharmony_ci					epc->max_vfs, epc->max_functions);
70262306a36Sopenharmony_ci	if (ret == 0) {
70362306a36Sopenharmony_ci		for (i = 0; i < epc->max_functions; i++) {
70462306a36Sopenharmony_ci			epf = &ep->epf[i];
70562306a36Sopenharmony_ci			if (epc->max_vfs[i] == 0)
70662306a36Sopenharmony_ci				continue;
70762306a36Sopenharmony_ci			epf->epf = devm_kcalloc(dev, epc->max_vfs[i],
70862306a36Sopenharmony_ci						sizeof(*ep->epf), GFP_KERNEL);
70962306a36Sopenharmony_ci			if (!epf->epf)
71062306a36Sopenharmony_ci				return -ENOMEM;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	ret = pci_epc_mem_init(epc, pcie->mem_res->start,
71562306a36Sopenharmony_ci			       resource_size(pcie->mem_res), PAGE_SIZE);
71662306a36Sopenharmony_ci	if (ret < 0) {
71762306a36Sopenharmony_ci		dev_err(dev, "failed to initialize the memory space\n");
71862306a36Sopenharmony_ci		return ret;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr,
72262306a36Sopenharmony_ci						  SZ_128K);
72362306a36Sopenharmony_ci	if (!ep->irq_cpu_addr) {
72462306a36Sopenharmony_ci		dev_err(dev, "failed to reserve memory space for MSI\n");
72562306a36Sopenharmony_ci		ret = -ENOMEM;
72662306a36Sopenharmony_ci		goto free_epc_mem;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci	ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE;
72962306a36Sopenharmony_ci	/* Reserve region 0 for IRQs */
73062306a36Sopenharmony_ci	set_bit(0, &ep->ob_region_map);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (ep->quirk_detect_quiet_flag)
73362306a36Sopenharmony_ci		cdns_pcie_detect_quiet_min_delay_set(&ep->pcie);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	spin_lock_init(&ep->lock);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	return 0;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci free_epc_mem:
74062306a36Sopenharmony_ci	pci_epc_mem_exit(epc);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return ret;
74362306a36Sopenharmony_ci}
744