From 141cb3d4ee521eff45d0520327106ddfe0abe90a Mon Sep 17 00:00:00 2001 From: Xiaowei Song Date: Wed, 11 Jul 2018 16:09:46 +0800 Subject: PCI: kirin: Add MSI support Add support for MSI to the kirin host controller driver, based on the generic dwc infrastructure. Signed-off-by: Xiaowei Song Signed-off-by: Yao Chen [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Andy Shevchenko --- drivers/pci/controller/dwc/pcie-kirin.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index d2970a009eb5..5352e0c3be82 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -430,6 +430,9 @@ static int kirin_pcie_host_init(struct pcie_port *pp) { kirin_pcie_establish_link(pp); + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + return 0; } @@ -445,9 +448,34 @@ static const struct dw_pcie_host_ops kirin_pcie_host_ops = { .host_init = kirin_pcie_host_init, }; +static int kirin_pcie_add_msi(struct dw_pcie *pci, + struct platform_device *pdev) +{ + int irq; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, + "failed to get MSI IRQ (%d)\n", irq); + return irq; + } + + pci->pp.msi_irq = irq; + } + + return 0; +} + static int __init kirin_add_pcie_port(struct dw_pcie *pci, struct platform_device *pdev) { + int ret; + + ret = kirin_pcie_add_msi(pci, pdev); + if (ret) + return ret; + pci->pp.ops = &kirin_pcie_host_ops; return dw_pcie_host_init(&pci->pp); -- cgit v1.2.3 From a0476b3a678b4145a993f56071989af39d5718fb Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:43 +0800 Subject: PCI: exynos: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han --- drivers/pci/controller/dwc/pci-exynos.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index 4cc1e5df8c79..cee5f2f590e2 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -421,7 +421,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep, } } - pp->root_bus_nr = -1; pp->ops = &exynos_pcie_host_ops; ret = dw_pcie_host_init(pp); -- cgit v1.2.3 From 39f712e989c5e591c58b65b62981b85027ba3103 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:44 +0800 Subject: PCI: imx6: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Richard Zhu Cc: Lucas Stach --- drivers/pci/controller/dwc/pci-imx6.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 80f604602783..4a9a673b4777 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -667,7 +667,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, } } - pp->root_bus_nr = -1; pp->ops = &imx6_pcie_host_ops; ret = dw_pcie_host_init(pp); -- cgit v1.2.3 From 8519bc8fe88fbe5c1e448ae1dd1ba215a1723abe Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:45 +0800 Subject: PCI: keystone: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Cc: Murali Karicheri --- drivers/pci/controller/dwc/pci-keystone.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 3722a5f31e5e..e88bd221fffe 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -347,7 +347,6 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, } } - pp->root_bus_nr = -1; pp->ops = &keystone_pcie_host_ops; ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np); if (ret) { -- cgit v1.2.3 From 53b801fd020d4d00a1eebbab9f042a8b9fe03b78 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:46 +0800 Subject: PCI: armada8k: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Acked-by: Thomas Petazzoni --- drivers/pci/controller/dwc/pcie-armada8k.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 072fd7ecc29f..0c389a30ef5d 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -172,7 +172,6 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, struct device *dev = &pdev->dev; int ret; - pp->root_bus_nr = -1; pp->ops = &armada8k_pcie_host_ops; pp->irq = platform_get_irq(pdev, 0); -- cgit v1.2.3 From 84b88219553af870a47e28d6ba288bd6fbd129f7 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:47 +0800 Subject: PCI: artpec6: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Acked-by: Jesper Nilsson Cc: Jesper Nilsson --- drivers/pci/controller/dwc/pcie-artpec6.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 321b56cfd5d0..128b182648b3 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -399,7 +399,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, } } - pp->root_bus_nr = -1; pp->ops = &artpec6_pcie_host_ops; ret = dw_pcie_host_init(pp); -- cgit v1.2.3 From 3513f81816ef085c0068c8aea04b78b0a06a8720 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:48 +0800 Subject: PCI: designware-plat: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: Joao Pinto --- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 5937fed4c938..6e048c0b67f2 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -118,7 +118,6 @@ static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, return pp->msi_irq; } - pp->root_bus_nr = -1; pp->ops = &dw_plat_pcie_host_ops; ret = dw_pcie_host_init(pp); -- cgit v1.2.3 From 73fb9924d26b835f67acedacfe032343c9ed4f06 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:49 +0800 Subject: PCI: histb: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Cc: Jianguo Sun --- drivers/pci/controller/dwc/pcie-histb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 3611d6ce9a92..7b32e619b959 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -420,7 +420,6 @@ static int histb_pcie_probe(struct platform_device *pdev) phy_init(hipcie->phy); } - pp->root_bus_nr = -1; pp->ops = &histb_pcie_host_ops; platform_set_drvdata(pdev, hipcie); -- cgit v1.2.3 From 739cd35918b7cf495ea2d7abb2ff6ee97605f41d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:50 +0800 Subject: PCI: qcom: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index a1d0198081a6..4352c1cb926d 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1251,7 +1251,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) if (ret) return ret; - pp->root_bus_nr = -1; pp->ops = &qcom_pcie_dw_ops; if (IS_ENABLED(CONFIG_PCI_MSI)) { -- cgit v1.2.3 From 53dd0c51f16be5f215d3ecad435062df99ee29ae Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 1 Jul 2018 11:54:51 +0800 Subject: PCI: spear13xx: Drop unnecessary root_bus_nr setting Function dw_pcie_host_init() already initializes the root_bus_nr field of 'struct pcie_port', so the -1 assignment prior to calling dw_pcie_host_init() in platform specific driver is not really needed. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Lorenzo Pieralisi Acked-by: Pratyush Anand --- drivers/pci/controller/dwc/pcie-spear13xx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c index ecb58f7b7566..7d0cdfd8138b 100644 --- a/drivers/pci/controller/dwc/pcie-spear13xx.c +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -210,7 +210,6 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, return ret; } - pp->root_bus_nr = -1; pp->ops = &spear13xx_pcie_host_ops; ret = dw_pcie_host_init(pp); -- cgit v1.2.3 From 4e965ede1856ed62c7ac8b7ad905a4a285e4a9f3 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 19 Jul 2018 10:32:11 +0200 Subject: PCI: dwc: Fix EP link notification implementation Move specific features settings from EP shared code (pcie-designware-ep.c) to the driver (pcie-designware-plat.c). Previous implementation disables the EP link notification by default for all SoCs that uses EP DesignWare IP, which affects directly the dra7xx and artpec6 SoCs. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/pcie-designware-ep.c | 14 ++++++-------- drivers/pci/controller/dwc/pcie-designware-plat.c | 3 +++ 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 8650416f6f9e..04092a7aba89 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -386,15 +386,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) return -ENOMEM; ep->outbound_addr = addr; - if (ep->ops->ep_init) - ep->ops->ep_init(ep); - epc = devm_pci_epc_create(dev, &epc_ops); if (IS_ERR(epc)) { dev_err(dev, "Failed to create epc device\n"); return PTR_ERR(epc); } + ep->epc = epc; + epc_set_drvdata(epc, ep); + + if (ep->ops->ep_init) + ep->ops->ep_init(ep); + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); if (ret < 0) epc->max_functions = 1; @@ -413,11 +416,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) return -ENOMEM; } - epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER; - EPC_FEATURE_SET_BAR(epc->features, BAR_0); - - ep->epc = epc; - epc_set_drvdata(epc, ep); dw_pcie_setup(pci); return 0; diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 6e048c0b67f2..a37dc92a03c7 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -70,10 +70,13 @@ static const struct dw_pcie_ops dw_pcie_ops = { static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct pci_epc *epc = ep->epc; enum pci_barno bar; for (bar = BAR_0; bar <= BAR_5; bar++) dw_pcie_ep_reset_bar(pci, bar); + + epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER; } static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, -- cgit v1.2.3 From d3c70a98d7d63cae02d50ebfafea04264a767401 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 19 Jul 2018 10:32:13 +0200 Subject: PCI: Update xxx_pcie_ep_raise_irq() and pci_epc_raise_irq() signatures Change {cdns, dra7xx, artpec6, dw, rockchip}_pcie_ep_raise_irq() and pci_epc_raise_irq() signature, namely the interrupt_num variable type from u8 to u16 to accommodate 2048 maximum MSI-X interrupts. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Acked-by: Alan Douglas Acked-by: Shawn Lin Acked-by: Jesper Nilsson Acked-by: Joao Pinto Acked-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/pci-dra7xx.c | 2 +- drivers/pci/controller/dwc/pcie-artpec6.c | 2 +- drivers/pci/controller/dwc/pcie-designware-ep.c | 2 +- drivers/pci/controller/dwc/pcie-designware-plat.c | 2 +- drivers/pci/controller/dwc/pcie-designware.h | 2 +- drivers/pci/controller/pcie-cadence-ep.c | 3 ++- drivers/pci/controller/pcie-rockchip-ep.c | 2 +- drivers/pci/endpoint/pci-epc-core.c | 8 ++++---- include/linux/pci-epc.h | 6 +++--- 9 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 345aab56ce8b..ce9224a36f62 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -370,7 +370,7 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, } static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) + enum pci_epc_irq_type type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 128b182648b3..dba83abfe764 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -427,7 +427,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) } static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) + enum pci_epc_irq_type type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 04092a7aba89..69d039de2af6 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -242,7 +242,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) } static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) + enum pci_epc_irq_type type, u16 interrupt_num) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index a37dc92a03c7..cca69ac63319 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -81,7 +81,7 @@ static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, enum pci_epc_irq_type type, - u8 interrupt_num) + u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index bee4e2535a61..9d581c077329 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -191,7 +191,7 @@ enum dw_pcie_as_type { struct dw_pcie_ep_ops { void (*ep_init)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num); + enum pci_epc_irq_type type, u16 interrupt_num); }; struct dw_pcie_ep { diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index e3fe4124e3af..208d11f27d5d 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -363,7 +363,8 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, } static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, - enum pci_epc_irq_type type, u8 interrupt_num) + enum pci_epc_irq_type type, + u16 interrupt_num) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 6beba8ed7b84..b8163c56a142 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -472,7 +472,7 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, enum pci_epc_irq_type type, - u8 interrupt_num) + u16 interrupt_num) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 7d77bd0e5d4a..c72e656e7e29 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -131,13 +131,13 @@ EXPORT_SYMBOL_GPL(pci_epc_start); * pci_epc_raise_irq() - interrupt the host system * @epc: the EPC device which has to interrupt the host * @func_no: the endpoint function number in the EPC device - * @type: specify the type of interrupt; legacy or MSI - * @interrupt_num: the MSI interrupt number + * @type: specify the type of interrupt; legacy, MSI or MSI-X + * @interrupt_num: the MSI or MSI-X interrupt number * - * Invoke to raise an MSI or legacy interrupt + * Invoke to raise an legacy, MSI or MSI-X interrupt */ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) + enum pci_epc_irq_type type, u16 interrupt_num) { int ret; unsigned long flags; diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 89f079f582df..bb2395b56f13 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -35,7 +35,7 @@ enum pci_epc_irq_type { * MSI-X capability register * @get_msix: ops to get the number of MSI-X interrupts allocated by the RC * from the MSI-X capability register - * @raise_irq: ops to raise a legacy or MSI interrupt + * @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt * @start: ops to start the PCI link * @stop: ops to stop the PCI link * @owner: the module owner containing the ops @@ -56,7 +56,7 @@ struct pci_epc_ops { int (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts); int (*get_msix)(struct pci_epc *epc, u8 func_no); int (*raise_irq)(struct pci_epc *epc, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num); + enum pci_epc_irq_type type, u16 interrupt_num); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); struct module *owner; @@ -154,7 +154,7 @@ int pci_epc_get_msi(struct pci_epc *epc, u8 func_no); int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts); int pci_epc_get_msix(struct pci_epc *epc, u8 func_no); int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num); + enum pci_epc_irq_type type, u16 interrupt_num); int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); struct pci_epc *pci_epc_get(const char *epc_name); -- cgit v1.2.3 From beb4641a787df79a1423a8789d185b6b78fcbfea Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 19 Jul 2018 10:32:14 +0200 Subject: PCI: dwc: Add MSI-X callbacks handler Add PCIe config space capability search function. Add sysfs set/get interface to allow the change of EP MSI-X maximum number. Add EP MSI-X callback for triggering interruptions. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/pcie-designware-ep.c | 137 +++++++++++++++++++++- drivers/pci/controller/dwc/pcie-designware-plat.c | 2 + drivers/pci/controller/dwc/pcie-designware.h | 10 ++ 3 files changed, 148 insertions(+), 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 69d039de2af6..23be2c0249ee 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) __dw_pcie_ep_reset_bar(pci, bar, 0); } +static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, + u8 cap) +{ + u8 cap_id, next_cap_ptr; + u16 reg; + + reg = dw_pcie_readw_dbi(pci, cap_ptr); + next_cap_ptr = (reg & 0xff00) >> 8; + cap_id = (reg & 0x00ff); + + if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX) + return 0; + + if (cap_id == cap) + return cap_ptr; + + return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); +} + +static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap) +{ + u8 next_cap_ptr; + u16 reg; + + reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); + next_cap_ptr = (reg & 0x00ff); + + if (!next_cap_ptr) + return 0; + + return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); +} + static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr) { @@ -241,6 +274,45 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) return 0; } +static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 val, reg; + + if (!ep->msix_cap) + return -EINVAL; + + reg = ep->msix_cap + PCI_MSIX_FLAGS; + val = dw_pcie_readw_dbi(pci, reg); + if (!(val & PCI_MSIX_FLAGS_ENABLE)) + return -EINVAL; + + val &= PCI_MSIX_FLAGS_QSIZE; + + return val; +} + +static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 val, reg; + + if (!ep->msix_cap) + return -EINVAL; + + reg = ep->msix_cap + PCI_MSIX_FLAGS; + val = dw_pcie_readw_dbi(pci, reg); + val &= ~PCI_MSIX_FLAGS_QSIZE; + val |= interrupts; + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writew_dbi(pci, reg, val); + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, enum pci_epc_irq_type type, u16 interrupt_num) { @@ -282,6 +354,8 @@ static const struct pci_epc_ops epc_ops = { .unmap_addr = dw_pcie_ep_unmap_addr, .set_msi = dw_pcie_ep_set_msi, .get_msi = dw_pcie_ep_get_msi, + .set_msix = dw_pcie_ep_set_msix, + .get_msix = dw_pcie_ep_get_msix, .raise_irq = dw_pcie_ep_raise_irq, .start = dw_pcie_ep_start, .stop = dw_pcie_ep_stop, @@ -322,6 +396,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, + u16 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct pci_epc *epc = ep->epc; + u16 tbl_offset, bir; + u32 bar_addr_upper, bar_addr_lower; + u32 msg_addr_upper, msg_addr_lower; + u32 reg, msg_data, vec_ctrl; + u64 tbl_addr, msg_addr, reg_u64; + void __iomem *msix_tbl; + int ret; + + reg = ep->msix_cap + PCI_MSIX_TABLE; + tbl_offset = dw_pcie_readl_dbi(pci, reg); + bir = (tbl_offset & PCI_MSIX_TABLE_BIR); + tbl_offset &= PCI_MSIX_TABLE_OFFSET; + tbl_offset >>= 3; + + reg = PCI_BASE_ADDRESS_0 + (4 * bir); + bar_addr_upper = 0; + bar_addr_lower = dw_pcie_readl_dbi(pci, reg); + reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK); + if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64) + bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4); + + tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower; + tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE)); + tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK; + + msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr, + PCI_MSIX_ENTRY_SIZE); + if (!msix_tbl) + return -EINVAL; + + msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR); + msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR); + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; + msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA); + vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL); + + iounmap(msix_tbl); + + if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) + return -EPERM; + + ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, + epc->mem->page_size); + if (ret) + return ret; + + writel(msg_data, ep->msi_mem); + + dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); + + return 0; +} + void dw_pcie_ep_exit(struct dw_pcie_ep *ep) { struct pci_epc *epc = ep->epc; @@ -412,9 +544,12 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, epc->mem->page_size); if (!ep->msi_mem) { - dev_err(dev, "Failed to reserve memory for MSI\n"); + dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n"); return -ENOMEM; } + ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI); + + ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX); dw_pcie_setup(pci); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index cca69ac63319..35d2291c6ba5 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -91,6 +91,8 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return -EINVAL; case PCI_EPC_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); + case PCI_EPC_IRQ_MSIX: + return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 9d581c077329..00ac4197c457 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -208,6 +208,8 @@ struct dw_pcie_ep { u32 num_ob_windows; void __iomem *msi_mem; phys_addr_t msi_mem_phys; + u8 msi_cap; /* MSI capability offset */ + u8 msix_cap; /* MSI-X capability offset */ }; struct dw_pcie_ops { @@ -359,6 +361,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep); void dw_pcie_ep_exit(struct dw_pcie_ep *ep); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num); +int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, + u16 interrupt_num); void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); #else static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) @@ -380,6 +384,12 @@ static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +static inline int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, + u16 interrupt_num) +{ + return 0; +} + static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) { } -- cgit v1.2.3 From 3920a5d7b24dc5229abe6fb8409e4c9de2c8c7a0 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 19 Jul 2018 10:32:15 +0200 Subject: PCI: dwc: Rework MSI callbacks handler Remove duplicate defines located on pcie-designware.h file already available on /include/uapi/linux/pci-regs.h file. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/pcie-designware-ep.c | 49 +++++++++++++++++-------- drivers/pci/controller/dwc/pcie-designware.h | 11 ------ 2 files changed, 33 insertions(+), 27 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 23be2c0249ee..dd9c36678cba 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -246,29 +246,38 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) { - int val; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 val, reg; + + if (!ep->msi_cap) + return -EINVAL; - val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); - if (!(val & MSI_CAP_MSI_EN_MASK)) + reg = ep->msi_cap + PCI_MSI_FLAGS; + val = dw_pcie_readw_dbi(pci, reg); + if (!(val & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; - val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; + val = (val & PCI_MSI_FLAGS_QSIZE) >> 4; + return val; } -static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) { - int val; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 val, reg; - val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); - val &= ~MSI_CAP_MMC_MASK; - val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; + if (!ep->msi_cap) + return -EINVAL; + + reg = ep->msi_cap + PCI_MSI_FLAGS; + val = dw_pcie_readw_dbi(pci, reg); + val &= ~PCI_MSI_FLAGS_QMASK; + val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK; dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); + dw_pcie_writew_dbi(pci, reg, val); dw_pcie_dbi_ro_wr_dis(pci); return 0; @@ -367,21 +376,29 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct pci_epc *epc = ep->epc; u16 msg_ctrl, msg_data; - u32 msg_addr_lower, msg_addr_upper; + u32 msg_addr_lower, msg_addr_upper, reg; u64 msg_addr; bool has_upper; int ret; + if (!ep->msi_cap) + return -EINVAL; + /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ - msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); + reg = ep->msi_cap + PCI_MSI_FLAGS; + msg_ctrl = dw_pcie_readw_dbi(pci, reg); has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); - msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); + reg = ep->msi_cap + PCI_MSI_ADDRESS_LO; + msg_addr_lower = dw_pcie_readl_dbi(pci, reg); if (has_upper) { - msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); - msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64); + reg = ep->msi_cap + PCI_MSI_ADDRESS_HI; + msg_addr_upper = dw_pcie_readl_dbi(pci, reg); + reg = ep->msi_cap + PCI_MSI_DATA_64; + msg_data = dw_pcie_readw_dbi(pci, reg); } else { msg_addr_upper = 0; - msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32); + reg = ep->msi_cap + PCI_MSI_DATA_32; + msg_data = dw_pcie_readw_dbi(pci, reg); } msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 00ac4197c457..e8b97f5a64be 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -96,17 +96,6 @@ #define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ ((0x3 << 20) | ((region) << 9) | (0x1 << 8)) -#define MSI_MESSAGE_CONTROL 0x52 -#define MSI_CAP_MMC_SHIFT 1 -#define MSI_CAP_MMC_MASK (7 << MSI_CAP_MMC_SHIFT) -#define MSI_CAP_MME_SHIFT 4 -#define MSI_CAP_MSI_EN_MASK 0x1 -#define MSI_CAP_MME_MASK (7 << MSI_CAP_MME_SHIFT) -#define MSI_MESSAGE_ADDR_L32 0x54 -#define MSI_MESSAGE_ADDR_U32 0x58 -#define MSI_MESSAGE_DATA_32 0x58 -#define MSI_MESSAGE_DATA_64 0x5C - #define MAX_MSI_IRQS 256 #define MAX_MSI_IRQS_PER_CTRL 32 #define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) -- cgit v1.2.3 From cb22d40b5f2bcf9d374ae92ea504df620daea342 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 19 Jul 2018 10:32:16 +0200 Subject: PCI: dwc: Add legacy interrupt callback handler Currently DesignWare IP does not handle legacy interrupts. Add a legacy interrupt callback handler. Signed-off-by: Gustavo Pimentel [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/pcie-designware-ep.c | 10 ++++++++++ drivers/pci/controller/dwc/pcie-designware-plat.c | 3 +-- drivers/pci/controller/dwc/pcie-designware.h | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index dd9c36678cba..1e7b02221eac 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -370,6 +370,16 @@ static const struct pci_epc_ops epc_ops = { .stop = dw_pcie_ep_stop, }; +int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct device *dev = pci->dev; + + dev_err(dev, "EP cannot trigger legacy IRQs\n"); + + return -EINVAL; +} + int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num) { diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 35d2291c6ba5..3f8a3aa3a91e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -87,8 +87,7 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, switch (type) { case PCI_EPC_IRQ_LEGACY: - dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); - return -EINVAL; + return dw_pcie_ep_raise_legacy_irq(ep, func_no); case PCI_EPC_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); case PCI_EPC_IRQ_MSIX: diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index e8b97f5a64be..96126fd8403c 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -348,6 +348,7 @@ static inline int dw_pcie_allocate_domains(struct pcie_port *pp) void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); int dw_pcie_ep_init(struct dw_pcie_ep *ep); void dw_pcie_ep_exit(struct dw_pcie_ep *ep); +int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num); int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, @@ -367,6 +368,11 @@ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) { } +static inline int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +{ + return 0; +} + static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num) { -- cgit v1.2.3 From c2e00e31087e58f6c49b90b4702fc3df4fad6a83 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 19 Jul 2018 10:32:19 +0200 Subject: pci-epf-test/pci_endpoint_test: Add MSI-X support Add MSI-X support and update driver documentation accordingly. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- Documentation/PCI/endpoint/pci-endpoint.txt | 4 ++-- Documentation/PCI/endpoint/pci-test-function.txt | 4 +++- Documentation/PCI/endpoint/pci-test-howto.txt | 22 ++++++++++++++--- Documentation/ioctl/ioctl-number.txt | 1 + Documentation/misc-devices/pci-endpoint-test.txt | 3 +++ drivers/misc/pci_endpoint_test.c | 29 ++++++++++++++++------- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 + drivers/pci/endpoint/functions/pci-epf-test.c | 29 +++++++++++++++++++++-- include/linux/pci-epc.h | 1 + include/uapi/linux/pcitest.h | 1 + 10 files changed, 79 insertions(+), 16 deletions(-) (limited to 'drivers/pci/controller') diff --git a/Documentation/PCI/endpoint/pci-endpoint.txt b/Documentation/PCI/endpoint/pci-endpoint.txt index 9b1d66829290..e86a96b66a6a 100644 --- a/Documentation/PCI/endpoint/pci-endpoint.txt +++ b/Documentation/PCI/endpoint/pci-endpoint.txt @@ -44,7 +44,7 @@ by the PCI controller driver. * clear_bar: ops to reset the BAR * alloc_addr_space: ops to allocate in PCI controller address space * free_addr_space: ops to free the allocated address space - * raise_irq: ops to raise a legacy or MSI interrupt + * raise_irq: ops to raise a legacy, MSI or MSI-X interrupt * start: ops to start the PCI link * stop: ops to stop the PCI link @@ -96,7 +96,7 @@ by the PCI endpoint function driver. *) pci_epc_raise_irq() The PCI endpoint function driver should use pci_epc_raise_irq() to raise - Legacy Interrupt or MSI Interrupt. + Legacy Interrupt, MSI or MSI-X Interrupt. *) pci_epc_mem_alloc_addr() diff --git a/Documentation/PCI/endpoint/pci-test-function.txt b/Documentation/PCI/endpoint/pci-test-function.txt index bf4b5cf6fee6..5916f1f592bb 100644 --- a/Documentation/PCI/endpoint/pci-test-function.txt +++ b/Documentation/PCI/endpoint/pci-test-function.txt @@ -36,7 +36,7 @@ that the endpoint device must perform. Bitfield Description: Bit 0 : raise legacy IRQ Bit 1 : raise MSI IRQ - Bit 2 : raise MSI-X IRQ (reserved for future implementation) + Bit 2 : raise MSI-X IRQ Bit 3 : read command (read data from RC buffer) Bit 4 : write command (write data to RC buffer) Bit 5 : copy command (copy data from one RC buffer to another @@ -75,6 +75,7 @@ for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands. Possible types: - Legacy : 0 - MSI : 1 + - MSI-X : 2 *) PCI_ENDPOINT_TEST_IRQ_NUMBER @@ -83,3 +84,4 @@ This register contains the triggered ID interrupt. Admissible values: - Legacy : 0 - MSI : [1 .. 32] + - MSI-X : [1 .. 2048] diff --git a/Documentation/PCI/endpoint/pci-test-howto.txt b/Documentation/PCI/endpoint/pci-test-howto.txt index 75f48c3bb191..65f1a137e35c 100644 --- a/Documentation/PCI/endpoint/pci-test-howto.txt +++ b/Documentation/PCI/endpoint/pci-test-howto.txt @@ -45,9 +45,9 @@ The PCI endpoint framework populates the directory with the following configurable fields. # ls functions/pci_epf_test/func1 - baseclass_code interrupt_pin revid subsys_vendor_id - cache_line_size msi_interrupts subclass_code vendorid - deviceid progif_code subsys_id + baseclass_code interrupt_pin progif_code subsys_id + cache_line_size msi_interrupts revid subsys_vendorid + deviceid msix_interrupts subclass_code vendorid The PCI endpoint function driver populates these entries with default values when the device is bound to the driver. The pci-epf-test driver populates @@ -67,6 +67,7 @@ device, the following commands can be used. # echo 0x104c > functions/pci_epf_test/func1/vendorid # echo 0xb500 > functions/pci_epf_test/func1/deviceid # echo 16 > functions/pci_epf_test/func1/msi_interrupts + # echo 8 > functions/pci_epf_test/func1/msix_interrupts 1.5 Binding pci-epf-test Device to EP Controller @@ -153,6 +154,21 @@ following commands. MSI30: NOT OKAY MSI31: NOT OKAY MSI32: NOT OKAY + MSIX1: OKAY + MSIX2: OKAY + MSIX3: OKAY + MSIX4: OKAY + MSIX5: OKAY + MSIX6: OKAY + MSIX7: OKAY + MSIX8: OKAY + MSIX9: NOT OKAY + MSIX10: NOT OKAY + MSIX11: NOT OKAY + MSIX12: NOT OKAY + MSIX13: NOT OKAY + [...] + MSIX2048: NOT OKAY Read Tests diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 480c8609dc58..65259d459fd1 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -166,6 +166,7 @@ Code Seq#(hex) Include File Comments 'P' all linux/soundcard.h conflict! 'P' 60-6F sound/sscape_ioctl.h conflict! 'P' 00-0F drivers/usb/class/usblp.c conflict! +'P' 01-07 drivers/misc/pci_endpoint_test.c conflict! 'Q' all linux/soundcard.h 'R' 00-1F linux/random.h conflict! 'R' 01 linux/rfkill.h conflict! diff --git a/Documentation/misc-devices/pci-endpoint-test.txt b/Documentation/misc-devices/pci-endpoint-test.txt index 4ebc3594b32c..fdfa0f66d3d0 100644 --- a/Documentation/misc-devices/pci-endpoint-test.txt +++ b/Documentation/misc-devices/pci-endpoint-test.txt @@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests *) verifying addresses programmed in BAR *) raise legacy IRQ *) raise MSI IRQ + *) raise MSI-X IRQ *) read data *) write data *) copy data @@ -25,6 +26,8 @@ ioctl PCITEST_LEGACY_IRQ: Tests legacy IRQ PCITEST_MSI: Tests message signalled interrupts. The MSI number to be tested should be passed as argument. + PCITEST_MSIX: Tests message signalled interrupts. The MSI-X number + to be tested should be passed as argument. PCITEST_WRITE: Perform write tests. The size of the buffer should be passed as argument. PCITEST_READ: Perform read tests. The size of the buffer should be passed diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 349794cbe1f3..f4fef108caff 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -39,13 +39,14 @@ #define IRQ_TYPE_LEGACY 0 #define IRQ_TYPE_MSI 1 +#define IRQ_TYPE_MSIX 2 #define PCI_ENDPOINT_TEST_MAGIC 0x0 #define PCI_ENDPOINT_TEST_COMMAND 0x4 #define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define COMMAND_RAISE_MSI_IRQ BIT(1) -/* BIT(2) is reserved for raising MSI-X IRQ command */ +#define COMMAND_RAISE_MSIX_IRQ BIT(2) #define COMMAND_READ BIT(3) #define COMMAND_WRITE BIT(4) #define COMMAND_COPY BIT(5) @@ -84,7 +85,7 @@ MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test"); static int irq_type = IRQ_TYPE_MSI; module_param(irq_type, int, 0444); -MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI)"); +MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)"); enum pci_barno { BAR_0, @@ -202,16 +203,18 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) } static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, - u8 msi_num) + u16 msi_num, bool msix) { u32 val; struct pci_dev *pdev = test->pdev; pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, - IRQ_TYPE_MSI); + msix == false ? IRQ_TYPE_MSI : + IRQ_TYPE_MSIX); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - COMMAND_RAISE_MSI_IRQ); + msix == false ? COMMAND_RAISE_MSI_IRQ : + COMMAND_RAISE_MSIX_IRQ); val = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000)); if (!val) @@ -456,7 +459,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, ret = pci_endpoint_test_legacy_irq(test); break; case PCITEST_MSI: - ret = pci_endpoint_test_msi_irq(test, arg); + case PCITEST_MSIX: + ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX); break; case PCITEST_WRITE: ret = pci_endpoint_test_write(test, arg); @@ -542,6 +546,12 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, dev_err(dev, "Failed to get MSI interrupts\n"); test->num_irqs = irq; break; + case IRQ_TYPE_MSIX: + irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX); + if (irq < 0) + dev_err(dev, "Failed to get MSI-X interrupts\n"); + test->num_irqs = irq; + break; default: dev_err(dev, "Invalid IRQ type selected\n"); } @@ -558,8 +568,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, pci_endpoint_test_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, test); if (err) - dev_err(dev, "failed to request IRQ %d for MSI %d\n", - pci_irq_vector(pdev, i), i + 1); + dev_err(dev, "Failed to request IRQ %d for MSI%s %d\n", + pci_irq_vector(pdev, i), + irq_type == IRQ_TYPE_MSIX ? "-X" : "", i + 1); } for (bar = BAR_0; bar <= BAR_5; bar++) { @@ -625,6 +636,7 @@ err_iounmap: err_disable_msi: pci_disable_msi(pdev); + pci_disable_msix(pdev); pci_release_regions(pdev); err_disable_pdev: @@ -656,6 +668,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) for (i = 0; i < test->num_irqs; i++) devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test); pci_disable_msi(pdev); + pci_disable_msix(pdev); pci_release_regions(pdev); pci_disable_device(pdev); } diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 3f8a3aa3a91e..c12bf794d69c 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -77,6 +77,7 @@ static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) dw_pcie_ep_reset_bar(pci, bar); epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER; + epc->features |= EPC_FEATURE_MSIX_AVAILABLE; } static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index db4b23672004..3e86fa3c7da3 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -20,10 +20,11 @@ #define IRQ_TYPE_LEGACY 0 #define IRQ_TYPE_MSI 1 +#define IRQ_TYPE_MSIX 2 #define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define COMMAND_RAISE_MSI_IRQ BIT(1) -/* BIT(2) is reserved for raising MSI-X IRQ command */ +#define COMMAND_RAISE_MSIX_IRQ BIT(2) #define COMMAND_READ BIT(3) #define COMMAND_WRITE BIT(4) #define COMMAND_COPY BIT(5) @@ -47,6 +48,7 @@ struct pci_epf_test { struct pci_epf *epf; enum pci_barno test_reg_bar; bool linkup_notifier; + bool msix_available; struct delayed_work cmd_handler; }; @@ -266,6 +268,9 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq_type, case IRQ_TYPE_MSI: pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); break; + case IRQ_TYPE_MSIX: + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, irq); + break; default: dev_err(dev, "Failed to raise IRQ, unknown type\n"); break; @@ -292,7 +297,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->command = 0; reg->status = 0; - if (reg->irq_type > IRQ_TYPE_MSI) { + if (reg->irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Failed to detect IRQ type\n"); goto reset_handler; } @@ -346,6 +351,16 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } + if (command & COMMAND_RAISE_MSIX_IRQ) { + count = pci_epc_get_msix(epc, epf->func_no); + if (reg->irq_number > count || count <= 0) + goto reset_handler; + reg->status = STATUS_IRQ_RAISED; + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, + reg->irq_number); + goto reset_handler; + } + reset_handler: queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler, msecs_to_jiffies(1)); @@ -459,6 +474,8 @@ static int pci_epf_test_bind(struct pci_epf *epf) else epf_test->linkup_notifier = true; + epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE; + epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features); ret = pci_epc_write_header(epc, epf->func_no, header); @@ -481,6 +498,14 @@ static int pci_epf_test_bind(struct pci_epf *epf) return ret; } + if (epf_test->msix_available) { + ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts); + if (ret) { + dev_err(dev, "MSI-X configuration failed\n"); + return ret; + } + } + if (!epf_test->linkup_notifier) queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index bb2395b56f13..37dab8116901 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -102,6 +102,7 @@ struct pci_epc { #define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0) #define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3)) +#define EPC_FEATURE_MSIX_AVAILABLE BIT(4) #define EPC_FEATURE_SET_BAR(features, bar) \ (features |= (EPC_FEATURE_BAR_MASK & (bar << 1))) #define EPC_FEATURE_GET_BAR(features) \ diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h index 953cf036cb26..d746fb159dcd 100644 --- a/include/uapi/linux/pcitest.h +++ b/include/uapi/linux/pcitest.h @@ -16,5 +16,6 @@ #define PCITEST_WRITE _IOW('P', 0x4, unsigned long) #define PCITEST_READ _IOW('P', 0x5, unsigned long) #define PCITEST_COPY _IOW('P', 0x6, unsigned long) +#define PCITEST_MSIX _IOW('P', 0x7, int) #endif /* __UAPI_LINUX_PCITEST_H */ -- cgit v1.2.3