Here is v2 of the patchset for the ACPI PCI controller driver based on the earlier discussion[1].
This patchset unifies the pci-host-generic ECAM code and the ACPI PCI ECAM code much further so that both the implementations can use the same structure (struct pci_config_window) as ->sysdata.
The patchset should also make it easier to share platform quirks between DT and ACPI PCI controller drivers using struct pci_generic_ecam_ops.
Short description of the patches in the patchset: - fixup arm64 PCI code so that it can use the generic ACPI PCI driver. - implement pci/drivers/ecam.[ch] which will replace drivers/pci/host/pci-host-common.h API for ECAM access and will be shared by ACPI and DT. - Update users of the pci-host-common API to the ecam API - implement a simple PCI ACPI host. I have not used the pci_mmcfg_list or the region definitions from x86, but have used a much simpler approach here.
This should apply cleanly on top of the current 4.6 tree or the pci next tree, and can be reviewed as a patchset. The full set of changes for arm64 includes other fixes, that series is available at https://github.com/jchandra-brcm/linux branch arm64-acpi-pci-v2
This has been tested on qemu with OVMF for the ACPI part and with device tree for pci-host-generic code. Further testing and reviews are welcome.
Thanks, JC.
[1] https://lkml.org/lkml/2016/3/3/921
Jayachandran C (4): arm64: Prepare to use generic ACPI PCI implementation PCI: Provide common functions for ECAM mapping PCI: generic, thunder: update to use generic ECAM API ACPI: PCI: Add generic PCI host controller
arch/arm64/Kconfig | 3 + arch/arm64/kernel/pci.c | 7 +- drivers/acpi/Kconfig | 9 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pci_gen_host.c | 258 ++++++++++++++++++++++++++++++++++++ drivers/pci/Kconfig | 3 + drivers/pci/Makefile | 2 + drivers/pci/ecam.c | 130 ++++++++++++++++++ drivers/pci/ecam.h | 63 +++++++++ drivers/pci/host/Kconfig | 1 + drivers/pci/host/pci-host-common.c | 121 ++++++++--------- drivers/pci/host/pci-host-common.h | 47 ------- drivers/pci/host/pci-host-generic.c | 50 ++----- drivers/pci/host/pci-thunder-ecam.c | 37 +----- drivers/pci/host/pci-thunder-pem.c | 53 +++----- 15 files changed, 560 insertions(+), 225 deletions(-) create mode 100644 drivers/acpi/pci_gen_host.c create mode 100644 drivers/pci/ecam.c create mode 100644 drivers/pci/ecam.h delete mode 100644 drivers/pci/host/pci-host-common.h
Define PCI_MMCONFIG config option for arm64.
Weaken implementations of raw_pci_read/write and pci_acpi_scan_root, the generic ACPI PCI driver will implement these.
Signed-off-by: Jayachandran C jchandra@broadcom.com --- arch/arm64/Kconfig | 3 +++ arch/arm64/kernel/pci.c | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4f43622..6c6658a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -236,6 +236,9 @@ config PCI_DOMAINS_GENERIC config PCI_SYSCALL def_bool PCI
+config PCI_MMCONFIG + bool + source "drivers/pci/Kconfig"
endmenu diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 15109c11..098028c 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -67,13 +67,13 @@ int pcibios_alloc_irq(struct pci_dev *dev) /* * raw_pci_read/write - Platform-specific PCI config space access. */ -int raw_pci_read(unsigned int domain, unsigned int bus, +int __weak raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { return -ENXIO; }
-int raw_pci_write(unsigned int domain, unsigned int bus, +int __weak raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val) { return -ENXIO; @@ -81,9 +81,8 @@ int raw_pci_write(unsigned int domain, unsigned int bus,
#ifdef CONFIG_ACPI /* Root bridge scanning */ -struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +struct pci_bus *__weak pci_acpi_scan_root(struct acpi_pci_root *root) { - /* TODO: Should be revisited when implementing PCI on ACPI */ return NULL; } #endif
Add config option PCI_GENERIC_ECAM and file drivers/pci/ecam.c to provide generic functions for accessing memory mapped PCI config space.
The API is defined in drivers/pci/ecam.h and is written to replace the API in drivers/pci/host/pci-host-common.h. The file defines a new 'struct pci_config_window' to hold the information related to a PCI config area and its mapping. This structure is expected to be used as sysdata for controllers that have ECAM based mapping.
Helper functions are provided to setup the mapping, free the mapping and to implement the map_bus method in 'struct pci_ops'
Signed-off-by: Jayachandran C jchandra@broadcom.com --- drivers/pci/Kconfig | 3 ++ drivers/pci/Makefile | 2 + drivers/pci/ecam.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/ecam.h | 58 +++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 drivers/pci/ecam.c create mode 100644 drivers/pci/ecam.h
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 209292e..e930d62 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -83,6 +83,9 @@ config HT_IRQ config PCI_ATS bool
+config PCI_GENERIC_ECAM + bool + config PCI_IOV bool "PCI IOV support" depends on PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 2154092..810aec8 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -55,6 +55,8 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o
obj-$(CONFIG_PCI_STUB) += pci-stub.o
+obj-$(CONFIG_PCI_GENERIC_ECAM) += ecam.o + obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
obj-$(CONFIG_OF) += of.o diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c new file mode 100644 index 00000000..798f0b7 --- /dev/null +++ b/drivers/pci/ecam.c @@ -0,0 +1,130 @@ +/* + * Copyright 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/slab.h> + +#include "ecam.h" + +/* + * On 64 bit systems, we do a single ioremap for the whole config space + * since we have enough virtual address range available. On 32 bit, do an + * ioremap per bus. + */ +static const bool per_bus_mapping = !config_enabled(CONFIG_64BIT); + +/* + * Create a PCI config space window + * - reserve mem region + * - alloc struct pci_config_window with space for all mappings + * - ioremap the config space + */ +struct pci_config_window *pci_generic_ecam_create(struct device *dev, + phys_addr_t addr, u8 bus_start, u8 bus_end, + struct pci_generic_ecam_ops *ops) +{ + struct pci_config_window *cfg; + unsigned int bus_shift, bus_range, bsz, mapsz; + int i, nidx; + + if (bus_end < bus_start) + return ERR_PTR(-EINVAL); + + bus_shift = ops->bus_shift; + bus_range = bus_end - bus_start + 1; + bsz = 1 << bus_shift; + nidx = per_bus_mapping ? bus_range : 1; + mapsz = per_bus_mapping ? bsz : bus_range * bsz; + cfg = kzalloc(sizeof(*cfg) + nidx * sizeof(cfg->win[0]), GFP_KERNEL); + if (!cfg) + return ERR_PTR(-ENOMEM); + + cfg->bus_start = bus_start; + cfg->bus_end = bus_end; + cfg->ops = ops; + + if (!request_mem_region(addr, bus_range * bsz, "Configuration Space")) + goto err_exit; + + /* cfgaddr has to be set after request_mem_region */ + cfg->cfgaddr = addr; + + for (i = 0; i < nidx; i++) { + cfg->win[i] = ioremap(addr + i * mapsz, mapsz); + if (!cfg->win[i]) + goto err_exit; + } + return cfg; + +err_exit: + pci_generic_ecam_free(cfg); + return ERR_PTR(-ENOMEM); +} + +/* + * Free a config space mapping + */ +void pci_generic_ecam_free(struct pci_config_window *cfg) +{ + unsigned int bus_range; + int i, nidx; + + bus_range = cfg->bus_end - cfg->bus_start + 1; + nidx = per_bus_mapping ? bus_range : 1; + for (i = 0; i < nidx; i++) + if (cfg->win[i]) + iounmap(cfg->win[i]); + if (cfg->cfgaddr) + release_mem_region(cfg->cfgaddr, + bus_range << cfg->ops->bus_shift); + kfree(cfg); +} + +/* + * Function to implement the pci_ops ->map_bus method + */ +void __iomem *pci_generic_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_config_window *cfg = bus->sysdata; + unsigned int devfn_shift = cfg->ops->bus_shift - 8; + unsigned int busn = bus->number; + void __iomem *base; + + if (busn < cfg->bus_start || busn > cfg->bus_end) + return NULL; + + busn -= cfg->bus_start; + if (per_bus_mapping) + base = cfg->win[busn]; + else + base = cfg->win[0] + (busn << cfg->ops->bus_shift); + return base + (devfn << devfn_shift) + where; +} + +/* default ECAM ops */ +struct pci_generic_ecam_ops pci_generic_ecam_default_ops = { + .bus_shift = 20, + .ops = { + .map_bus = pci_generic_ecam_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; diff --git a/drivers/pci/ecam.h b/drivers/pci/ecam.h new file mode 100644 index 00000000..dda8c50 --- /dev/null +++ b/drivers/pci/ecam.h @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ +#ifndef DRIVERS_PCI_ECAM_H +#define DRIVERS_PCI_ECAM_H + +#include <linux/kernel.h> +#include <linux/platform_device.h> + +/* + * struct to hold pci ops and bus shift of the config window + * for a PCI controller. + */ +struct pci_generic_ecam_ops { + unsigned int bus_shift; + struct pci_ops ops; +}; + +/* + * struct to hold the mappings of a config space window. This + * will be allocated with enough entries in win[] to hold all + * the mappings for the bus range. + */ +struct pci_config_window { + phys_addr_t cfgaddr; + u16 domain; + u8 bus_start; + u8 bus_end; + void *priv; + struct pci_generic_ecam_ops *ops; + void __iomem *win[0]; +}; + +/* create and free for pci_config_window */ +struct pci_config_window *pci_generic_ecam_create(struct device *dev, + phys_addr_t addr, u8 bus_start, u8 bus_end, + struct pci_generic_ecam_ops *ops); +void pci_generic_ecam_free(struct pci_config_window *cfg); + +/* map_bus when ->sysdata is an instance of pci_config_window */ +void __iomem *pci_generic_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, + int where); +/* default ECAM ops, bus shift 20, generic read and write */ +extern struct pci_generic_ecam_ops pci_generic_ecam_default_ops; + +#endif
On 04/11/2016 03:45 PM, Jayachandran C wrote:
Add config option PCI_GENERIC_ECAM and file drivers/pci/ecam.c to provide generic functions for accessing memory mapped PCI config space.
The API is defined in drivers/pci/ecam.h and is written to replace the API in drivers/pci/host/pci-host-common.h. The file defines a new 'struct pci_config_window' to hold the information related to a PCI config area and its mapping. This structure is expected to be used as sysdata for controllers that have ECAM based mapping.
Helper functions are provided to setup the mapping, free the mapping and to implement the map_bus method in 'struct pci_ops'
Signed-off-by: Jayachandran C jchandra@broadcom.com
Tested-by: David Daney david.daney@cavium.com
drivers/pci/Kconfig | 3 ++ drivers/pci/Makefile | 2 + drivers/pci/ecam.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/ecam.h | 58 +++++++++++++++++++++++
I wonder if these files should go in drivers/pci/host ... I understand that you still have to use them from drivers/pci/acpi though.
I will let others opine on this, but could you put the contents of ecam.h into include/linux/pci.h along with the pci_generic_config_*() declarations?
If you did that, the contents of ecam.c could go into drivers/pci/access.c...
4 files changed, 193 insertions(+) create mode 100644 drivers/pci/ecam.c create mode 100644 drivers/pci/ecam.h
[...]
Hi David, JC,
On 04/11/2016 08:24 PM, David Daney wrote:
Tested-by: David Daney david.daney@cavium.com
On ThunderX (please let me know the silicon pass specifics off-list)? I'm planning to give this series a test run also on some other ARMv8 hardware and will prod a few of the other vendors to do so.
drivers/pci/ecam.c | 130 drivers/pci/ecam.h | 58 +++++++++++++++++++++++
I wonder if these files should go in drivers/pci/host ... I understand that you still have to use them from drivers/pci/acpi though.
I will let others opine on this, but could you put the contents of ecam.h into include/linux/pci.h along with the pci_generic_config_*() declarations?
If you did that, the contents of ecam.c could go into drivers/pci/access.c...
Quoting Bjorn's original reply to the previous series:
Some of the code that moved to drivers/acpi/pci_mcfg.c is not really ACPI-specific, and could potentially be used for non-ACPI bridges that support ECAM. I'd like to see that sort of code moved to a new file like drivers/pci/ecam.c.
So my guess is that this is the reasoning behind JC's file layout.
I'm curious what Lorenzo's take on things is currently. I assume this series is now to be the official coordinated version of this effort for upstream, following the advice of Bjorn previously, but I would like to know if everyone is behind this plan. I've (previously) requested a Linaro LEG meeting this week (part of our bootarch working group) to specifically discuss the status of PCI upstreaming in order to get the different vendors together to ensure every single one of them is tracking the correct latest effort and doing what is needed to test/aid, hence my ask. If this is now plan A, I'll make sure everyone is aligned behind it and start pinging people individually for testing.
Jon.
On Tue, Apr 12, 2016 at 12:26:25AM -0400, Jon Masters wrote:
[...]
Quoting Bjorn's original reply to the previous series:
Some of the code that moved to drivers/acpi/pci_mcfg.c is not really ACPI-specific, and could potentially be used for non-ACPI bridges that support ECAM. I'd like to see that sort of code moved to a new file like drivers/pci/ecam.c.
So my guess is that this is the reasoning behind JC's file layout.
I'm curious what Lorenzo's take on things is currently. I assume this series is now to be the official coordinated version of this effort for upstream, following the advice of Bjorn previously, but I would like to know if everyone is behind this plan. I've (previously) requested a Linaro LEG meeting this week (part of our bootarch working group) to specifically discuss the status of PCI upstreaming in order to get the different vendors together to ensure every single one of them is tracking the correct latest effort and doing what is needed to test/aid, hence my ask. If this is now plan A, I'll make sure everyone is aligned behind it and start pinging people individually for testing.
My take is that JC's aim is to get this four patch series reviewed and merged (which is *not* sufficient to get ACPI PCI to work fully on ARM64 - see cover letter - the remaining patches in his branch are not fixes, it is code that is required to get things to work, these 4 patches stand alone are not sufficient but I understand he wants to get them reviewed following feedback on the lists) so that we can make progress on ACPI PCI on ARM64.
I will comment on the patches as soon as I have time to review them, I certainly would like to understand what we have to do with the rest of the code though (provided this series is good to go) see above.
Lorenzo
On 04/12/2016 12:44 PM, Lorenzo Pieralisi wrote:
On Tue, Apr 12, 2016 at 12:26:25AM -0400, Jon Masters wrote:
[...]
Quoting Bjorn's original reply to the previous series:
Some of the code that moved to drivers/acpi/pci_mcfg.c is not really ACPI-specific, and could potentially be used for non-ACPI bridges that support ECAM. I'd like to see that sort of code moved to a new file like drivers/pci/ecam.c.
So my guess is that this is the reasoning behind JC's file layout.
I'm curious what Lorenzo's take on things is currently. I assume this series is now to be the official coordinated version of this effort for upstream, following the advice of Bjorn previously, but I would like to know if everyone is behind this plan. I've (previously) requested a Linaro LEG meeting this week (part of our bootarch working group) to specifically discuss the status of PCI upstreaming in order to get the different vendors together to ensure every single one of them is tracking the correct latest effort and doing what is needed to test/aid, hence my ask. If this is now plan A, I'll make sure everyone is aligned behind it and start pinging people individually for testing.
My take is that JC's aim is to get this four patch series reviewed and merged
Indeed, I see that's probably the goal, and why not :)
(which is *not* sufficient to get ACPI PCI to work fully on ARM64
- see cover letter - the remaining patches in his branch are not
fixes, it is code that is required to get things to work, these 4 patches stand alone are not sufficient but I understand he wants to get them reviewed following feedback on the lists) so that we can make progress on ACPI PCI on ARM64.
Agreed. I went through the branch and the 11 patches there, reacquainted myself with what's what. So what we have now is 4 patches here plus a few others that in total replace v5 of your previous mmconfig patches in functionality. The question is what happens with the rest (of JC's branch let's say) - do they get sent out now too?
I will comment on the patches as soon as I have time to review them, I certainly would like to understand what we have to do with the rest of the code though (provided this series is good to go) see above.
Right. That's my reason for asking. I'd like to know who is driving (I believe that to be Lorenzo) and what the path forward is, and whether we need to get additional support from anyone else. There's a multi-vendor meeting in the morning where I'm going to summarize the current state of these patches and I would like to know (soon) what the plan is so that we can get everyone on deck to help out at least with testing (most have tested the previous set, but we need public acks happening).
Jon.
On Thu, Apr 14, 2016 at 01:55:32AM -0400, Jon Masters wrote:
[...]
I will comment on the patches as soon as I have time to review them, I certainly would like to understand what we have to do with the rest of the code though (provided this series is good to go) see above.
Right. That's my reason for asking. I'd like to know who is driving (I believe that to be Lorenzo) and what the path forward is, and whether we need to get additional support from anyone else. There's a multi-vendor meeting in the morning where I'm going to summarize the current state of these patches and I would like to know (soon) what the plan is so that we can get everyone on deck to help out at least with testing (most have tested the previous set, but we need public acks happening).
Testing always helps, this code needs reviewing from PCI and ACPI standpoints, what we can do is review this series and repost the whole thing when we agree the ECAM refactoring this series is implementing is the right way to go and it is a step in that direction, with no generic MCFG support in the kernel ACPI PCI on ARM64 can't be implemented so I would start from that.
Lorenzo
On Tue, Apr 12, 2016 at 5:54 AM, David Daney ddaney@caviumnetworks.com wrote:
On 04/11/2016 03:45 PM, Jayachandran C wrote:
Add config option PCI_GENERIC_ECAM and file drivers/pci/ecam.c to provide generic functions for accessing memory mapped PCI config space.
The API is defined in drivers/pci/ecam.h and is written to replace the API in drivers/pci/host/pci-host-common.h. The file defines a new 'struct pci_config_window' to hold the information related to a PCI config area and its mapping. This structure is expected to be used as sysdata for controllers that have ECAM based mapping.
Helper functions are provided to setup the mapping, free the mapping and to implement the map_bus method in 'struct pci_ops'
Signed-off-by: Jayachandran C jchandra@broadcom.com
Tested-by: David Daney david.daney@cavium.com
I have updated the git tree (https://github.com/jchandra-brcm/linux/) with a branch arm64-acpi-pci-v3 . The branch has a new patch to use thunder ECAM ops in case of Cavium ThunderX platform when doing generic ACPI PCI initialization.
I am hoping that the controllers that have "ECAM with quirks" can use this mechanism for sharing the quirks between OF and ACPI.
If you have some time to review the patch and see it works for you, then I can post it with the v3 of this patchset.
drivers/pci/Kconfig | 3 ++ drivers/pci/Makefile | 2 + drivers/pci/ecam.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/ecam.h | 58 +++++++++++++++++++++++
I wonder if these files should go in drivers/pci/host ... I understand that you still have to use them from drivers/pci/acpi though.
I will let others opine on this, but could you put the contents of ecam.h into include/linux/pci.h along with the pci_generic_config_*() declarations?
If you did that, the contents of ecam.c could go into drivers/pci/access.c...
Earlier discussion seems to indicated that separate ecam.c/h was preferred. But I agree that it may be small enough to be merged.
Thanks, JC.
Use functions provided by drivers/pci/ecam.h for mapping the config space in drivers/pci/host/pci-host-common.c, and update its users to use 'struct pci_config_window' and 'struct pci_generic_ecam_ops'
The changes are mostly to use 'struct pci_config_window' in place of 'struct gen_pci'. Some of the fields of gen_pci were only used temporarily and can be eliminated by using local variables or function arguments, these are not carried over to struct pci_config_window.
pci-thunder-ecam.c and pci-thunder-pem.c are the only users of the pci_host_common_probe function and the gen_pci structure, these have been updated to use the new API as well.
The patch does not introduce any functional changes other than a very minor one: with the new code, on 64-bit platforms, we do just a single ioremap for the whole config space.
Signed-off-by: Jayachandran C jchandra@broadcom.com --- drivers/pci/ecam.h | 5 ++ drivers/pci/host/Kconfig | 1 + drivers/pci/host/pci-host-common.c | 121 ++++++++++++++++-------------------- drivers/pci/host/pci-host-common.h | 47 -------------- drivers/pci/host/pci-host-generic.c | 50 +++------------ drivers/pci/host/pci-thunder-ecam.c | 37 +++-------- drivers/pci/host/pci-thunder-pem.c | 53 +++++----------- 7 files changed, 93 insertions(+), 221 deletions(-) delete mode 100644 drivers/pci/host/pci-host-common.h
diff --git a/drivers/pci/ecam.h b/drivers/pci/ecam.h index dda8c50..8a9ca51 100644 --- a/drivers/pci/ecam.h +++ b/drivers/pci/ecam.h @@ -55,4 +55,9 @@ void __iomem *pci_generic_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, /* default ECAM ops, bus shift 20, generic read and write */ extern struct pci_generic_ecam_ops pci_generic_ecam_default_ops;
+#ifdef CONFIG_PCI_HOST_GENERIC +/* for DT based pci controllers that support ECAM */ +int pci_host_common_probe(struct platform_device *pdev, + struct pci_generic_ecam_ops *ops, void *priv); +#endif #endif diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 7a0780d..31d6eb5 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -82,6 +82,7 @@ config PCI_HOST_GENERIC bool "Generic PCI host controller" depends on (ARM || ARM64) && OF select PCI_HOST_COMMON + select PCI_GENERIC_ECAM help Say Y here if you want to support a simple generic PCI host controller, such as the one emulated by kvmtool. diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c index e9f850f..90dd0fc 100644 --- a/drivers/pci/host/pci-host-common.c +++ b/drivers/pci/host/pci-host-common.c @@ -22,27 +22,21 @@ #include <linux/of_pci.h> #include <linux/platform_device.h>
-#include "pci-host-common.h" +#include "../ecam.h"
-static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) -{ - pci_free_resource_list(&pci->resources); -} - -static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) +static int gen_pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *resources, struct resource **bus_range) { int err, res_valid = 0; - struct device *dev = pci->host.dev.parent; struct device_node *np = dev->of_node; resource_size_t iobase; struct resource_entry *win;
- err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, - &iobase); + err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase); if (err) return err;
- resource_list_for_each_entry(win, &pci->resources) { + resource_list_for_each_entry(win, resources) { struct resource *parent, *res = win->res;
switch (resource_type(res)) { @@ -60,7 +54,7 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) res_valid |= !(res->flags & IORESOURCE_PREFETCH); break; case IORESOURCE_BUS: - pci->cfg.bus_range = res; + *bus_range = res; default: continue; } @@ -79,65 +73,69 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) return 0;
out_release_res: - gen_pci_release_of_pci_ranges(pci); return err; }
-static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) +static void gen_pci_generic_unmap_cfg(void *ptr) +{ + pci_generic_ecam_free((struct pci_config_window *)ptr); +} + +static struct pci_config_window *gen_pci_init(struct device *dev, + struct list_head *resources, struct pci_generic_ecam_ops *ops, + void *priv) { int err; - u8 bus_max; - resource_size_t busn; - struct resource *bus_range; - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; - u32 sz = 1 << pci->cfg.ops->bus_shift; + struct resource cfgres; + struct resource *bus_range = NULL; + struct pci_config_window *cfg; + unsigned int bus_shift = ops->bus_shift;
- err = of_address_to_resource(np, 0, &pci->cfg.res); + /* Parse our PCI ranges and request their resources */ + err = gen_pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + if (err) + goto err_out; + + err = of_address_to_resource(dev->of_node, 0, &cfgres); if (err) { dev_err(dev, "missing "reg" property\n"); - return err; + goto err_out; }
/* Limit the bus-range to fit within reg */ - bus_max = pci->cfg.bus_range->start + - (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; - pci->cfg.bus_range->end = min_t(resource_size_t, - pci->cfg.bus_range->end, bus_max); - - pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range), - sizeof(*pci->cfg.win), GFP_KERNEL); - if (!pci->cfg.win) - return -ENOMEM; - - /* Map our Configuration Space windows */ - if (!devm_request_mem_region(dev, pci->cfg.res.start, - resource_size(&pci->cfg.res), - "Configuration Space")) - return -ENOMEM; - - bus_range = pci->cfg.bus_range; - for (busn = bus_range->start; busn <= bus_range->end; ++busn) { - u32 idx = busn - bus_range->start; - - pci->cfg.win[idx] = devm_ioremap(dev, - pci->cfg.res.start + idx * sz, - sz); - if (!pci->cfg.win[idx]) - return -ENOMEM; + bus_range->end = min(bus_range->end, + bus_range->start + (resource_size(&cfgres) >> bus_shift) - 1); + + cfg = pci_generic_ecam_create(dev, cfgres.start, bus_range->start, + bus_range->end, ops); + if (IS_ERR(cfg)) { + err = PTR_ERR(cfg); + goto err_out; }
- return 0; + cfg->priv = priv; + err = devm_add_action(dev, gen_pci_generic_unmap_cfg, cfg); + if (err) { + gen_pci_generic_unmap_cfg(cfg); + goto err_out; + } + return cfg; + +err_out: + pci_free_resource_list(resources); + return ERR_PTR(err); }
int pci_host_common_probe(struct platform_device *pdev, - struct gen_pci *pci) + struct pci_generic_ecam_ops *ops, void *priv) + { - int err; const char *type; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct pci_bus *bus, *child; + struct pci_config_window *cfg; + struct list_head resources;
type = of_get_property(np, "device_type", NULL); if (!type || strcmp(type, "pci")) { @@ -147,29 +145,18 @@ int pci_host_common_probe(struct platform_device *pdev,
of_pci_check_probe_only();
- pci->host.dev.parent = dev; - INIT_LIST_HEAD(&pci->host.windows); - INIT_LIST_HEAD(&pci->resources); - - /* Parse our PCI ranges and request their resources */ - err = gen_pci_parse_request_of_pci_ranges(pci); - if (err) - return err; - /* Parse and map our Configuration Space windows */ - err = gen_pci_parse_map_cfg_windows(pci); - if (err) { - gen_pci_release_of_pci_ranges(pci); - return err; - } + INIT_LIST_HEAD(&resources); + cfg = gen_pci_init(dev, &resources, ops, priv); + if (IS_ERR(cfg)) + return PTR_ERR(cfg);
/* Do not reassign resources if probe only */ if (!pci_has_flag(PCI_PROBE_ONLY)) pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
- - bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start, - &pci->cfg.ops->ops, pci, &pci->resources); + bus = pci_scan_root_bus(dev, cfg->bus_start, &ops->ops, cfg, + &resources); if (!bus) { dev_err(dev, "Scanning rootbus failed"); return -ENODEV; diff --git a/drivers/pci/host/pci-host-common.h b/drivers/pci/host/pci-host-common.h deleted file mode 100644 index 09f3fa0..00000000 --- a/drivers/pci/host/pci-host-common.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - * Copyright (C) 2014 ARM Limited - * - * Author: Will Deacon will.deacon@arm.com - */ - -#ifndef _PCI_HOST_COMMON_H -#define _PCI_HOST_COMMON_H - -#include <linux/kernel.h> -#include <linux/platform_device.h> - -struct gen_pci_cfg_bus_ops { - u32 bus_shift; - struct pci_ops ops; -}; - -struct gen_pci_cfg_windows { - struct resource res; - struct resource *bus_range; - void __iomem **win; - - struct gen_pci_cfg_bus_ops *ops; -}; - -struct gen_pci { - struct pci_host_bridge host; - struct gen_pci_cfg_windows cfg; - struct list_head resources; -}; - -int pci_host_common_probe(struct platform_device *pdev, - struct gen_pci *pci); - -#endif /* _PCI_HOST_COMMON_H */ diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index e8aa78f..e34718a 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -25,41 +25,12 @@ #include <linux/of_pci.h> #include <linux/platform_device.h>
-#include "pci-host-common.h" +#include "../ecam.h"
-static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, - unsigned int devfn, - int where) -{ - struct gen_pci *pci = bus->sysdata; - resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 8) | where); -} - -static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = { +static struct pci_generic_ecam_ops gen_pci_cfg_cam_bus_ops = { .bus_shift = 16, .ops = { - .map_bus = gen_pci_map_cfg_bus_cam, - .read = pci_generic_config_read, - .write = pci_generic_config_write, - } -}; - -static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, - unsigned int devfn, - int where) -{ - struct gen_pci *pci = bus->sysdata; - resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 12) | where); -} - -static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = { - .bus_shift = 20, - .ops = { - .map_bus = gen_pci_map_cfg_bus_ecam, + .map_bus = pci_generic_ecam_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, } @@ -70,25 +41,22 @@ static const struct of_device_id gen_pci_of_match[] = { .data = &gen_pci_cfg_cam_bus_ops },
{ .compatible = "pci-host-ecam-generic", - .data = &gen_pci_cfg_ecam_bus_ops }, + .data = &pci_generic_ecam_default_ops },
{ }, }; + MODULE_DEVICE_TABLE(of, gen_pci_of_match);
static int gen_pci_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; const struct of_device_id *of_id; - struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - - if (!pci) - return -ENOMEM; + struct pci_generic_ecam_ops *ops;
- of_id = of_match_node(gen_pci_of_match, dev->of_node); - pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data; + of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node); + ops = (struct pci_generic_ecam_ops *)of_id->data;
- return pci_host_common_probe(pdev, pci); + return pci_host_common_probe(pdev, ops, NULL); }
static struct platform_driver gen_pci_driver = { diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c index d71935cb..6b8d4b4 100644 --- a/drivers/pci/host/pci-thunder-ecam.c +++ b/drivers/pci/host/pci-thunder-ecam.c @@ -13,18 +13,7 @@ #include <linux/of.h> #include <linux/platform_device.h>
-#include "pci-host-common.h" - -/* Mapping is standard ECAM */ -static void __iomem *thunder_ecam_map_bus(struct pci_bus *bus, - unsigned int devfn, - int where) -{ - struct gen_pci *pci = bus->sysdata; - resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 12) | where); -} +#include "../ecam.h"
static void set_val(u32 v, int where, int size, u32 *val) { @@ -99,7 +88,7 @@ static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus, static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { - struct gen_pci *pci = bus->sysdata; + struct pci_config_window *cfg = bus->sysdata; int where_a = where & ~3; void __iomem *addr; u32 node_bits; @@ -129,7 +118,7 @@ static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn, * the config space access window. Since we are working with * the high-order 32 bits, shift everything down by 32 bits. */ - node_bits = (pci->cfg.res.start >> 32) & (1 << 12); + node_bits = (cfg->cfgaddr >> 32) & (1 << 12);
v |= node_bits; set_val(v, where, size, val); @@ -358,36 +347,24 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn, return pci_generic_config_write(bus, devfn, where, size, val); }
-static struct gen_pci_cfg_bus_ops thunder_ecam_bus_ops = { +static struct pci_generic_ecam_ops thunder_ecam_bus_ops = { .bus_shift = 20, .ops = { - .map_bus = thunder_ecam_map_bus, + .map_bus = pci_generic_ecam_map_bus, .read = thunder_ecam_config_read, .write = thunder_ecam_config_write, } };
static const struct of_device_id thunder_ecam_of_match[] = { - { .compatible = "cavium,pci-host-thunder-ecam", - .data = &thunder_ecam_bus_ops }, - + { .compatible = "cavium,pci-host-thunder-ecam" }, { }, }; MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
static int thunder_ecam_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - const struct of_device_id *of_id; - struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - - if (!pci) - return -ENOMEM; - - of_id = of_match_node(thunder_ecam_of_match, dev->of_node); - pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data; - - return pci_host_common_probe(pdev, pci); + return pci_host_common_probe(pdev, &thunder_ecam_bus_ops, NULL); }
static struct platform_driver thunder_ecam_driver = { diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c index cabb92a..52fe0ff 100644 --- a/drivers/pci/host/pci-thunder-pem.c +++ b/drivers/pci/host/pci-thunder-pem.c @@ -20,34 +20,22 @@ #include <linux/of_pci.h> #include <linux/platform_device.h>
-#include "pci-host-common.h" +#include "../ecam.h"
#define PEM_CFG_WR 0x28 #define PEM_CFG_RD 0x30
struct thunder_pem_pci { - struct gen_pci gen_pci; u32 ea_entry[3]; void __iomem *pem_reg_base; };
-static void __iomem *thunder_pem_map_bus(struct pci_bus *bus, - unsigned int devfn, int where) -{ - struct gen_pci *pci = bus->sysdata; - resource_size_t idx = bus->number - pci->cfg.bus_range->start; - - return pci->cfg.win[idx] + ((devfn << 16) | where); -} - static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { u64 read_val; - struct thunder_pem_pci *pem_pci; - struct gen_pci *pci = bus->sysdata; - - pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci); + struct pci_config_window *cfg = bus->sysdata; + struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
if (devfn != 0 || where >= 2048) { *val = ~0; @@ -132,17 +120,17 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { - struct gen_pci *pci = bus->sysdata; + struct pci_config_window *cfg = bus->sysdata;
- if (bus->number < pci->cfg.bus_range->start || - bus->number > pci->cfg.bus_range->end) + if (bus->number < cfg->bus_start || + bus->number > cfg->bus_end) return PCIBIOS_DEVICE_NOT_FOUND;
/* * The first device on the bus is the PEM PCIe bridge. * Special case its config access. */ - if (bus->number == pci->cfg.bus_range->start) + if (bus->number == cfg->bus_start) return thunder_pem_bridge_read(bus, devfn, where, size, val);
return pci_generic_config_read(bus, devfn, where, size, val); @@ -187,12 +175,11 @@ static u32 thunder_pem_bridge_w1c_bits(int where) static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { - struct gen_pci *pci = bus->sysdata; - struct thunder_pem_pci *pem_pci; + struct pci_config_window *cfg = bus->sysdata; + struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv; u64 write_val, read_val; u32 mask = 0;
- pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
if (devfn != 0 || where >= 2048) return PCIBIOS_DEVICE_NOT_FOUND; @@ -256,35 +243,33 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { - struct gen_pci *pci = bus->sysdata; + struct pci_config_window *cfg = bus->sysdata;
- if (bus->number < pci->cfg.bus_range->start || - bus->number > pci->cfg.bus_range->end) + if (bus->number < cfg->bus_start || + bus->number > cfg->bus_end) return PCIBIOS_DEVICE_NOT_FOUND; /* * The first device on the bus is the PEM PCIe bridge. * Special case its config access. */ - if (bus->number == pci->cfg.bus_range->start) + if (bus->number == cfg->bus_start) return thunder_pem_bridge_write(bus, devfn, where, size, val);
return pci_generic_config_write(bus, devfn, where, size, val); }
-static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = { +static struct pci_generic_ecam_ops thunder_pem_bus_ops = { .bus_shift = 24, .ops = { - .map_bus = thunder_pem_map_bus, + .map_bus = pci_generic_ecam_map_bus, .read = thunder_pem_config_read, .write = thunder_pem_config_write, } };
static const struct of_device_id thunder_pem_of_match[] = { - { .compatible = "cavium,pci-host-thunder-pem", - .data = &thunder_pem_bus_ops }, - + { .compatible = "cavium,pci-host-thunder-pem" }, { }, }; MODULE_DEVICE_TABLE(of, thunder_pem_of_match); @@ -292,7 +277,6 @@ MODULE_DEVICE_TABLE(of, thunder_pem_of_match); static int thunder_pem_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id; resource_size_t bar4_start; struct resource *res_pem; struct thunder_pem_pci *pem_pci; @@ -301,9 +285,6 @@ static int thunder_pem_probe(struct platform_device *pdev) if (!pem_pci) return -ENOMEM;
- of_id = of_match_node(thunder_pem_of_match, dev->of_node); - pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data; - /* * The second register range is the PEM bridge to the PCIe * bus. It has a different config access method than those @@ -330,7 +311,7 @@ static int thunder_pem_probe(struct platform_device *pdev) pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
- return pci_host_common_probe(pdev, &pem_pci->gen_pci); + return pci_host_common_probe(pdev, &thunder_pem_bus_ops, pem_pci); }
static struct platform_driver thunder_pem_driver = {
On 04/11/2016 03:45 PM, Jayachandran C wrote:
Use functions provided by drivers/pci/ecam.h for mapping the config space in drivers/pci/host/pci-host-common.c, and update its users to use 'struct pci_config_window' and 'struct pci_generic_ecam_ops'
The changes are mostly to use 'struct pci_config_window' in place of 'struct gen_pci'. Some of the fields of gen_pci were only used temporarily and can be eliminated by using local variables or function arguments, these are not carried over to struct pci_config_window.
pci-thunder-ecam.c and pci-thunder-pem.c are the only users of the pci_host_common_probe function and the gen_pci structure, these have been updated to use the new API as well.
The patch does not introduce any functional changes other than a very minor one: with the new code, on 64-bit platforms, we do just a single ioremap for the whole config space.
Signed-off-by: Jayachandran C jchandra@broadcom.com
Tested-by: David Daney david.daney@cavium.com
However, ...
drivers/pci/ecam.h | 5 ++ drivers/pci/host/Kconfig | 1 + drivers/pci/host/pci-host-common.c | 121 ++++++++++++++++-------------------- drivers/pci/host/pci-host-common.h | 47 -------------- drivers/pci/host/pci-host-generic.c | 50 +++------------ drivers/pci/host/pci-thunder-ecam.c | 37 +++-------- drivers/pci/host/pci-thunder-pem.c | 53 +++++-----------
... The patch conflicts with a bug-fix patch I just sent to pci-thunder-pem.c, so there will be a race to see who gets in first.
Also, I don't know if it would make sense to split out the pci-thunder-* changes to a separate patch.
7 files changed, 93 insertions(+), 221 deletions(-) delete mode 100644 drivers/pci/host/pci-host-common.h
[...]
Hi David,
On Tue, Apr 12, 2016 at 6:04 AM, David Daney ddaney@caviumnetworks.com wrote:
On 04/11/2016 03:45 PM, Jayachandran C wrote:
Use functions provided by drivers/pci/ecam.h for mapping the config space in drivers/pci/host/pci-host-common.c, and update its users to use 'struct pci_config_window' and 'struct pci_generic_ecam_ops'
The changes are mostly to use 'struct pci_config_window' in place of 'struct gen_pci'. Some of the fields of gen_pci were only used temporarily and can be eliminated by using local variables or function arguments, these are not carried over to struct pci_config_window.
pci-thunder-ecam.c and pci-thunder-pem.c are the only users of the pci_host_common_probe function and the gen_pci structure, these have been updated to use the new API as well.
The patch does not introduce any functional changes other than a very minor one: with the new code, on 64-bit platforms, we do just a single ioremap for the whole config space.
Signed-off-by: Jayachandran C jchandra@broadcom.com
Tested-by: David Daney david.daney@cavium.com
Thanks.
However, ...
drivers/pci/ecam.h | 5 ++ drivers/pci/host/Kconfig | 1 + drivers/pci/host/pci-host-common.c | 121 ++++++++++++++++-------------------- drivers/pci/host/pci-host-common.h | 47 -------------- drivers/pci/host/pci-host-generic.c | 50 +++------------ drivers/pci/host/pci-thunder-ecam.c | 37 +++-------- drivers/pci/host/pci-thunder-pem.c | 53 +++++-----------
... The patch conflicts with a bug-fix patch I just sent to pci-thunder-pem.c, so there will be a race to see who gets in first.
I would expect this patchset to take more time than the bugfix. I will update the patches when the fix is queued to -next.
Also, I don't know if it would make sense to split out the pci-thunder-* changes to a separate patch.
In this case, I think splitting the pci-thunder-* will make it more complex than just switching everything to ecam.h in one go.
If the patch needs to be split, I can move some of the simplification that was done to gen_pci structure and pci-host-common to a separate patch.
JC.
Add a generic ACPI based PCI host controller, and provide a config option ACPI_PCI_HOST_GENERIC to enable it.
The implementation selects PCI_GENERIC_ECAM and uses functions from drivers/pci/ecam.h to create and access ECAM mappings. It also selects PCI_MMCONFIG and implements the pci_mmcfg_late_init() function to parse and map entries in the MCFG table.
The implementation of pci_acpi_scan_root() looks up the saved mappings and sets up a new mapping if needed. Generic PCI functions are used for accessing config space.
Signed-off-by: Jayachandran C jchandra@broadcom.com --- drivers/acpi/Kconfig | 9 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pci_gen_host.c | 258 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 drivers/acpi/pci_gen_host.c
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 82b96ee..f178f2e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -343,6 +343,15 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N.
+config ACPI_PCI_HOST_GENERIC + bool "Generic ACPI based PCI controller" + depends on ARM64 + select PCI_MMCONFIG + select PCI_GENERIC_ECAM + help + Say Y if you want the generic ACPI based PCI controller + implementation. + config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index edeb2d1..eaf429c 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o +obj-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_gen_host.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI) += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o diff --git a/drivers/acpi/pci_gen_host.c b/drivers/acpi/pci_gen_host.c new file mode 100644 index 00000000..bd31faa --- /dev/null +++ b/drivers/acpi/pci_gen_host.c @@ -0,0 +1,258 @@ +/* + * Copyright 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> + +#include "../pci/ecam.h" + +#define PREFIX "ACPI: " + +/* + * Array of config windows from MCFG table, parsed and created by + * pci_mmcfg_late_init() at boot + */ +struct pci_config_window **cfgarr; + +/* ACPI info for generic ACPI PCI controller */ +struct acpi_pci_generic_root_info { + struct acpi_pci_root_info common; + struct pci_config_window *cfg; /* config space mapping */ + bool mcfg_added; +}; + +/* find the entry in cfgarr which contains range bus_start..bus_end */ +static int mcfg_lookup(u16 seg, u8 bus_start, u8 bus_end) +{ + struct pci_config_window *cfg; + int i; + + if (!cfgarr) + return -ENOENT; + + for (i = 0; cfgarr[i]; i++) { + cfg = cfgarr[i]; + if (seg != cfg->domain) + continue; + if (bus_start >= cfg->bus_start && bus_start <= cfg->bus_end) + return (bus_end <= cfg->bus_end) ? i : -EINVAL; + else if (bus_end >= cfg->bus_start && bus_end <= cfg->bus_end) + return -EINVAL; + } + return -ENOENT; +} + +/* + * create a new mapping + */ +static struct pci_config_window *pci_acpi_ecam_create(struct device *dev, + phys_addr_t addr, u16 seg, u8 bus_start, u8 bus_end) +{ + struct pci_config_window *cfg; + int ret; + + cfg = pci_generic_ecam_create(dev, addr, bus_start, bus_end, + &pci_generic_ecam_default_ops); + if (IS_ERR(cfg)) { + ret = PTR_ERR(cfg); + pr_err("%04x:%02x-%02x error %d mapping CAM\n", seg, + bus_start, bus_end, ret); + return NULL; + } + cfg->domain = seg; + return cfg; +} + +/* + * Lookup the bus range for the domain in MCFG, and set up config space + * mapping. + */ +static int pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root, + struct acpi_pci_generic_root_info *ri) +{ + struct pci_config_window *cfg; + u16 seg = root->segment; + u8 bus_start = root->secondary.start; + u8 bus_end = root->secondary.end; + phys_addr_t addr = root->mcfg_addr; + struct acpi_device *adev = root->device; + int ret; + + ret = mcfg_lookup(seg, bus_start, bus_end); + if (ret == -ENOENT) { + if (addr == 0) { + pr_err("%04x:%02x-%02x mcfg lookup failed\n", seg, + bus_start, bus_end); + return ret; + } + cfg = pci_acpi_ecam_create(&adev->dev, addr, seg, bus_start, + bus_end); + if (!cfg) + return ret; + } else if (ret < 0) { + pr_err("%04x:%02x-%02x bus range error (%d)\n", seg, bus_start, + bus_end, ret); + return ret; + } else { + cfg = cfgarr[ret]; + if (addr == 0) + addr = cfg->cfgaddr; + if (bus_start != cfg->bus_start) { + pr_err("%04x:%02x-%02x bus range mismatch %02x\n", + seg, bus_start, bus_end, cfg->bus_start); + return -EINVAL; + } + if (addr != cfg->cfgaddr) { + pr_warn("%04x:%02x-%02x addr mismatch, ignoring MCFG\n", + seg, bus_start, bus_end); + } else if (bus_end != cfg->bus_end) { + pr_warn("%04x:%02x-%02x bus end mismatch using %02x\n", + seg, bus_start, bus_end, cfg->bus_end); + bus_end = cfg->bus_end; + } + } + ri->cfg = cfg; + ri->mcfg_added = (ret >= 0); + + return 0; +} + +/* release_info: free resrouces allocated by init_info */ +static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci) +{ + struct acpi_pci_generic_root_info *ri; + + ri = container_of(ci, struct acpi_pci_generic_root_info, common); + if (!ri->mcfg_added) + pci_generic_ecam_free(ri->cfg); + kfree(ri); +} + +static struct acpi_pci_root_ops acpi_pci_root_ops = { + .release_info = pci_acpi_generic_release_info, +}; + +/* Interface called from ACPI code to setup PCI host controller */ +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + int node = acpi_get_node(root->device->handle); + struct acpi_pci_generic_root_info *ri; + struct pci_bus *bus, *child; + int err; + + ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node); + if (!ri) + return NULL; + + err = pci_acpi_setup_ecam_mapping(root, ri); + if (err) + return NULL; + + acpi_pci_root_ops.pci_ops = &ri->cfg->ops->ops; + bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common, + ri->cfg); + if (!bus) + return NULL; + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +/* handle MCFG table entries */ +static __init int handle_mcfg(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *mptr; + struct pci_config_window *cfg; + int i, j, n; + + if (!header) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *)header; + mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; + n = (header->length - sizeof(*mcfg)) / sizeof(*mptr); + if (n <= 0 || n > 255) { + pr_err(PREFIX " MCFG has incorrect entries (%d).\n", n); + return -EINVAL; + } + + cfgarr = kcalloc(n + 1, sizeof(*cfgarr), GFP_KERNEL); + if (!cfgarr) + return -ENOMEM; + + for (i = 0, j = 0; i < n; i++) { + cfg = pci_acpi_ecam_create(NULL, mptr->address, + mptr->pci_segment, mptr->start_bus_number, + mptr->end_bus_number); + if (!cfg) + continue; + cfgarr[j++] = cfg; + } + + if (j == 0) { + kfree(cfgarr); + cfgarr = NULL; + return -ENOENT; + } + cfgarr[j] = NULL; + return 0; +} + +/* Interface called by ACPI - parse and save MCFG table */ +void __init pci_mmcfg_late_init(void) +{ + int i, err; + + err = acpi_sfi_table_parse(ACPI_SIG_MCFG, handle_mcfg); + if (err) { + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); + } else if (cfgarr == NULL) { + pr_err(PREFIX " Failed to parse MCFG, no valid entries.\n"); + } else { + for (i = 0; cfgarr[i]; i++) + ; + pr_info(PREFIX " MCFG table at loaded, %d entries\n", i); + } +} + +/* Raw operations, works only for MCFG entries with an associated bus */ +int raw_pci_read(unsigned int domain, unsigned int busn, unsigned int devfn, + int reg, int len, u32 *val) +{ + struct pci_bus *bus = pci_find_bus(domain, busn); + + if (!bus) + return PCIBIOS_DEVICE_NOT_FOUND; + return bus->ops->read(bus, devfn, reg, len, val); +} + +int raw_pci_write(unsigned int domain, unsigned int busn, unsigned int devfn, + int reg, int len, u32 val) +{ + struct pci_bus *bus = pci_find_bus(domain, busn); + + if (!bus) + return PCIBIOS_DEVICE_NOT_FOUND; + return bus->ops->write(bus, devfn, reg, len, val); +}
Hi Jayachandran,
[auto build test ERROR on pci/next] [also build test ERROR on v4.6-rc3 next-20160411] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/Jayachandran-C/ACPI-based-PCI-host-... base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next config: arm64-allyesconfig (attached as .config) reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/ma... -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm64
All errors (new ones prefixed by >>):
drivers/xen/pci.c:31:25: fatal error: asm/pci_x86.h: No such file or directory
#include <asm/pci_x86.h> ^ compilation terminated.
vim +31 drivers/xen/pci.c
e28c31a9 Weidong Han 2010-10-27 15 * Place - Suite 330, Boston, MA 02111-1307 USA. e28c31a9 Weidong Han 2010-10-27 16 * e28c31a9 Weidong Han 2010-10-27 17 * Author: Weidong Han weidong.han@intel.com e28c31a9 Weidong Han 2010-10-27 18 */ e28c31a9 Weidong Han 2010-10-27 19 e28c31a9 Weidong Han 2010-10-27 20 #include <linux/pci.h> 55e901fc Jan Beulich 2011-09-22 21 #include <linux/acpi.h> 0b97b03d Ross Lagerwall 2015-04-09 22 #include <linux/pci-acpi.h> e28c31a9 Weidong Han 2010-10-27 23 #include <xen/xen.h> e28c31a9 Weidong Han 2010-10-27 24 #include <xen/interface/physdev.h> e28c31a9 Weidong Han 2010-10-27 25 #include <xen/interface/xen.h> e28c31a9 Weidong Han 2010-10-27 26 e28c31a9 Weidong Han 2010-10-27 27 #include <asm/xen/hypervisor.h> e28c31a9 Weidong Han 2010-10-27 28 #include <asm/xen/hypercall.h> e28c31a9 Weidong Han 2010-10-27 29 #include "../pci/pci.h" b7ef4a6d Ben Hutchings 2013-12-31 30 #ifdef CONFIG_PCI_MMCONFIG 8deb3eb1 Konrad Rzeszutek Wilk 2013-10-25 @31 #include <asm/pci_x86.h> b7ef4a6d Ben Hutchings 2013-12-31 32 #endif e28c31a9 Weidong Han 2010-10-27 33 55e901fc Jan Beulich 2011-09-22 34 static bool __read_mostly pci_seg_supported = true; 55e901fc Jan Beulich 2011-09-22 35 e28c31a9 Weidong Han 2010-10-27 36 static int xen_add_device(struct device *dev) e28c31a9 Weidong Han 2010-10-27 37 { e28c31a9 Weidong Han 2010-10-27 38 int r; e28c31a9 Weidong Han 2010-10-27 39 struct pci_dev *pci_dev = to_pci_dev(dev);
:::::: The code at line 31 was first introduced by commit :::::: 8deb3eb1461e4cb136c88d03ec5a6729ccf2f933 xen/mcfg: Call PHYSDEVOP_pci_mmcfg_reserved for MCFG areas.
:::::: TO: Konrad Rzeszutek Wilk konrad.wilk@oracle.com :::::: CC: Konrad Rzeszutek Wilk konrad.wilk@oracle.com
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On 4/11/2016 6:45 PM, Jayachandran C wrote:
+/* find the entry in cfgarr which contains range bus_start..bus_end */ +static int mcfg_lookup(u16 seg, u8 bus_start, u8 bus_end) +{
- struct pci_config_window *cfg;
- int i;
- if (!cfgarr)
return -ENOENT;
- for (i = 0; cfgarr[i]; i++) {
cfg = cfgarr[i];
I see that you are allocating an array of cfgarr to keep the MCFG table entries. The above way of checking the number of entries is not correct.
You should keep track of the number of entries you found out globally instead of relying an element being NULL or not.
If you exceed the array size, you may or may not be lucky to find another entry in memory.
On 4/14/2016 11:53 AM, Sinan Kaya wrote:
On 4/11/2016 6:45 PM, Jayachandran C wrote:
+/* find the entry in cfgarr which contains range bus_start..bus_end */ +static int mcfg_lookup(u16 seg, u8 bus_start, u8 bus_end) +{
- struct pci_config_window *cfg;
- int i;
- if (!cfgarr)
return -ENOENT;
- for (i = 0; cfgarr[i]; i++) {
cfg = cfgarr[i];
I see that you are allocating an array of cfgarr to keep the MCFG table entries. The above way of checking the number of entries is not correct.
You should keep track of the number of entries you found out globally instead of relying an element being NULL or not.
If you exceed the array size, you may or may not be lucky to find another entry in memory.
I see now that you are allocating an extra element in memory. Still, looping to find out the number of elements didn't quite look good to me.
+ for (i = 0; cfgarr[i]; i++) + ; + pr_info(PREFIX " MCFG table at loaded, %d entries\n", i);