Now that support for SRIOV is added in PCIe endpoint core, add support
to configure virtual functions in the Cadence PCIe EP driver.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
 drivers/pci/controller/pcie-cadence-ep.c | 104 ++++++++++++++++++++---
 drivers/pci/controller/pcie-cadence.h    |   5 ++
 2 files changed, 98 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/controller/pcie-cadence-ep.c 
b/drivers/pci/controller/pcie-cadence-ep.c
index df8fc495ffd9..3dc1a896c1e6 100644
--- a/drivers/pci/controller/pcie-cadence-ep.c
+++ b/drivers/pci/controller/pcie-cadence-ep.c
@@ -53,7 +53,18 @@ static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 
fn, u8 vfn,
                                     struct pci_epf_header *hdr)
 {
        struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
+       u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
        struct cdns_pcie *pcie = &ep->pcie;
+       u32 reg;
+
+       if (vfn > 1) {
+               dev_dbg(&epc->dev, "Only Virtual Function #1 has deviceID\n");
+               return 0;
+       } else if (vfn == 1) {
+               reg = cap + PCI_SRIOV_VF_DID;
+               cdns_pcie_ep_fn_writew(pcie, fn, reg, hdr->deviceid);
+               return 0;
+       }
 
        cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid);
        cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid);
@@ -84,11 +95,13 @@ static int cdns_pcie_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);
+       u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
        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;
+       u32 first_vf_offset, stride;
        u64 sz;
 
        /* BAR size is 2^(aperture + 7) */
@@ -124,19 +137,38 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 
fn, u8 vfn,
 
        addr0 = lower_32_bits(bar_phys);
        addr1 = upper_32_bits(bar_phys);
+
+       if (vfn == 1) {
+               if (bar < BAR_4) {
+                       reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn);
+                       b = bar;
+               } else {
+                       reg = CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn);
+                       b = bar - BAR_4;
+               }
+       } else {
+               if (bar < BAR_4) {
+                       reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn);
+                       b = bar;
+               } else {
+                       reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn);
+                       b = bar - BAR_4;
+               }
+       }
+
+       if (vfn > 0) {
+               first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap +
+                                                       PCI_SRIOV_VF_OFFSET);
+               stride = cdns_pcie_ep_fn_readw(pcie, fn, cap +
+                                              PCI_SRIOV_VF_STRIDE);
+               fn = fn + first_vf_offset + ((vfn - 1) * stride);
+       }
+
        cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar),
                         addr0);
        cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar),
                         addr1);
 
-       if (bar < BAR_4) {
-               reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn);
-               b = bar;
-       } else {
-               reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn);
-               b = bar - BAR_4;
-       }
-
        cfg = cdns_pcie_readl(pcie, reg);
        cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) |
                 CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b));
@@ -179,8 +211,18 @@ static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 
fn, u8 vfn,
 {
        struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
        struct cdns_pcie *pcie = &ep->pcie;
+       u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
+       u32 first_vf_offset, stride;
        u32 r;
 
+       if (vfn > 0) {
+               first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap +
+                                                       PCI_SRIOV_VF_OFFSET);
+               stride = cdns_pcie_ep_fn_readw(pcie, fn, cap +
+                                              PCI_SRIOV_VF_STRIDE);
+               fn = fn + first_vf_offset + ((vfn - 1) * stride);
+       }
+
        r = find_first_zero_bit(&ep->ob_region_map,
                                sizeof(ep->ob_region_map) * BITS_PER_LONG);
        if (r >= ep->max_regions - 1) {
@@ -220,9 +262,19 @@ static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 
fn, u8 vfn, u8 mmc)
 {
        struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
        struct cdns_pcie *pcie = &ep->pcie;
+       u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
        u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
+       u32 first_vf_offset, stride;
        u16 flags;
 
+       if (vfn > 0) {
+               first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap +
+                                                       PCI_SRIOV_VF_OFFSET);
+               stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap +
+                                              PCI_SRIOV_VF_STRIDE);
+               fn = fn + first_vf_offset + ((vfn - 1) * stride);
+       }
+
        /*
         * Set the Multiple Message Capable bitfield into the Message Control
         * register.
@@ -240,9 +292,19 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 
fn, u8 vfn)
 {
        struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
        struct cdns_pcie *pcie = &ep->pcie;
+       u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
        u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
+       u32 first_vf_offset, stride;
        u16 flags, mme;
 
+       if (vfn > 0) {
+               first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap +
+                                                       PCI_SRIOV_VF_OFFSET);
+               stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap +
+                                              PCI_SRIOV_VF_STRIDE);
+               fn = fn + first_vf_offset + ((vfn - 1) * stride);
+       }
+
        /* Validate that the MSI feature is actually enabled. */
        flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
        if (!(flags & PCI_MSI_FLAGS_ENABLE))
@@ -257,7 +319,7 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, 
u8 vfn)
        return mme;
 }
 
-static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn,
+static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
                                     u8 intx, bool is_asserted)
 {
        struct cdns_pcie *pcie = &ep->pcie;
@@ -320,10 +382,20 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep 
*ep, u8 fn, u8 vfn,
                                     u8 interrupt_num)
 {
        struct cdns_pcie *pcie = &ep->pcie;
+       u32 sriov_cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET;
        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;
+       u32 first_vf_offset, stride;
+
+       if (vfn > 0) {
+               first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap +
+                                                       PCI_SRIOV_VF_OFFSET);
+               stride = cdns_pcie_ep_fn_readw(pcie, fn, sriov_cap +
+                                              PCI_SRIOV_VF_STRIDE);
+               fn = fn + first_vf_offset + ((vfn - 1) * stride);
+       }
 
        /* Check whether the MSI feature has been enabled by the PCI host. */
        flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
@@ -364,7 +436,7 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep 
*ep, u8 fn, u8 vfn,
        return 0;
 }
 
-static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
+static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn,
                                  enum pci_epc_irq_type type,
                                  u16 interrupt_num)
 {
@@ -414,10 +486,20 @@ static const struct pci_epc_features 
cdns_pcie_epc_features = {
        .msix_capable = false,
 };
 
+static const struct pci_epc_features cdns_pcie_epc_vf_features = {
+       .linkup_notifier = false,
+       .msi_capable = true,
+       .msix_capable = true,
+       .align = 65536,
+};
+
 static const struct pci_epc_features*
 cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
 {
-       return &cdns_pcie_epc_features;
+       if (!vfunc_no)
+               return &cdns_pcie_epc_features;
+
+       return &cdns_pcie_epc_vf_features;
 }
 
 static const struct pci_epc_ops cdns_pcie_epc_ops = {
diff --git a/drivers/pci/controller/pcie-cadence.h 
b/drivers/pci/controller/pcie-cadence.h
index 3cef398b50da..8237847bbc4e 100644
--- a/drivers/pci/controller/pcie-cadence.h
+++ b/drivers/pci/controller/pcie-cadence.h
@@ -50,6 +50,10 @@
        (CDNS_PCIE_LM_BASE + 0x0240 + (fn) * 0x0008)
 #define CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn) \
        (CDNS_PCIE_LM_BASE + 0x0244 + (fn) * 0x0008)
+#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn) \
+       (CDNS_PCIE_LM_BASE + 0x0280 + (fn) * 0x0008)
+#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn) \
+       (CDNS_PCIE_LM_BASE + 0x0284 + (fn) * 0x0008)
 #define  CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \
        (GENMASK(4, 0) << ((b) * 8))
 #define  CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \
@@ -99,6 +103,7 @@
 #define CDNS_PCIE_EP_FUNC_BASE(fn)     (((fn) << 12) & GENMASK(19, 12))
 
 #define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET       0x90
+#define CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET     0x200
 
 /*
  * Root Port Registers (PCI configuration space for the root port function)
-- 
2.17.1

Reply via email to