lkml.org 
[lkml]   [2020]   [Jul]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v6 09/14] PCI: cadence: Add MSI-X support to Endpoint driver
    Date
    From: Alan Douglas <adouglas@cadence.com>

    Implement ->set_msix() and ->get_msix() callback functions in order
    to configure MSIX capability in the PCIe endpoint controller.

    Add cdns_pcie_ep_send_msix_irq() to send MSIX interrupts to Host.
    cdns_pcie_ep_send_msix_irq() gets the MSIX table address (virtual
    address) from "struct cdns_pcie_epf" that gets initialized in
    ->set_bar() call back function.

    Signed-off-by: Alan Douglas <adouglas@cadence.com>
    [kishon@ti.com: Re-implement MSIX support in accordance with the
    re-designed core MSI-X interfaces]
    Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
    ---
    .../pci/controller/cadence/pcie-cadence-ep.c | 108 +++++++++++++++++-
    drivers/pci/controller/cadence/pcie-cadence.h | 11 ++
    2 files changed, 118 insertions(+), 1 deletion(-)

    diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c
    index 034cb3cf726e..87c76341eab4 100644
    --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c
    +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c
    @@ -51,6 +51,7 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
    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;
    @@ -111,6 +112,8 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
    CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
    cdns_pcie_writel(pcie, reg, cfg);

    + epf->epf_bar[bar] = epf_bar;
    +
    return 0;
    }

    @@ -118,6 +121,7 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,
    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;
    @@ -139,6 +143,8 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,

    cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
    cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
    +
    + epf->epf_bar[bar] = NULL;
    }

    static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr,
    @@ -224,6 +230,50 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn)
    return mme;
    }

    +static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
    +{
    + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
    + struct cdns_pcie *pcie = &ep->pcie;
    + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
    + u32 val, reg;
    +
    + reg = cap + PCI_MSIX_FLAGS;
    + val = cdns_pcie_ep_fn_readw(pcie, func_no, reg);
    + if (!(val & PCI_MSIX_FLAGS_ENABLE))
    + return -EINVAL;
    +
    + val &= PCI_MSIX_FLAGS_QSIZE;
    +
    + return val;
    +}
    +
    +static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u16 interrupts,
    + enum pci_barno bir, u32 offset)
    +{
    + struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
    + struct cdns_pcie *pcie = &ep->pcie;
    + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
    + u32 val, reg;
    +
    + reg = cap + PCI_MSIX_FLAGS;
    + val = cdns_pcie_ep_fn_readw(pcie, fn, reg);
    + val &= ~PCI_MSIX_FLAGS_QSIZE;
    + val |= interrupts;
    + cdns_pcie_ep_fn_writew(pcie, fn, reg, val);
    +
    + /* Set MSIX BAR and offset */
    + reg = cap + PCI_MSIX_TABLE;
    + val = offset | bir;
    + cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
    +
    + /* Set PBA BAR and offset. BAR must match MSIX BAR */
    + reg = cap + PCI_MSIX_PBA;
    + val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
    + cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
    +
    + return 0;
    +}
    +
    static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn,
    u8 intx, bool is_asserted)
    {
    @@ -333,6 +383,52 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
    return 0;
    }

    +static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn,
    + u16 interrupt_num)
    +{
    + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
    + u32 tbl_offset, msg_data, reg, vec_ctrl;
    + 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;
    +
    + /* 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 = tbl_offset & PCI_MSIX_TABLE_BIR;
    + tbl_offset &= PCI_MSIX_TABLE_OFFSET;
    +
    + epf = &ep->epf[fn];
    + 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;
    + vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl;
    +
    + /* 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_set_outbound_region(pcie, 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_ep_raise_irq(struct pci_epc *epc, u8 fn,
    enum pci_epc_irq_type type,
    u16 interrupt_num)
    @@ -346,6 +442,9 @@ static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
    case PCI_EPC_IRQ_MSI:
    return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num);

    + case PCI_EPC_IRQ_MSIX:
    + return cdns_pcie_ep_send_msix_irq(ep, fn, interrupt_num);
    +
    default:
    break;
    }
    @@ -383,7 +482,7 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
    static const struct pci_epc_features cdns_pcie_epc_features = {
    .linkup_notifier = false,
    .msi_capable = true,
    - .msix_capable = false,
    + .msix_capable = true,
    };

    static const struct pci_epc_features*
    @@ -400,6 +499,8 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
    .unmap_addr = cdns_pcie_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_ep_raise_irq,
    .start = cdns_pcie_ep_start,
    .get_features = cdns_pcie_ep_get_features,
    @@ -458,6 +559,11 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *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;
    +
    ret = pci_epc_mem_init(epc, pcie->mem_res->start,
    resource_size(pcie->mem_res), PAGE_SIZE);
    if (ret < 0) {
    diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
    index 1d2bdc4e08e4..8057f4a338bd 100644
    --- a/drivers/pci/controller/cadence/pcie-cadence.h
    +++ b/drivers/pci/controller/cadence/pcie-cadence.h
    @@ -113,6 +113,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_MSIX_CAP_OFFSET 0xb0

    /*
    * Root Port Registers (PCI configuration space for the root port function)
    @@ -302,6 +303,14 @@ struct cdns_pcie_rc {
    bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
    };

    +/**
    + * struct cdns_pcie_epf - Structure to hold info about endpoint function
    + * @epf_bar: reference to the pci_epf_bar for the six Base Address Registers
    + */
    +struct cdns_pcie_epf {
    + struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
    +};
    +
    /**
    * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
    * @pcie: Cadence PCIe controller
    @@ -321,6 +330,7 @@ struct cdns_pcie_rc {
    * @lock: spin lock to disable interrupts while modifying PCIe controller
    * registers fields (RMW) accessible by both remote RC and EP to
    * minimize time between read and write
    + * @epf: Structure to hold info about endpoint function
    */
    struct cdns_pcie_ep {
    struct cdns_pcie pcie;
    @@ -334,6 +344,7 @@ struct cdns_pcie_ep {
    u8 irq_pending;
    /* protect writing to PCI_STATUS while raising legacy interrupts */
    spinlock_t lock;
    + struct cdns_pcie_epf *epf;
    };


    --
    2.17.1
    \
     
     \ /
      Last update: 2020-07-08 11:31    [W:5.039 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site