From 36cc14ac14c0d49d33820a82dab52a7edc802fef Mon Sep 17 00:00:00 2001 From: Rolf Evers-Fischer Date: Wed, 28 Feb 2018 18:32:18 +0100 Subject: [PATCH 01/15] PCI: endpoint: Simplify name allocation for EPF device This commit replaces allocating and freeing the intermediate 'buf'/'func_name' with a combination of 'kstrndup()' and 'len'. 'len' is the required length of 'epf->name'. 'epf->name' should be either the first part of 'name' preceding the '.' or the complete 'name', if there is no '.' in the name. Signed-off-by: Rolf Evers-Fischer Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I Reviewed-by: Andy Shevchenko --- drivers/pci/endpoint/pci-epf-core.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 766ce1dca2ec..1f2506f32bb9 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -200,8 +200,7 @@ struct pci_epf *pci_epf_create(const char *name) int ret; struct pci_epf *epf; struct device *dev; - char *func_name; - char *buf; + int len; epf = kzalloc(sizeof(*epf), GFP_KERNEL); if (!epf) { @@ -209,20 +208,11 @@ struct pci_epf *pci_epf_create(const char *name) goto err_ret; } - buf = kstrdup(name, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto free_epf; - } - - func_name = buf; - buf = strchrnul(buf, '.'); - *buf = '\0'; - - epf->name = kstrdup(func_name, GFP_KERNEL); + len = strchrnul(name, '.') - name; + epf->name = kstrndup(name, len, GFP_KERNEL); if (!epf->name) { ret = -ENOMEM; - goto free_func_name; + goto free_epf; } dev = &epf->dev; @@ -238,16 +228,12 @@ struct pci_epf *pci_epf_create(const char *name) if (ret) goto put_dev; - kfree(func_name); return epf; put_dev: put_device(dev); kfree(epf->name); -free_func_name: - kfree(func_name); - free_epf: kfree(epf); From 9eef6a5c3b0bf90eb292d462ea267bcb6ad1c334 Mon Sep 17 00:00:00 2001 From: Rolf Evers-Fischer Date: Wed, 28 Feb 2018 18:32:19 +0100 Subject: [PATCH 02/15] PCI: endpoint: Fix kernel panic after put_device() 'put_device()' calls the relase function 'pci_epf_dev_release()', which already frees 'epf->name' and 'epf'. Therefore we must not free them again after 'put_device()'. Fixes: 5e8cb4033807 ("PCI: endpoint: Add EP core layer to enable EP controller and EP functions") Signed-off-by: Rolf Evers-Fischer Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I Reviewed-by: Andy Shevchenko --- drivers/pci/endpoint/pci-epf-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 1f2506f32bb9..1878a6776519 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -232,7 +232,7 @@ struct pci_epf *pci_epf_create(const char *name) put_dev: put_device(dev); - kfree(epf->name); + return ERR_PTR(ret); free_epf: kfree(epf); From 50ee106137f2947958b2931de853010582e9d719 Mon Sep 17 00:00:00 2001 From: Rolf Evers-Fischer Date: Wed, 28 Feb 2018 18:32:20 +0100 Subject: [PATCH 03/15] PCI: endpoint: Remove goto labels in pci_epf_create() Remove the pci_epf_create() goto labels completely and handle the errors at the respective call site to simplify the function error handling. Signed-off-by: Rolf Evers-Fischer Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epf-core.c | 32 +++++++++++------------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 1878a6776519..59ed29e550e9 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -203,16 +203,14 @@ struct pci_epf *pci_epf_create(const char *name) int len; epf = kzalloc(sizeof(*epf), GFP_KERNEL); - if (!epf) { - ret = -ENOMEM; - goto err_ret; - } + if (!epf) + return ERR_PTR(-ENOMEM); len = strchrnul(name, '.') - name; epf->name = kstrndup(name, len, GFP_KERNEL); if (!epf->name) { - ret = -ENOMEM; - goto free_epf; + kfree(epf); + return ERR_PTR(-ENOMEM); } dev = &epf->dev; @@ -221,24 +219,18 @@ struct pci_epf *pci_epf_create(const char *name) dev->type = &pci_epf_type; ret = dev_set_name(dev, "%s", name); - if (ret) - goto put_dev; + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } ret = device_add(dev); - if (ret) - goto put_dev; + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } return epf; - -put_dev: - put_device(dev); - return ERR_PTR(ret); - -free_epf: - kfree(epf); - -err_ret: - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(pci_epf_create); From bf597574b6bee5ac741c58da763ad2f10df19065 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:06 +0200 Subject: [PATCH 04/15] PCI: endpoint: BAR width should not depend on sizeof dma_addr_t If a BAR supports 64-bit width or not depends on the hardware, and should thus not depend on sizeof(dma_addr_t). If a certain hardware doesn't support 64-bit BARs, its epc->ops->set_bar() implementation should return -EINVAL when PCI_BASE_ADDRESS_MEM_TYPE_64 is set. We can't change pci_epc_set_bar() to only set PCI_BASE_ADDRESS_MEM_TYPE_64 based on size, since if the user, for some reason, wants to configure a BAR with a 64-bit width, even though the BAR size is less than 4 GB, he should be able to do that. However, since pci-epf-test is simply a test and not an API, we can set PCI_BASE_ADDRESS_MEM_TYPE_64 in pci-epf-test itself only based on size. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/functions/pci-epf-test.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 64d8a17f8094..f6c0c59b1bc8 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -70,7 +70,7 @@ struct pci_epf_test_data { bool linkup_notifier; }; -static int bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; +static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; static int pci_epf_test_copy(struct pci_epf_test *epf_test) { @@ -367,12 +367,14 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) struct pci_epf_test *epf_test = epf_get_drvdata(epf); enum pci_barno test_reg_bar = epf_test->test_reg_bar; - flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32; - if (sizeof(dma_addr_t) == 0x8) - flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; + + flags = PCI_BASE_ADDRESS_SPACE_MEMORY; + flags |= upper_32_bits(epf_bar->size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; + ret = pci_epc_set_bar(epc, epf->func_no, bar, epf_bar->phys_addr, epf_bar->size, flags); From bc4a48976f57bc88319bfa32690bcc4b6cef4a29 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:07 +0200 Subject: [PATCH 05/15] PCI: endpoint: Simplify epc->ops->set_bar()/pci_epc_set_bar() Add barno and flags to struct epf_bar. That way we can simplify epc->ops->set_bar()/pci_epc_set_bar() by passing a struct *epf_bar instead of a whole lot of arguments. This is needed so that epc->ops->set_bar() implementations can modify BAR flags. Will be utilized in a succeeding patch. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel Acked-by: Kishon Vijay Abraham I --- drivers/pci/cadence/pcie-cadence-ep.c | 9 ++++++--- drivers/pci/dwc/pcie-designware-ep.c | 8 +++++--- drivers/pci/endpoint/functions/pci-epf-test.c | 8 ++------ drivers/pci/endpoint/pci-epc-core.c | 10 ++++------ drivers/pci/endpoint/pci-epf-core.c | 4 ++++ include/linux/pci-epc.h | 6 ++---- include/linux/pci-epf.h | 2 ++ 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index 3c3a97743453..cef36cd6b710 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -77,16 +77,19 @@ static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, return 0; } -static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) +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 *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; u64 sz; /* BAR size is 2^(aperture + 7) */ - sz = max_t(size_t, size, CDNS_PCIE_EP_MIN_APERTURE); + sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE); /* * roundup_pow_of_two() returns an unsigned long, which is not suited * for 64bit values. diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 3a6feeff5f5b..b3a5533fe0b9 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -117,12 +117,14 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, } static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) + struct pci_epf_bar *epf_bar) { int ret; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + int flags = epf_bar->flags; enum dw_pcie_as_type as_type; u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); @@ -131,7 +133,7 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, else as_type = DW_PCIE_AS_IO; - ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type); + ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); if (ret) return ret; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index f6c0c59b1bc8..91274779e59f 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -358,7 +358,6 @@ static void pci_epf_test_unbind(struct pci_epf *epf) static int pci_epf_test_set_bar(struct pci_epf *epf) { - int flags; int bar; int ret; struct pci_epf_bar *epf_bar; @@ -370,14 +369,11 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; - flags = PCI_BASE_ADDRESS_SPACE_MEMORY; - flags |= upper_32_bits(epf_bar->size) ? + epf_bar->flags |= upper_32_bits(epf_bar->size) ? PCI_BASE_ADDRESS_MEM_TYPE_64 : PCI_BASE_ADDRESS_MEM_TYPE_32; - ret = pci_epc_set_bar(epc, epf->func_no, bar, - epf_bar->phys_addr, - epf_bar->size, flags); + ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); dev_err(dev, "failed to set BAR%d\n", bar); diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e245bba0ab53..784e33d6f229 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -300,14 +300,12 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar); * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space * @epc: the EPC device on which BAR has to be configured * @func_no: the endpoint function number in the EPC device - * @bar: the BAR number that has to be configured - * @size: the size of the addr space - * @flags: specify memory allocation/io allocation/32bit address/64 bit address + * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to configure the BAR of the endpoint device. */ -int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) { int ret; unsigned long irq_flags; @@ -319,7 +317,7 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar, return 0; spin_lock_irqsave(&epc->lock, irq_flags); - ret = epc->ops->set_bar(epc, func_no, bar, bar_phys, size, flags); + ret = epc->ops->set_bar(epc, func_no, epf_bar); spin_unlock_irqrestore(&epc->lock, irq_flags); return ret; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 59ed29e550e9..465b5f058b6d 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -98,6 +98,8 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar) epf->bar[bar].phys_addr = 0; epf->bar[bar].size = 0; + epf->bar[bar].barno = 0; + epf->bar[bar].flags = 0; } EXPORT_SYMBOL_GPL(pci_epf_free_space); @@ -126,6 +128,8 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) epf->bar[bar].phys_addr = phys_addr; epf->bar[bar].size = size; + epf->bar[bar].barno = bar; + epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY; return space; } diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index a1a5e5df0f66..75bae8aabbf9 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -39,8 +39,7 @@ struct pci_epc_ops { int (*write_header)(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int (*set_bar)(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags); + struct pci_epf_bar *epf_bar); void (*clear_bar)(struct pci_epc *epc, u8 func_no, enum pci_barno bar); int (*map_addr)(struct pci_epc *epc, u8 func_no, @@ -127,8 +126,7 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags); + struct pci_epf_bar *epf_bar); void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar); int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr, diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index e897bf076701..f7d6f4883f8b 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -97,6 +97,8 @@ struct pci_epf_driver { struct pci_epf_bar { dma_addr_t phys_addr; size_t size; + enum pci_barno barno; + int flags; }; /** From f16b1f6fdf631213428ff115ae49ea70c9ac4f2d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:08 +0200 Subject: [PATCH 06/15] PCI: endpoint: Setting BAR_5 to 64-bits wide is invalid Since a 64-bit BAR consists of a BAR pair, and since there is no BAR after BAR_5, BAR_5 cannot be 64-bits wide. This sanity check is done in pci_epc_set_bar(), so that we don't need to do this sanity check in all epc->ops->set_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 784e33d6f229..109d75f0b7d2 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -310,7 +310,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, int ret; unsigned long irq_flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || + (epf_bar->barno == BAR_5 && + epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) return -EINVAL; if (!epc->ops->set_bar) From 3567a4edd6352239586470da1bd5834989425085 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:09 +0200 Subject: [PATCH 07/15] PCI: endpoint: Setting 64-bit/prefetch bit is invalid when IO is set If flag PCI_BASE_ADDRESS_SPACE_IO is set, also having any PCI_BASE_ADDRESS_MEM_* bit set is invalid. This sanity check is done in pci_epc_set_bar(), so that we don't need to do this sanity check in all epc->ops->set_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 109d75f0b7d2..40eea20d21f9 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -309,10 +309,13 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, { int ret; unsigned long irq_flags; + int flags = epf_bar->flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || (epf_bar->barno == BAR_5 && - epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || + (flags & PCI_BASE_ADDRESS_SPACE_IO && + flags & PCI_BASE_ADDRESS_IO_MASK)) return -EINVAL; if (!epc->ops->set_bar) From f25b5fae29d4e5fe6ae17d3f898a959d72519b6a Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:10 +0200 Subject: [PATCH 08/15] PCI: endpoint: Setting a BAR size > 4 GB is invalid if 64-bit flag is not set Setting a BAR size > 4 GB is invalid if PCI_BASE_ADDRESS_MEM_TYPE_64 flag is not set. This sanity check is done in pci_epc_set_bar(), so that we don't need to do this sanity check in all epc->ops->set_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 40eea20d21f9..8637822605ff 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -315,7 +315,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, (epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || (flags & PCI_BASE_ADDRESS_SPACE_IO && - flags & PCI_BASE_ADDRESS_IO_MASK)) + flags & PCI_BASE_ADDRESS_IO_MASK) || + (upper_32_bits(epf_bar->size) && + !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))) return -EINVAL; if (!epc->ops->set_bar) From d28810ba7891a1df2cb00116c6c66167970a193d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:11 +0200 Subject: [PATCH 09/15] PCI: designware-ep: Make dw_pcie_ep_set_bar() handle 64-bit BARs properly Since a 64-bit BAR consists of a BAR pair, we need to write to both BARs in the BAR pair to setup the BAR properly. Link: https://lkml.kernel.org/r/20180328115018.31921-7-niklas.cassel@axis.com Signed-off-by: Niklas Cassel [lorenzo.pieralisi@arm.com: updated code according to review] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel --- drivers/pci/dwc/pcie-designware-ep.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index b3a5533fe0b9..70c8c1eedb42 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -138,8 +138,15 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, return ret; dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg, size - 1); + + dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); dw_pcie_writel_dbi(pci, reg, flags); + + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg + 4, 0); + } + dw_pcie_dbi_ro_wr_dis(pci); return 0; From a2ea8ac4ec72da44e2cf508e6431db0487b26893 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:12 +0200 Subject: [PATCH 10/15] PCI: cadence: Set PCI_BASE_ADDRESS_MEM_TYPE_64 if a 64-bit BAR was set-up cdns_pcie_ep_set_bar() does some round-up of the BAR size, which means that a 64-bit BAR can be set-up, even when the flag PCI_BASE_ADDRESS_MEM_TYPE_64 isn't set. If a 64-bit BAR was set-up, set the flag PCI_BASE_ADDRESS_MEM_TYPE_64, so that the calling function can know what BAR width that was actually set-up. I'm not sure why cdns_pcie_ep_set_bar() doesn't obey the flag PCI_BASE_ADDRESS_MEM_TYPE_64, but I leave this for the MAINTAINER to fix, since there might be a reason why this flag is ignored. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Alan Douglas --- drivers/pci/cadence/pcie-cadence-ep.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index cef36cd6b710..2905e098678c 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -106,6 +106,9 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, if (is_64bits && (bar & 1)) return -EINVAL; + if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + if (is_64bits && is_prefetch) ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; else if (is_prefetch) From fca83058753456528bef62579ae2b50799d7a473 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:13 +0200 Subject: [PATCH 11/15] PCI: endpoint: Handle 64-bit BARs properly If a 64-bit BAR was set-up, we need to skip a BAR, since a 64-bit BAR consists of a BAR pair. We need to check what BAR width the epc->ops->set_bar() specific implementation actually did set-up, since some drivers, like the Cadence EP controller, sometimes sets up a 64-bit BAR, even though a 32-bit BAR was requested. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/functions/pci-epf-test.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 91274779e59f..d46e3ebabb8e 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -380,6 +380,13 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) if (bar == test_reg_bar) return ret; } + /* + * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64 + * if the specific implementation required a 64-bit BAR, + * even if we only requested a 32-bit BAR. + */ + if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) + bar++; } return 0; From 77d08dbdae2e70a446c61f5db763deed5947acf3 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:14 +0200 Subject: [PATCH 12/15] PCI: endpoint: Make epc->ops->clear_bar()/pci_epc_clear_bar() take struct *epf_bar Make epc->ops->clear_bar()/pci_epc_clear_bar() take struct *epf_bar. This is needed so that epc->ops->clear_bar() can clear the BAR pair, if the BAR is 64-bits wide. This also makes it possible for pci_epc_clear_bar() to sanity check the flags. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel --- drivers/pci/cadence/pcie-cadence-ep.c | 3 ++- drivers/pci/dwc/pcie-designware-ep.c | 13 ++++++++++--- drivers/pci/endpoint/functions/pci-epf-test.c | 5 ++++- drivers/pci/endpoint/pci-epc-core.c | 7 ++++--- include/linux/pci-epc.h | 5 +++-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index 2905e098678c..3d8283e450a9 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -145,10 +145,11 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, } static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, - enum pci_barno bar) + struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + enum pci_barno bar = epf_bar->barno; u32 reg, cfg, b, ctrl; if (bar < BAR_4) { diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 70c8c1eedb42..c9bdb5f139b4 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -19,7 +19,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) pci_epc_linkup(epc); } -void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, + int flags) { u32 reg; @@ -30,6 +31,11 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) dw_pcie_dbi_ro_wr_dis(pci); } +void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ + __dw_pcie_ep_reset_bar(pci, bar, 0); +} + static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr) { @@ -104,13 +110,14 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, } static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar) + struct pci_epf_bar *epf_bar) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; u32 atu_index = ep->bar_to_atu[bar]; - dw_pcie_ep_reset_bar(pci, bar); + __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); clear_bit(atu_index, ep->ib_window_map); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index d46e3ebabb8e..7cef85124325 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -344,14 +344,17 @@ static void pci_epf_test_unbind(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epc *epc = epf->epc; + struct pci_epf_bar *epf_bar; int bar; cancel_delayed_work(&epf_test->cmd_handler); pci_epc_stop(epc); for (bar = BAR_0; bar <= BAR_5; bar++) { + epf_bar = &epf->bar[bar]; + if (epf_test->reg[bar]) { pci_epf_free_space(epf, epf_test->reg[bar], bar); - pci_epc_clear_bar(epc, epf->func_no, bar); + pci_epc_clear_bar(epc, epf->func_no, epf_bar); } } } diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 8637822605ff..eccc942043cb 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -276,11 +276,12 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr); * pci_epc_clear_bar() - reset the BAR * @epc: the EPC device for which the BAR has to be cleared * @func_no: the endpoint function number in the EPC device - * @bar: the BAR number that has to be reset + * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to reset the BAR of the endpoint device. */ -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar) +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) { unsigned long flags; @@ -291,7 +292,7 @@ void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar) return; spin_lock_irqsave(&epc->lock, flags); - epc->ops->clear_bar(epc, func_no, bar); + epc->ops->clear_bar(epc, func_no, epf_bar); spin_unlock_irqrestore(&epc->lock, flags); } EXPORT_SYMBOL_GPL(pci_epc_clear_bar); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 75bae8aabbf9..af657ca58b70 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -41,7 +41,7 @@ struct pci_epc_ops { int (*set_bar)(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar); void (*clear_bar)(struct pci_epc *epc, u8 func_no, - enum pci_barno bar); + struct pci_epf_bar *epf_bar); int (*map_addr)(struct pci_epc *epc, u8 func_no, phys_addr_t addr, u64 pci_addr, size_t size); void (*unmap_addr)(struct pci_epc *epc, u8 func_no, @@ -127,7 +127,8 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar); -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar); +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar); int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr, u64 pci_addr, size_t size); From 6474a4e5546e1e3fbba5081dd201ebed62939278 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:15 +0200 Subject: [PATCH 13/15] PCI: endpoint: Make sure that BAR_5 does not have 64-bit flag set when clearing Since a 64-bit BAR consists of a BAR pair, and since there is no BAR after BAR_5, BAR_5 cannot be 64-bits wide. This sanity check is done in pci_epc_clear_bar(), so that we don't need to do this sanity check in all epc->ops->clear_bar() implementations. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index eccc942043cb..b0ee42739c3c 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -285,7 +285,9 @@ void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, { unsigned long flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || + (epf_bar->barno == BAR_5 && + epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) return; if (!epc->ops->clear_bar) From 96a3be43261b919a1785d080b501fae26ce97bc2 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:16 +0200 Subject: [PATCH 14/15] PCI: designware-ep: Make dw_pcie_ep_reset_bar() handle 64-bit BARs properly Since a 64-bit BAR consists of a BAR pair, we need to write to both BARs in the BAR pair to clear the BAR properly. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel Acked-by: Kishon Vijay Abraham I --- drivers/pci/dwc/pcie-designware-ep.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index c9bdb5f139b4..9164c9084b4e 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -28,6 +28,10 @@ static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, dw_pcie_dbi_ro_wr_en(pci); dw_pcie_writel_dbi2(pci, reg, 0x0); dw_pcie_writel_dbi(pci, reg, 0x0); + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, 0x0); + dw_pcie_writel_dbi(pci, reg + 4, 0x0); + } dw_pcie_dbi_ro_wr_dis(pci); } From 16b17cad0990f13cdcdfd9694cc7c41c7c76b0cf Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 28 Mar 2018 13:50:17 +0200 Subject: [PATCH 15/15] misc: pci_endpoint_test: Handle 64-bit BARs properly A 64-bit BAR consists of a BAR pair, where the second BAR has the upper bits, so we cannot simply call pci_ioremap_bar() on every single BAR index. The second BAR in a BAR pair will not have the IORESOURCE_MEM resource flag set. Only call ioremap on BARs that have the IORESOURCE_MEM resource flag set. pci 0000:01:00.0: BAR 4: assigned [mem 0xc0300000-0xc031ffff 64bit] pci 0000:01:00.0: BAR 2: assigned [mem 0xc0320000-0xc03203ff 64bit] pci 0000:01:00.0: BAR 0: assigned [mem 0xc0320400-0xc03204ff 64bit] pci-endpoint-test 0000:01:00.0: can't ioremap BAR 1: [??? 0x00000000 flags 0x0] pci-endpoint-test 0000:01:00.0: failed to read BAR1 pci-endpoint-test 0000:01:00.0: can't ioremap BAR 3: [??? 0x00000000 flags 0x0] pci-endpoint-test 0000:01:00.0: failed to read BAR3 pci-endpoint-test 0000:01:00.0: can't ioremap BAR 5: [??? 0x00000000 flags 0x0] pci-endpoint-test 0000:01:00.0: failed to read BAR5 Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/misc/pci_endpoint_test.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 320276f42653..fe8897e64635 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -534,12 +534,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, } for (bar = BAR_0; bar <= BAR_5; bar++) { - base = pci_ioremap_bar(pdev, bar); - if (!base) { - dev_err(dev, "failed to read BAR%d\n", bar); - WARN_ON(bar == test_reg_bar); + if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { + base = pci_ioremap_bar(pdev, bar); + if (!base) { + dev_err(dev, "failed to read BAR%d\n", bar); + WARN_ON(bar == test_reg_bar); + } + test->bar[bar] = base; } - test->bar[bar] = base; } test->base = test->bar[test_reg_bar];