Re: [PATCH 4/4] PCI: cadence: Add MSI-X support to Endpoint driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Dec 11, 2019 at 2:47 PM Kishon Vijay Abraham I <kishon@xxxxxx> wrote:
>
> From: Alan Douglas <adouglas@xxxxxxxxxxx>
>
> 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.
>
> [kishon@xxxxxx: Re-implement MSIX support in accordance with the
>  re-designed core MSI-X interfaces]
> Signed-off-by: Kishon Vijay Abraham I <kishon@xxxxxx>
> Signed-off-by: Alan Douglas <adouglas@xxxxxxxxxxx>
> ---
>  .../pci/controller/cadence/pcie-cadence-ep.c  | 112 +++++++++++++++++-
>  drivers/pci/controller/cadence/pcie-cadence.h |  10 ++
>  2 files changed, 121 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c
> index 088394b6be04..c3081e8e52a4 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c
> +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c
> @@ -52,6 +52,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;
> @@ -112,6 +113,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;
>  }
>
> @@ -119,6 +122,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;
> @@ -140,6 +144,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,
> @@ -225,6 +231,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)
>  {
> @@ -331,6 +381,56 @@ 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 pci_epf_bar *epf_bar;
> +       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];
> +       epf_bar = epf->epf_bar[bir];
> +       msix_tbl = epf_bar->addr;
> +       msix_tbl = (struct pci_epf_msix_tbl *)((char *)msix_tbl + 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 (unlikely(ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) ||
I'm not sure about this _unlikely_, because unlike MSI, msg_addr will
be different for every vector.
It's interesting if safe to assume that host will allocate all message
address in linear fashion,
in that case maybe it's wiser to map all the the area at once and just
offset into it.

Thanks,
Ramon.

> +                    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)
> @@ -344,6 +444,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;
>         }
> @@ -381,7 +484,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*
> @@ -398,6 +501,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,
> @@ -457,6 +562,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));
>         if (ret < 0) {
> diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
> index ffa8b9f78ff8..207d6ba03f70 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence.h
> +++ b/drivers/pci/controller/cadence/pcie-cadence.h
> @@ -99,6 +99,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)
> @@ -281,6 +282,14 @@ struct cdns_pcie_rc {
>         u16                     device_id;
>  };
>
> +/**
> + * 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
> @@ -308,6 +317,7 @@ struct cdns_pcie_ep {
>         u64                     irq_pci_addr;
>         u8                      irq_pci_fn;
>         u8                      irq_pending;
> +       struct cdns_pcie_epf    *epf;
>  };
>
>
> --
> 2.17.1
>



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux