From 1d09d57728fedd7357ee22d407ae8aae6477757f Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:41:36 +0100 Subject: PCI: Mark expected switch fall-through There is a plan to build the kernel with -Wimplicit-fallthrough and these places in the code produced warnings (W=1). Fix them up. Signed-off-by: Mathieu Malaterre [bhelgaas: squash into one patch, drop extra changelog detail] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- drivers/pci/quirks.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..94069fc35e91 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -861,7 +861,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) need_restore = true; - /* Fall-through: force to D0 */ + /* Fall-through - force to D0 */ default: pmcsr = 0; break; @@ -2260,7 +2260,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup) case PCI_D2: if (pci_no_d1d2(dev)) break; - /* else: fall through */ + /* else, fall through */ default: target_state = state; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b0a413f3f7ca..9fabcef345ed 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2138,7 +2138,7 @@ static void quirk_netmos(struct pci_dev *dev) if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return; - /* else: fall through */ + /* else, fall through */ case PCI_DEVICE_ID_NETMOS_9735: case PCI_DEVICE_ID_NETMOS_9745: case PCI_DEVICE_ID_NETMOS_9845: -- cgit v1.2.3 From 25bd879ec16ad3b83a5b1c3f16faa55e696bfccb Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 7 Jan 2019 16:09:40 +0300 Subject: PCI: pciehp: Assign ctrl->slot_ctrl before writing it to hardware Shameerali reported that running v4.20-rc1 as QEMU guest, the PCIe hotplug port times out during boot: pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x03f1 (issued 1016 msec ago) pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x03f1 (issued 1024 msec ago) pciehp 0000:00:01.0:pcie004: Failed to check link status pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x02f1 (issued 2520 msec ago) The issue was bisected down to commit 720d6a671a6e ("PCI: pciehp: Do not handle events if interrupts are masked") and was further analyzed by the reporter to be caused by the fact that pciehp first updates the hardware and only then cache the ctrl->slot_ctrl in pcie_do_write_cmd(). If the interrupt happens before we cache the value, pciehp_isr() reads value 0 and decides that the interrupt was not meant for it causing the above timeout to trigger. Fix by moving ctrl->slot_ctrl assignment to happen before it is written to the hardware. Fixes: 720d6a671a6e ("PCI: pciehp: Do not handle events if interrupts are masked") Link: https://lore.kernel.org/linux-pci/5FC3163CFD30C246ABAA99954A238FA8387DD344@FRAEML521-MBX.china.huawei.com Reported-by: Shameerali Kolothum Thodi Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7dd443aea5a5..cd9eae650aa5 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -156,9 +156,9 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); + ctrl->slot_ctrl = slot_ctrl; pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); ctrl->cmd_started = jiffies; - ctrl->slot_ctrl = slot_ctrl; /* * Controllers with the Intel CF118 and similar errata advertise -- cgit v1.2.3 From 51c48b310183ab6ba5419edfc6a8de889cc04521 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 19 Jan 2019 11:35:04 -0600 Subject: PCI: Probe bridge window attributes once at enumeration-time pci_bridge_check_ranges() determines whether a bridge supports the optional I/O and prefetchable memory windows and sets the flag bits in the bridge resources. This *could* be done once during enumeration except that the resource allocation code completely clears the flag bits, e.g., in the pci_assign_unassigned_bridge_resources() path. The problem with pci_bridge_check_ranges() in the resource allocation path is that we may allocate resources after devices have been claimed by drivers, and pci_bridge_check_ranges() *changes* the window registers to determine whether they're writable. This may break concurrent accesses to devices behind the bridge. Add a new pci_read_bridge_windows() to determine whether a bridge supports the optional windows, call it once during enumeration, remember the results, and change pci_bridge_check_ranges() so it doesn't touch the bridge windows but sets the flag bits based on those remembered results. Link: https://lore.kernel.org/linux-pci/1506151482-113560-1-git-send-email-wangzhou1@hisilicon.com Link: https://lists.gnu.org/archive/html/qemu-devel/2018-12/msg02082.html Reported-by: Yandong Xu Tested-by: Yandong Xu Signed-off-by: Bjorn Helgaas Cc: Michael S. Tsirkin Cc: Sagi Grimberg Cc: Ofer Hayut Cc: Roy Shterman Cc: Keith Busch Cc: Zhou Wang --- drivers/pci/probe.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/setup-bus.c | 45 ++++-------------------------------------- include/linux/pci.h | 3 +++ 3 files changed, 59 insertions(+), 41 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..2ef8b954c65a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -348,6 +348,57 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) } } +static void pci_read_bridge_windows(struct pci_dev *bridge) +{ + u16 io; + u32 pmem, tmp; + + pci_read_config_word(bridge, PCI_IO_BASE, &io); + if (!io) { + pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); + pci_read_config_word(bridge, PCI_IO_BASE, &io); + pci_write_config_word(bridge, PCI_IO_BASE, 0x0); + } + if (io) + bridge->io_window = 1; + + /* + * DECchip 21050 pass 2 errata: the bridge may miss an address + * disconnect boundary by one PCI data phase. Workaround: do not + * use prefetching on this device. + */ + if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) + return; + + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); + if (!pmem) { + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, + 0xffe0fff0); + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); + } + if (!pmem) + return; + + bridge->pref_window = 1; + + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + + /* + * Bridge claims to have a 64-bit prefetchable memory + * window; verify that the upper bits are actually + * writable. + */ + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, + 0xffffffff); + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem); + if (tmp) + bridge->pref_64_window = 1; + } +} + static void pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; @@ -1739,6 +1790,7 @@ int pci_setup_device(struct pci_dev *dev) pci_read_irq(dev); dev->transparent = ((dev->class & 0xff) == 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); + pci_read_bridge_windows(dev); set_pcie_hotplug_bridge(dev); pos = pci_find_capability(dev, PCI_CAP_ID_SSVID); if (pos) { diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ed960436df5e..1941bb0a6c13 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -735,58 +735,21 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i) base/limit registers must be read-only and read as 0. */ static void pci_bridge_check_ranges(struct pci_bus *bus) { - u16 io; - u32 pmem; struct pci_dev *bridge = bus->self; - struct resource *b_res; + struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; - b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; b_res[1].flags |= IORESOURCE_MEM; - pci_read_config_word(bridge, PCI_IO_BASE, &io); - if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); - pci_read_config_word(bridge, PCI_IO_BASE, &io); - pci_write_config_word(bridge, PCI_IO_BASE, 0x0); - } - if (io) + if (bridge->io_window) b_res[0].flags |= IORESOURCE_IO; - /* DECchip 21050 pass 2 errata: the bridge may miss an address - disconnect boundary by one PCI data phase. - Workaround: do not use prefetching on this device. */ - if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) - return; - - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - if (!pmem) { - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xffe0fff0); - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); - } - if (pmem) { + if (bridge->pref_window) { b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == - PCI_PREF_RANGE_TYPE_64) { + if (bridge->pref_64_window) { b_res[2].flags |= IORESOURCE_MEM_64; b_res[2].flags |= PCI_PREF_RANGE_TYPE_64; } } - - /* double check if bridge does support 64 bit pref */ - if (b_res[2].flags & IORESOURCE_MEM_64) { - u32 mem_base_hi, tmp; - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, - &mem_base_hi); - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - 0xffffffff); - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); - if (!tmp) - b_res[2].flags &= ~IORESOURCE_MEM_64; - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - mem_base_hi); - } } /* Helper function for sizing routines: find first available diff --git a/include/linux/pci.h b/include/linux/pci.h index 65f1d8c2f082..40b327b814aa 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -373,6 +373,9 @@ struct pci_dev { bool match_driver; /* Skip attaching driver */ unsigned int transparent:1; /* Subtractive decode bridge */ + unsigned int io_window:1; /* Bridge has I/O window */ + unsigned int pref_window:1; /* Bridge has pref mem window */ + unsigned int pref_64_window:1; /* Pref mem window is 64-bit */ unsigned int multifunction:1; /* Multi-function device */ unsigned int is_busmaster:1; /* Is busmaster */ -- cgit v1.2.3 From 83a50d3a235b9c074f30af105b9d9751b55edb70 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:34 -0600 Subject: PCI: Use of_node_name_eq() for node name comparisons Convert string compares of DT node names to use of_node_name_eq() helper instead. This removes direct access to the node name pointer. Signed-off-by: Rob Herring [bhelgaas: drop similar rpaphp_core.c change to avoid merge conflict] Signed-off-by: Bjorn Helgaas --- drivers/pci/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 4c4217d0c3f1..3d32da15c215 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -113,7 +113,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, * a fake root for all functions of a multi-function * device we go down them as well. */ - if (!strcmp(node->name, "multifunc-device")) { + if (of_node_name_eq(node, "multifunc-device")) { for_each_child_of_node(node, node2) { if (__of_pci_pci_compare(node2, devfn)) { of_node_put(node); -- cgit v1.2.3 From 807ffb1e1eabbcdcd46494ee415317aa80ed415c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 28 Jan 2019 13:57:28 +0200 Subject: PCI/AER: Use match_string() helper to simplify the code match_string() returns the array index of a matching string. Use it instead of the open-coded implementation. Signed-off-by: Andy Shevchenko Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index fed29de783e0..f8fc2114ad39 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -117,7 +117,7 @@ bool pci_aer_available(void) static int ecrc_policy = ECRC_POLICY_DEFAULT; -static const char *ecrc_policy_str[] = { +static const char * const ecrc_policy_str[] = { [ECRC_POLICY_DEFAULT] = "bios", [ECRC_POLICY_OFF] = "off", [ECRC_POLICY_ON] = "on" @@ -203,11 +203,8 @@ void pcie_ecrc_get_policy(char *str) { int i; - for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) - if (!strncmp(str, ecrc_policy_str[i], - strlen(ecrc_policy_str[i]))) - break; - if (i >= ARRAY_SIZE(ecrc_policy_str)) + i = match_string(ecrc_policy_str, ARRAY_SIZE(ecrc_policy_str), str); + if (i < 0) return; ecrc_policy = i; -- cgit v1.2.3 From 2a727f6091372231281efaa8a5f56ee79509fd83 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 22:49:10 +0100 Subject: PCI: ibmphp: Turn semaphores into completions or mutexes The sem_exit variable is conceptually a completion, so it should be called that. Similarly, the semOperations semaphore is a simple mutex, and can be changed into that, respectively. With both converted, the ibmphp_hpc_initvars() function is no longer used and can be removed. Signed-off-by: Arnd Bergmann Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/ibmphp.h | 1 - drivers/pci/hotplug/ibmphp_core.c | 2 -- drivers/pci/hotplug/ibmphp_hpc.c | 47 ++++++++++++--------------------------- 3 files changed, 14 insertions(+), 36 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h index b89f850c3a4e..e90a4ebf6550 100644 --- a/drivers/pci/hotplug/ibmphp.h +++ b/drivers/pci/hotplug/ibmphp.h @@ -378,7 +378,6 @@ int ibmphp_add_pfmem_from_mem(struct resource_node *); struct bus_node *ibmphp_find_res_bus(u8); void ibmphp_print_test(void); /* for debugging purposes */ -void ibmphp_hpc_initvars(void); int ibmphp_hpc_readslot(struct slot *, u8, u8 *); int ibmphp_hpc_writeslot(struct slot *, u8); void ibmphp_lock_operations(void); diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 08a58e911fc2..17124254d897 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -1277,8 +1277,6 @@ static int __init ibmphp_init(void) ibmphp_debug = debug; - ibmphp_hpc_initvars(); - for (i = 0; i < 16; i++) irqs[i] = 0; diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c index 752c384cbd4c..508a62a6b5f9 100644 --- a/drivers/pci/hotplug/ibmphp_hpc.c +++ b/drivers/pci/hotplug/ibmphp_hpc.c @@ -15,13 +15,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include #include "ibmphp.h" @@ -88,10 +88,10 @@ static int to_debug = 0; //---------------------------------------------------------------------------- // global variables //---------------------------------------------------------------------------- -static struct mutex sem_hpcaccess; // lock access to HPC -static struct semaphore semOperations; // lock all operations and +static DEFINE_MUTEX(sem_hpcaccess); // lock access to HPC +static DEFINE_MUTEX(operations_mutex); // lock all operations and // access to data structures -static struct semaphore sem_exit; // make sure polling thread goes away +static DECLARE_COMPLETION(exit_complete); // make sure polling thread goes away static struct task_struct *ibmphp_poll_thread; //---------------------------------------------------------------------------- // local function prototypes @@ -109,23 +109,6 @@ static int hpc_wait_ctlr_notworking(int, struct controller *, void __iomem *, u8 //---------------------------------------------------------------------------- -/*---------------------------------------------------------------------- -* Name: ibmphp_hpc_initvars -* -* Action: initialize semaphores and variables -*---------------------------------------------------------------------*/ -void __init ibmphp_hpc_initvars(void) -{ - debug("%s - Entry\n", __func__); - - mutex_init(&sem_hpcaccess); - sema_init(&semOperations, 1); - sema_init(&sem_exit, 0); - to_debug = 0; - - debug("%s - Exit\n", __func__); -} - /*---------------------------------------------------------------------- * Name: i2c_ctrl_read * @@ -780,7 +763,7 @@ void free_hpc_access(void) *---------------------------------------------------------------------*/ void ibmphp_lock_operations(void) { - down(&semOperations); + mutex_lock(&operations_mutex); to_debug = 1; } @@ -790,7 +773,7 @@ void ibmphp_lock_operations(void) void ibmphp_unlock_operations(void) { debug("%s - Entry\n", __func__); - up(&semOperations); + mutex_unlock(&operations_mutex); to_debug = 0; debug("%s - Exit\n", __func__); } @@ -816,7 +799,7 @@ static int poll_hpc(void *data) while (!kthread_should_stop()) { /* try to get the lock to do some kind of hardware access */ - down(&semOperations); + mutex_lock(&operations_mutex); switch (poll_state) { case POLL_LATCH_REGISTER: @@ -871,13 +854,13 @@ static int poll_hpc(void *data) break; case POLL_SLEEP: /* don't sleep with a lock on the hardware */ - up(&semOperations); + mutex_unlock(&operations_mutex); msleep(POLL_INTERVAL_SEC * 1000); if (kthread_should_stop()) goto out_sleep; - down(&semOperations); + mutex_lock(&operations_mutex); if (poll_count >= POLL_LATCH_CNT) { poll_count = 0; @@ -887,12 +870,12 @@ static int poll_hpc(void *data) break; } /* give up the hardware semaphore */ - up(&semOperations); + mutex_unlock(&operations_mutex); /* sleep for a short time just for good measure */ out_sleep: msleep(100); } - up(&sem_exit); + complete(&exit_complete); debug("%s - Exit\n", __func__); return 0; } @@ -1060,9 +1043,9 @@ void __exit ibmphp_hpc_stop_poll_thread(void) debug("after locking operations\n"); // wait for poll thread to exit - debug("before sem_exit down\n"); - down(&sem_exit); - debug("after sem_exit down\n"); + debug("before exit_complete down\n"); + wait_for_completion(&exit_complete); + debug("after exit_completion down\n"); // cleanup debug("before free_hpc_access\n"); @@ -1070,8 +1053,6 @@ void __exit ibmphp_hpc_stop_poll_thread(void) debug("after free_hpc_access\n"); ibmphp_unlock_operations(); debug("after unlock operations\n"); - up(&sem_exit); - debug("after sem exit up\n"); debug("%s - Exit\n", __func__); } -- cgit v1.2.3 From 0294951030eb8e7d808d7f151a2cf0a2ef1afb64 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Thu, 13 Dec 2018 09:23:42 -0700 Subject: PCI/VMD: Configure MPS settings before adding devices In order to provide the most performance and/or compatible settings, ensure VMD root buses observe the pcie bus tuning settings by configuring those settings prior to adding the devices to the pcie tree. This patch open-codes pci_rescan_bus() and configures the buses prior to adding devices and attaching drivers. Signed-off-by: Jon Derrick Signed-off-by: Lorenzo Pieralisi Reviewed-by: Keith Busch --- drivers/pci/controller/vmd.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 3890812cdf87..cf6816b55b5e 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -571,6 +571,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) LIST_HEAD(resources); resource_size_t offset[2] = {0}; resource_size_t membar2_offset = 0x2000, busn_start = 0; + struct pci_bus *child; /* * Shadow registers may exist in certain VMD device ids which allow @@ -698,7 +699,19 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) vmd_attach_resources(vmd); vmd_setup_dma_ops(vmd); dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); - pci_rescan_bus(vmd->bus); + + pci_scan_child_bus(vmd->bus); + pci_assign_unassigned_bus_resources(vmd->bus); + + /* + * VMD root buses are virtual and don't return true on pci_is_pcie() + * and will fail pcie_bus_configure_settings() early. It can instead be + * run on each of the real root ports. + */ + list_for_each_entry(child, &vmd->bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(vmd->bus); WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj, "domain"), "Can't create symlink to domain\n"); -- cgit v1.2.3 From 01b37f851ca150554496fd6e79c6d9a67992a2c0 Mon Sep 17 00:00:00 2001 From: Du Changbin Date: Sat, 13 Oct 2018 08:49:19 +0800 Subject: PCI: Make pci_size() return real BAR size Currently, the pci_size() function actually returns 'size-1'. Make it return real size to avoid confusion. Signed-off-by: Du Changbin Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2ef8b954c65a..8e2e4154cdd9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -121,13 +121,13 @@ static u64 pci_size(u64 base, u64 maxbase, u64 mask) * Get the lowest of them to find the decode size, and from that * the extent. */ - size = (size & ~(size-1)) - 1; + size = size & ~(size-1); /* * base == maxbase can be valid only if the BAR has already been * programmed with all 1s. */ - if (base == maxbase && ((base | size) & mask) != mask) + if (base == maxbase && ((base | (size - 1)) & mask) != mask) return 0; return size; @@ -278,7 +278,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Above 32-bit boundary; try to reallocate */ res->flags |= IORESOURCE_UNSET; res->start = 0; - res->end = sz64; + res->end = sz64 - 1; pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n", pos, (unsigned long long)l64); goto out; @@ -286,7 +286,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, } region.start = l64; - region.end = l64 + sz64; + region.end = l64 + sz64 - 1; pcibios_bus_to_resource(dev->bus, res, ®ion); pcibios_resource_to_bus(dev->bus, &inverted_region, res); -- cgit v1.2.3 From b2fb5cc574695a32361a6c1878816b3d6563aa0f Mon Sep 17 00:00:00 2001 From: Honghui Zhang Date: Tue, 16 Oct 2018 18:44:43 +0800 Subject: PCI: Rely on config space header type, not class code The PCI configuration space header type tells us whether the device is a bridge, a CardBus bridge, or a normal device, and defines the layout of the rest of the header (PCI r3.0 sec 6.1, PCIe r4.0 sec 7.5.1.1.9). When we rely on the header format, e.g., when we're dealing with bridge windows, we should check the header type, not the class code. The class code is loosely related to the header type, but is often incorrect and the spec doesn't actually require it to be related to the header format. Suggested-by: Bjorn Helgaas Signed-off-by: Honghui Zhang [bhelgaas: changelog, keep the PCI_CLASS_BRIDGE_HOST check] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- drivers/pci/probe.c | 3 --- drivers/pci/setup-bus.c | 18 +++++++++--------- 3 files changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..e9d938e14ba8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6000,8 +6000,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) * to enable the kernel to reassign new resource * window later on. */ - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { r = &dev->resource[i]; if (!(r->flags & IORESOURCE_MEM)) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8e2e4154cdd9..128459a0ffba 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1779,9 +1779,6 @@ int pci_setup_device(struct pci_dev *dev) break; case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ - if (class != PCI_CLASS_BRIDGE_PCI) - goto bad; - /* * The PCI-to-PCI bridge spec requires that subtractive * decoding (i.e. transparent) bridge must have programming diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 1941bb0a6c13..ec44a0f3a7ac 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1186,12 +1186,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) if (!b) continue; - switch (dev->class >> 8) { - case PCI_CLASS_BRIDGE_CARDBUS: + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: pci_bus_size_cardbus(b, realloc_head); break; - case PCI_CLASS_BRIDGE_PCI: + case PCI_HEADER_TYPE_BRIDGE: default: __pci_bus_size_bridges(b, realloc_head); break; @@ -1202,12 +1202,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) if (pci_is_root_bus(bus)) return; - switch (bus->self->class >> 8) { - case PCI_CLASS_BRIDGE_CARDBUS: + switch (bus->self->hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: /* don't size cardbuses yet. */ break; - case PCI_CLASS_BRIDGE_PCI: + case PCI_HEADER_TYPE_BRIDGE: pci_bridge_check_ranges(bus); if (bus->self->is_hotplug_bridge) { additional_io_size = pci_hotplug_io_size; @@ -1356,13 +1356,13 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, __pci_bus_assign_resources(b, realloc_head, fail_head); - switch (dev->class >> 8) { - case PCI_CLASS_BRIDGE_PCI: + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_BRIDGE: if (!pci_is_enabled(dev)) pci_setup_bridge(b); break; - case PCI_CLASS_BRIDGE_CARDBUS: + case PCI_HEADER_TYPE_CARDBUS: pci_setup_cardbus(b); break; -- cgit v1.2.3 From c232c0df9610bf2055ff002ecafb4915f0aff528 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 24 Jan 2019 13:59:57 +0530 Subject: PCI: dwc: dra7xx: Enable x2 mode support for dra74x, dra76x and dra72x dra74x/dra76x and dra72x have separate compatible strings. Add support for these compatible strings in pci-dra7xx driver to perform syscon configurations required to get x2 mode working. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index a32d6dde7a57..5c72acb49c11 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -81,6 +81,10 @@ #define MSI_REQ_GRANT BIT(0) #define MSI_VECTOR_SHIFT 7 +#define PCIE_1LANE_2LANE_SELECTION BIT(13) +#define PCIE_B1C0_MODE_SEL BIT(2) +#define PCIE_B0_B1_TSYNCEN BIT(0) + struct dra7xx_pcie { struct dw_pcie *pci; void __iomem *base; /* DT ti_conf */ @@ -93,6 +97,7 @@ struct dra7xx_pcie { struct dra7xx_pcie_of_data { enum dw_pcie_device_mode mode; + u32 b1co_mode_sel_mask; }; #define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev) @@ -529,6 +534,26 @@ static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = { .mode = DW_PCIE_EP_TYPE, }; +static const struct dra7xx_pcie_of_data dra746_pcie_rc_of_data = { + .b1co_mode_sel_mask = BIT(2), + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra726_pcie_rc_of_data = { + .b1co_mode_sel_mask = GENMASK(3, 2), + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra746_pcie_ep_of_data = { + .b1co_mode_sel_mask = BIT(2), + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra726_pcie_ep_of_data = { + .b1co_mode_sel_mask = GENMASK(3, 2), + .mode = DW_PCIE_EP_TYPE, +}; + static const struct of_device_id of_dra7xx_pcie_match[] = { { .compatible = "ti,dra7-pcie", @@ -538,6 +563,22 @@ static const struct of_device_id of_dra7xx_pcie_match[] = { .compatible = "ti,dra7-pcie-ep", .data = &dra7xx_pcie_ep_of_data, }, + { + .compatible = "ti,dra746-pcie-rc", + .data = &dra746_pcie_rc_of_data, + }, + { + .compatible = "ti,dra726-pcie-rc", + .data = &dra726_pcie_rc_of_data, + }, + { + .compatible = "ti,dra746-pcie-ep", + .data = &dra746_pcie_ep_of_data, + }, + { + .compatible = "ti,dra726-pcie-ep", + .data = &dra726_pcie_ep_of_data, + }, {}, }; @@ -583,6 +624,34 @@ static int dra7xx_pcie_unaligned_memaccess(struct device *dev) return ret; } +static int dra7xx_pcie_configure_two_lane(struct device *dev, + u32 b1co_mode_sel_mask) +{ + struct device_node *np = dev->of_node; + struct regmap *pcie_syscon; + unsigned int pcie_reg; + u32 mask; + u32 val; + + pcie_syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-lane-sel"); + if (IS_ERR(pcie_syscon)) { + dev_err(dev, "unable to get ti,syscon-lane-sel\n"); + return -EINVAL; + } + + if (of_property_read_u32_index(np, "ti,syscon-lane-sel", 1, + &pcie_reg)) { + dev_err(dev, "couldn't get lane selection reg offset\n"); + return -EINVAL; + } + + mask = b1co_mode_sel_mask | PCIE_B0_B1_TSYNCEN; + val = PCIE_B1C0_MODE_SEL | PCIE_B0_B1_TSYNCEN; + regmap_update_bits(pcie_syscon, pcie_reg, mask, val); + + return 0; +} + static int __init dra7xx_pcie_probe(struct platform_device *pdev) { u32 reg; @@ -603,6 +672,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) const struct of_device_id *match; const struct dra7xx_pcie_of_data *data; enum dw_pcie_device_mode mode; + u32 b1co_mode_sel_mask; match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev); if (!match) @@ -610,6 +680,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) data = (struct dra7xx_pcie_of_data *)match->data; mode = (enum dw_pcie_device_mode)data->mode; + b1co_mode_sel_mask = data->b1co_mode_sel_mask; dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); if (!dra7xx) @@ -665,6 +736,12 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) dra7xx->pci = pci; dra7xx->phy_count = phy_count; + if (phy_count == 2) { + ret = dra7xx_pcie_configure_two_lane(dev, b1co_mode_sel_mask); + if (ret < 0) + dra7xx->phy_count = 1; /* Fallback to x1 lane mode */ + } + ret = dra7xx_pcie_enable_phy(dra7xx); if (ret) { dev_err(dev, "failed to enable phy\n"); -- cgit v1.2.3 From 65c839a134e6afca13822eeb6e22abea37f86e76 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 24 Jan 2019 16:15:37 +0530 Subject: PCI: dwc: dra7xx: Invoke phy_set_mode() API to set PHY mode to PHY_MODE_PCIE Certain PHYs used with PCIe controller can also be used with other controllers such as USB or SATA. In order to configure the PHY to work with PCIe controller, invoke phy_set_mode() API with mode set to PHY_MODE_PCIE. Signed-off-by: Kishon Vijay Abraham I [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 5c72acb49c11..5d5844fc143e 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -504,6 +504,10 @@ static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx) int i; for (i = 0; i < phy_count; i++) { + ret = phy_set_mode(dra7xx->phy[i], PHY_MODE_PCIE); + if (ret < 0) + goto err_phy; + ret = phy_init(dra7xx->phy[i]); if (ret < 0) goto err_phy; -- cgit v1.2.3 From 12de16ae0d6ca8d41b65dd922f813f74f8497398 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:00 +0100 Subject: PCI: dwc: Remove unnecessary header include (of_gpio.h) Remove unnecessary header include (of_gpio.h) since it doesn't provide any needed symbols. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index c12bf794d69c..c1aa700362a7 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 3630c4aaae217efd4ce1b090f57da128686d97ce Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:01 +0100 Subject: PCI: dwc: Remove unnecessary header include (signal.h) Remove unnecessary header include (signal.h) since it doesn't provide any needed symbols. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index c1aa700362a7..cee7b4a04fed 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 40e9892ef94ce8b02e6ca7c6ee5405e6c28e946c Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:02 +0100 Subject: PCI: dwc: Rename variable name from data to d on dw_pci_bottom_mask/unmask() Rename variable from data to d to maintain consistency between driver functions, such as dw_msi_mask_irq() and dw_msi_unmask_irq(). No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 721d60a5d9e4..36e284c75934 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -149,20 +149,20 @@ static int dw_pci_msi_set_affinity(struct irq_data *irq_data, return -EINVAL; } -static void dw_pci_bottom_mask(struct irq_data *data) +static void dw_pci_bottom_mask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); unsigned int res, bit, ctrl; unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); if (pp->ops->msi_clear_irq) { - pp->ops->msi_clear_irq(pp, data->hwirq); + pp->ops->msi_clear_irq(pp, d->hwirq); } else { - ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; + ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; - bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; + bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_status[ctrl] &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, @@ -172,20 +172,20 @@ static void dw_pci_bottom_mask(struct irq_data *data) raw_spin_unlock_irqrestore(&pp->lock, flags); } -static void dw_pci_bottom_unmask(struct irq_data *data) +static void dw_pci_bottom_unmask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); unsigned int res, bit, ctrl; unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); if (pp->ops->msi_set_irq) { - pp->ops->msi_set_irq(pp, data->hwirq); + pp->ops->msi_set_irq(pp, d->hwirq); } else { - ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; + ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; - bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; + bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_status[ctrl] |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, -- cgit v1.2.3 From 59ea68b3f17294f05c95ffcb6b44442f767fe93c Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:03 +0100 Subject: PCI: dwc: Rename variable name from data to d on dw_pci_setup_msi_msg() Rename variable from data to d to maintain consistency between driver functions, such as dw_pci_setup_msi_msg(). No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 36e284c75934..0c6e19ab707b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -120,9 +120,9 @@ static void dw_chained_msi_isr(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) +static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); u64 msi_target; @@ -135,12 +135,12 @@ static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) msg->address_hi = upper_32_bits(msi_target); if (pp->ops->get_msi_data) - msg->data = pp->ops->get_msi_data(pp, data->hwirq); + msg->data = pp->ops->get_msi_data(pp, d->hwirq); else - msg->data = data->hwirq; + msg->data = d->hwirq; dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); + (int)d->hwirq, msg->address_hi, msg->address_lo); } static int dw_pci_msi_set_affinity(struct irq_data *irq_data, -- cgit v1.2.3 From fd5288a362ab55371089acf92ede0d2b876fccc0 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:04 +0100 Subject: PCI: dwc: Rename variable name from data to d on dw_pci_msi_set_affinity() Rename variable from data to d to maintain consistency between driver functions. No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 0c6e19ab707b..a59fc17eb120 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -143,7 +143,7 @@ static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) (int)d->hwirq, msg->address_hi, msg->address_lo); } -static int dw_pci_msi_set_affinity(struct irq_data *irq_data, +static int dw_pci_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force) { return -EINVAL; -- cgit v1.2.3 From 4cfae0f1f8ce16c67abd2b40f7d1f645cbc0ae4a Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:05 +0100 Subject: PCI: dwc: Rename variable name from data to d on dw_pcie_irq_domain_free() Rename variable from data to d to maintain consistency between driver functions. No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index a59fc17eb120..768e16aad68e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -255,13 +255,13 @@ static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, static void dw_pcie_irq_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { - struct irq_data *data = irq_domain_get_irq_data(domain, virq); - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); - bitmap_release_region(pp->msi_irq_in_use, data->hwirq, + bitmap_release_region(pp->msi_irq_in_use, d->hwirq, order_base_2(nr_irqs)); raw_spin_unlock_irqrestore(&pp->lock, flags); -- cgit v1.2.3 From a348d015f0de7a08f88f8089f6f6c4bc5248029b Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:06 +0100 Subject: PCI: dwc: Improve code readability and simplify mask/unmask operations Improve code readability and simplifies mask/unmask operations by inverting the applied logic (no functional change is intended). Replace variable name from irq_status to irq_mask, since its goal is to keep track of which interrupts are masked or not. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 12 ++++++------ drivers/pci/controller/dwc/pcie-designware.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 768e16aad68e..c550c4a7ef89 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -164,9 +164,9 @@ static void dw_pci_bottom_mask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_status[ctrl] &= ~(1 << bit); + pp->irq_mask[ctrl] |= (1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, - ~pp->irq_status[ctrl]); + pp->irq_mask[ctrl]); } raw_spin_unlock_irqrestore(&pp->lock, flags); @@ -187,9 +187,9 @@ static void dw_pci_bottom_unmask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_status[ctrl] |= 1 << bit; + pp->irq_mask[ctrl] &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, - ~pp->irq_status[ctrl]); + pp->irq_mask[ctrl]); } raw_spin_unlock_irqrestore(&pp->lock, flags); @@ -665,13 +665,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* Initialize IRQ Status array */ for (ctrl = 0; ctrl < num_ctrls; ctrl++) { + pp->irq_mask[ctrl] = ~0; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), - 4, ~0); + 4, pp->irq_mask[ctrl]); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 4, ~0); - pp->irq_status[ctrl] = 0; } /* Setup RC BARs */ diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 9943d8c68335..279000255ad1 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -177,7 +177,7 @@ struct pcie_port { struct irq_domain *msi_domain; dma_addr_t msi_data; u32 num_vectors; - u32 irq_status[MAX_MSI_CTRLS]; + u32 irq_mask[MAX_MSI_CTRLS]; raw_spinlock_t lock; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; -- cgit v1.2.3 From 657722570a555c728c59f17f7825c812c124dc28 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:07 +0100 Subject: PCI: dwc: Replace bit rotation operation (1 << bit) with BIT(bit) Replace bit rotation operation (1 << bit) with BIT(bit), which simplifies code reading. No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index c550c4a7ef89..45ff5e4f8af6 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -164,7 +164,7 @@ static void dw_pci_bottom_mask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_mask[ctrl] |= (1 << bit); + pp->irq_mask[ctrl] |= BIT(bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, pp->irq_mask[ctrl]); } @@ -187,7 +187,7 @@ static void dw_pci_bottom_unmask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_mask[ctrl] &= ~(1 << bit); + pp->irq_mask[ctrl] &= ~BIT(bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, pp->irq_mask[ctrl]); } @@ -207,7 +207,7 @@ static void dw_pci_bottom_ack(struct irq_data *d) raw_spin_lock_irqsave(&pp->lock, flags); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, 1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, BIT(bit)); if (pp->ops->msi_irq_ack) pp->ops->msi_irq_ack(d->hwirq, pp); -- cgit v1.2.3 From 01926f6b321b069ce0a85a68554312b9e58ee87d Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Fri, 1 Feb 2019 17:13:10 -0600 Subject: PCI: Add ACS quirk for HXT SD4800 The design of HXT SD4800 ACS feature is the same as QCOM QDF2xxx. Add an ACS quirk for the SD4800. Signed-off-by: Shunyong Yang [bhelgaas: split to separate patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya CC: Joey Zheng --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9fabcef345ed..6bafd0fe0f0b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4519,6 +4519,8 @@ static const struct pci_dev_acs_enabled { /* QCOM QDF2xxx root ports */ { PCI_VENDOR_ID_QCOM, 0x0400, pci_quirk_qcom_rp_acs }, { PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs }, + /* HXT SD4800 root ports. The ACS design is same as QCOM QDF2xxx */ + { PCI_VENDOR_ID_HXT, 0x0401, pci_quirk_qcom_rp_acs }, /* Intel PCH root ports */ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, -- cgit v1.2.3 From 22e4d639cd05b98b27c78be497d6d42e4cfebf44 Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Wed, 7 Nov 2018 15:25:05 +0800 Subject: PCI: pciehp: Add HXT quirk for Command Completed errata The HXT SD4800 PCI controller does not set the Command Completed bit unless writes to the Slot Command register change "Control" bits. Add SD4800 to the quirk. Signed-off-by: Shunyong Yang Signed-off-by: Bjorn Helgaas CC: Joey Zheng --- drivers/pci/hotplug/pciehp_hpc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7dd443aea5a5..91db67963aea 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -920,3 +920,5 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0400, PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0401, PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_HXT, 0x0401, + PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); -- cgit v1.2.3 From b4f6dcb9d35688392d668c46e834f72c55900b49 Mon Sep 17 00:00:00 2001 From: Bharat Kumar Gogada Date: Wed, 14 Nov 2018 20:17:01 +0530 Subject: PCI: Enable SERR# forwarding for all bridges As per Figure 6-3 in PCIe r4.0, sec 6.2.6, ERR_ messages will be forwarded from the secondary interface to the primary interface, if the SERR# Enable bit in the Bridge Control register is set. It seems clear that an ACPI hotplug parameter method (_HPP or _HPX) that tells us to "enable SERR in the command register" (ACPI v6.2, sec 6.2.8, 6.2.9.1) refers to PCI_COMMAND_SERR, which enables reporting of errors by the function itself. For bridges, we also interpreted that to mean we should enable PCI_BRIDGE_CTL_SERR, which enables *forwarding* of errors by the bridge. But we didn't enable PCI_BRIDGE_CTL_SERR anywhere else, which means we never enabled it for non-ACPI systems or ACPI systems that didn't supply hotplug parameters. That means errors reported below bridges were often never forwarded up to a Root Port where they could be signaled via AER. Enable PCI_BRIDGE_CTL_SERR for all bridges so we can get better error reporting for downstream devices. Signed-off-by: Bharat Kumar Gogada [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..43e6583417ce 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1856,8 +1856,6 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp) pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, hpp->latency_timer); pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); - if (hpp->enable_serr) - pci_bctl |= PCI_BRIDGE_CTL_SERR; if (hpp->enable_perr) pci_bctl |= PCI_BRIDGE_CTL_PARITY; pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); @@ -2129,6 +2127,24 @@ static void pci_configure_eetlp_prefix(struct pci_dev *dev) #endif } +static void pci_configure_serr(struct pci_dev *dev) +{ + u16 control; + + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + + /* + * A bridge will not forward ERR_ messages coming from an + * endpoint unless SERR# forwarding is enabled. + */ + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &control); + if (!(control & PCI_BRIDGE_CTL_SERR)) { + control |= PCI_BRIDGE_CTL_SERR; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, control); + } + } +} + static void pci_configure_device(struct pci_dev *dev) { struct hotplug_params hpp; @@ -2139,6 +2155,7 @@ static void pci_configure_device(struct pci_dev *dev) pci_configure_relaxed_ordering(dev); pci_configure_ltr(dev); pci_configure_eetlp_prefix(dev); + pci_configure_serr(dev); memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); -- cgit v1.2.3 From e8e4d4e95701a10691c53165c55789e5e50ba3f5 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:20 -0800 Subject: PCI: imx6: Introduce drvdata Introduce driver data struct. This will simplify handling of device specific differences. Signed-off-by: Stefan Agner [andrew.smirnov@gmail.com reformatted drvdata, to simplify future diffs] Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 56 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 52e47dac028f..1e940ba7e4c4 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -41,6 +41,10 @@ enum imx6_pcie_variants { IMX7D, }; +struct imx6_pcie_drvdata { + enum imx6_pcie_variants variant; +}; + struct imx6_pcie { struct dw_pcie *pci; int reset_gpio; @@ -53,7 +57,6 @@ struct imx6_pcie { struct reset_control *pciephy_reset; struct reset_control *apps_reset; struct reset_control *turnoff_reset; - enum imx6_pcie_variants variant; u32 tx_deemph_gen1; u32 tx_deemph_gen2_3p5db; u32 tx_deemph_gen2_6db; @@ -66,6 +69,7 @@ struct imx6_pcie { struct device *pd_pcie; /* power domain for pcie phy */ struct device *pd_pcie_phy; + const struct imx6_pcie_drvdata *drvdata; }; /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ @@ -339,7 +343,7 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { struct device *dev = imx6_pcie->pci->dev; - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX7D: reset_control_assert(imx6_pcie->pciephy_reset); reset_control_assert(imx6_pcie->apps_reset); @@ -381,7 +385,7 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) struct device *dev = pci->dev; int ret = 0; - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); if (ret) { @@ -484,7 +488,7 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) !imx6_pcie->gpio_active_high); } - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); @@ -522,7 +526,7 @@ err_pcie_phy: static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX7D: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); @@ -644,7 +648,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6Q: case IMX6SX: case IMX6QP: @@ -697,7 +701,7 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) tmp |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - if (imx6_pcie->variant != IMX7D) { + if (imx6_pcie->drvdata->variant != IMX7D) { /* * On i.MX7, DIRECT_SPEED_CHANGE behaves differently * from i.MX6 family when no link speed transition @@ -794,7 +798,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: case IMX6QP: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, @@ -820,7 +824,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) } /* Others poke directly at IOMUXC registers */ - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_PM_TURN_OFF, @@ -850,7 +854,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) clk_disable_unprepare(imx6_pcie->pcie_phy); clk_disable_unprepare(imx6_pcie->pcie_bus); - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); break; @@ -866,8 +870,8 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie) { - return (imx6_pcie->variant == IMX7D || - imx6_pcie->variant == IMX6SX); + return (imx6_pcie->drvdata->variant == IMX7D || + imx6_pcie->drvdata->variant == IMX6SX); } static int imx6_pcie_suspend_noirq(struct device *dev) @@ -933,8 +937,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) pci->ops = &dw_pcie_ops; imx6_pcie->pci = pci; - imx6_pcie->variant = - (enum imx6_pcie_variants)of_device_get_match_data(dev); + imx6_pcie->drvdata = of_device_get_match_data(dev); dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); pci->dbi_base = devm_ioremap_resource(dev, dbi_base); @@ -978,7 +981,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->pcie); } - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, "pcie_inbound_axi"); @@ -1084,11 +1087,26 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) imx6_pcie_assert_core_reset(imx6_pcie); } +static const struct imx6_pcie_drvdata drvdata[] = { + [IMX6Q] = { + .variant = IMX6Q, + }, + [IMX6SX] = { + .variant = IMX6SX, + }, + [IMX6QP] = { + .variant = IMX6QP, + }, + [IMX7D] = { + .variant = IMX7D, + }, +}; + static const struct of_device_id imx6_pcie_of_match[] = { - { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, - { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, }, - { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, - { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, }, + { .compatible = "fsl,imx6q-pcie", .data = &drvdata[IMX6Q], }, + { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], }, + { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], }, + { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], }, {}, }; -- cgit v1.2.3 From 2f532d07f0cce72f88a4a0532ae11be6a2745697 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:21 -0800 Subject: PCI: imx6: Mark PHY functions as i.MX6 specific PCIe PHY IP block on i.MX7D differs from the one used on i.MX6 family, so none of the code in the current implementation of imx6_setup_phy_mpll() or imx6_pcie_reset_phy() is applicable. Introduce IMX6_PCIE_FLAG_IMX6_PHY and check for it in the aforementioned functions to make sure they are only executed on appropriate PCIe IP variants. Tested-by: Trent Piepho Signed-off-by: Andrey Smirnov [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 1e940ba7e4c4..26e1e974b0dd 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -41,8 +41,11 @@ enum imx6_pcie_variants { IMX7D, }; +#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) + struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; + u32 flags; }; struct imx6_pcie { @@ -255,6 +258,9 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) { u32 tmp; + if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY)) + return; + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); @@ -572,6 +578,9 @@ static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie) int mult, div; u32 val; + if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY)) + return 0; + switch (phy_rate) { case 125000000: /* @@ -1090,12 +1099,15 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) static const struct imx6_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, + .flags = IMX6_PCIE_FLAG_IMX6_PHY, }, [IMX6SX] = { .variant = IMX6SX, + .flags = IMX6_PCIE_FLAG_IMX6_PHY, }, [IMX6QP] = { .variant = IMX6QP, + .flags = IMX6_PCIE_FLAG_IMX6_PHY, }, [IMX7D] = { .variant = IMX7D, -- cgit v1.2.3 From 4c458bb347ae016e75f6c61dab95ce530f4ff6fc Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:22 -0800 Subject: PCI: imx6: Convert DIRECT_SPEED_CHANGE quirk code to use a flag Both i.MX7D and i.MX8MQ have the same behaviour when it comes to clearing DIRECT_SPEED_CHANGE bit when no speed change occurs, so to handle variants correctly add a flag instead of checking the IP block variant. Signed-off-by: Andrey Smirnov [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 26e1e974b0dd..11b73d733dae 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -42,6 +42,7 @@ enum imx6_pcie_variants { }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) +#define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1) struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; @@ -710,7 +711,8 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) tmp |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - if (imx6_pcie->drvdata->variant != IMX7D) { + if (imx6_pcie->drvdata->flags & + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE) { /* * On i.MX7, DIRECT_SPEED_CHANGE behaves differently * from i.MX6 family when no link speed transition @@ -1099,15 +1101,18 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) static const struct imx6_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, - .flags = IMX6_PCIE_FLAG_IMX6_PHY, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, }, [IMX6SX] = { .variant = IMX6SX, - .flags = IMX6_PCIE_FLAG_IMX6_PHY, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, }, [IMX6QP] = { .variant = IMX6QP, - .flags = IMX6_PCIE_FLAG_IMX6_PHY, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, }, [IMX7D] = { .variant = IMX7D, -- cgit v1.2.3 From 2d8ed461dbc9bc734185db92d2b9d1bb7b586b30 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:23 -0800 Subject: PCI: imx6: Add support for i.MX8MQ Add code needed to support i.MX8MQ variant. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- .../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 3 +- drivers/pci/controller/dwc/Kconfig | 4 +- drivers/pci/controller/dwc/pci-imx6.c | 77 +++++++++++++++++++++- 3 files changed, 79 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt index d514c1f2365f..920ca93870a8 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt @@ -9,6 +9,7 @@ Required properties: - "fsl,imx6sx-pcie", - "fsl,imx6qp-pcie" - "fsl,imx7d-pcie" + - "fsl,imx8mq-pcie" - reg: base address and length of the PCIe controller - interrupts: A list of interrupt outputs of the controller. Must contain an entry for each entry in the interrupt-names property. @@ -45,7 +46,7 @@ Additional required properties for imx6sx-pcie: PCIE_PHY power domains - power-domain-names: Must be "pcie", "pcie_phy" -Additional required properties for imx7d-pcie: +Additional required properties for imx7d-pcie and imx8mq-pcie: - power-domains: Must be set to a phandle pointing to PCIE_PHY power domain - resets: Must contain phandles to PCIe-related reset lines exposed by SRC IP block diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 548c58223868..6ea74b1c0d94 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -89,8 +89,8 @@ config PCI_EXYNOS select PCIE_DW_HOST config PCI_IMX6 - bool "Freescale i.MX6/7 PCIe controller" - depends on SOC_IMX6Q || SOC_IMX7D || (ARM && COMPILE_TEST) + bool "Freescale i.MX6/7/8 PCIe controller" + depends on SOC_IMX6Q || SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 11b73d733dae..c1d434ba3642 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -8,6 +8,7 @@ * Author: Sean Cross */ +#include #include #include #include @@ -32,6 +33,12 @@ #include "pcie-designware.h" +#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9) +#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10) +#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) +#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8) +#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000 + #define to_imx6_pcie(x) dev_get_drvdata((x)->dev) enum imx6_pcie_variants { @@ -39,6 +46,7 @@ enum imx6_pcie_variants { IMX6SX, IMX6QP, IMX7D, + IMX8MQ, }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) @@ -58,6 +66,7 @@ struct imx6_pcie { struct clk *pcie_inbound_axi; struct clk *pcie; struct regmap *iomuxc_gpr; + u32 controller_id; struct reset_control *pciephy_reset; struct reset_control *apps_reset; struct reset_control *turnoff_reset; @@ -275,6 +284,7 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); } +#ifdef CONFIG_ARM /* Added for PCI abort handling */ static int imx6q_pcie_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) @@ -308,6 +318,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr, return 1; } +#endif static int imx6_pcie_attach_pd(struct device *dev) { @@ -352,6 +363,7 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) switch (imx6_pcie->drvdata->variant) { case IMX7D: + case IMX8MQ: reset_control_assert(imx6_pcie->pciephy_reset); reset_control_assert(imx6_pcie->apps_reset); break; @@ -386,10 +398,17 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) } } +static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) +{ + WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ); + return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; +} + static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; struct device *dev = pci->dev; + unsigned int offset; int ret = 0; switch (imx6_pcie->drvdata->variant) { @@ -420,6 +439,19 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) break; case IMX7D: break; + case IMX8MQ: + offset = imx6_pcie_grp_offset(imx6_pcie); + /* + * Set the over ride low and enabled + * make sure that REF_CLK is turned on. + */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, offset, + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE, + 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, offset, + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN); + break; } return ret; @@ -496,6 +528,9 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) } switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + reset_control_deassert(imx6_pcie->pciephy_reset); + break; case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); @@ -531,9 +566,37 @@ err_pcie_phy: } } +static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie) +{ + unsigned int mask, val; + + if (imx6_pcie->drvdata->variant == IMX8MQ && + imx6_pcie->controller_id == 1) { + mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; + val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, + PCI_EXP_TYPE_ROOT_PORT); + } else { + mask = IMX6Q_GPR12_DEVICE_TYPE; + val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, + PCI_EXP_TYPE_ROOT_PORT); + } + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val); +} + static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + /* + * TODO: Currently this code assumes external + * oscillator is being used + */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, + imx6_pcie_grp_offset(imx6_pcie), + IMX8MQ_GPR_PCIE_REF_USE_PAD, + IMX8MQ_GPR_PCIE_REF_USE_PAD); + break; case IMX7D: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); @@ -569,8 +632,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) break; } - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); + imx6_pcie_configure_type(imx6_pcie); } static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie) @@ -667,6 +729,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev) IMX6Q_GPR12_PCIE_CTL_2); break; case IMX7D: + case IMX8MQ: reset_control_deassert(imx6_pcie->apps_reset); break; } @@ -1002,6 +1065,10 @@ static int imx6_pcie_probe(struct platform_device *pdev) } break; case IMX7D: + case IMX8MQ: + if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR) + imx6_pcie->controller_id = 1; + imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, "pciephy"); if (IS_ERR(imx6_pcie->pciephy_reset)) { @@ -1117,6 +1184,9 @@ static const struct imx6_pcie_drvdata drvdata[] = { [IMX7D] = { .variant = IMX7D, }, + [IMX8MQ] = { + .variant = IMX8MQ, + }, }; static const struct of_device_id imx6_pcie_of_match[] = { @@ -1124,6 +1194,7 @@ static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], }, { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], }, { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], }, + { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } , {}, }; @@ -1140,6 +1211,7 @@ static struct platform_driver imx6_pcie_driver = { static int __init imx6_pcie_init(void) { +#ifdef CONFIG_ARM /* * Since probe() can be deferred we need to make sure that * hook_fault_code is not called after __init memory is freed @@ -1149,6 +1221,7 @@ static int __init imx6_pcie_init(void) */ hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0, "external abort on non-linefetch"); +#endif return platform_driver_register(&imx6_pcie_driver); } -- cgit v1.2.3 From 20a796a969efe37c2dd38b72538407e544f41de4 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Fri, 8 Feb 2019 09:54:39 -0700 Subject: PCI: Clean up usage of __u32 type The double underscore types are meant for compatibility in userspace headers which does not apply here. Therefore, change to use the standard no-underscore types. The origin of the double underscore types dates back to before the git era so I was not able to find a commit to see the original justification. Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 79b1610a8beb..71853befd435 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -100,7 +100,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, { struct pci_driver *pdrv = to_pci_driver(driver); const struct pci_device_id *ids = pdrv->id_table; - __u32 vendor, device, subvendor = PCI_ANY_ID, + u32 vendor, device, subvendor = PCI_ANY_ID, subdevice = PCI_ANY_ID, class = 0, class_mask = 0; unsigned long driver_data = 0; int fields = 0; @@ -168,7 +168,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, { struct pci_dynid *dynid, *n; struct pci_driver *pdrv = to_pci_driver(driver); - __u32 vendor, device, subvendor = PCI_ANY_ID, + u32 vendor, device, subvendor = PCI_ANY_ID, subdevice = PCI_ANY_ID, class = 0, class_mask = 0; int fields = 0; size_t retval = -ENODEV; -- cgit v1.2.3 From 10ecc818ea7319b5d0d2b4e1aa6a77323e776f76 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2019 17:59:07 -0600 Subject: PCI/ASPM: Use LTR if already enabled by platform RussianNeuroMancer reported that the Intel 7265 wifi on a Dell Venue 11 Pro 7140 table stopped working after wakeup from suspend and bisected the problem to 9ab105deb60f ("PCI/ASPM: Disable ASPM L1.2 Substate if we don't have LTR"). David Ward reported the same problem on a Dell Latitude 7350. After af8bb9f89838 ("PCI/ACPI: Request LTR control from platform before using it"), we don't enable LTR unless the platform has granted LTR control to us. In addition, we don't notice if the platform had already enabled LTR itself. After 9ab105deb60f ("PCI/ASPM: Disable ASPM L1.2 Substate if we don't have LTR"), we avoid using LTR if we don't think the path to the device has LTR enabled. The combination means that if the platform itself enables LTR but declines to give the OS control over LTR, we unnecessarily avoided using ASPM L1.2. Link: https://bugzilla.kernel.org/show_bug.cgi?id=201469 Fixes: 9ab105deb60f ("PCI/ASPM: Disable ASPM L1.2 Substate if we don't have LTR") Fixes: af8bb9f89838 ("PCI/ACPI: Request LTR control from platform before using it") Reported-by: RussianNeuroMancer Reported-by: David Ward Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v4.18+ --- drivers/pci/probe.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..c46a3fcb341e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2071,11 +2071,8 @@ static void pci_configure_ltr(struct pci_dev *dev) { #ifdef CONFIG_PCIEASPM struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); - u32 cap; struct pci_dev *bridge; - - if (!host->native_ltr) - return; + u32 cap, ctl; if (!pci_is_pcie(dev)) return; @@ -2084,22 +2081,35 @@ static void pci_configure_ltr(struct pci_dev *dev) if (!(cap & PCI_EXP_DEVCAP2_LTR)) return; - /* - * Software must not enable LTR in an Endpoint unless the Root - * Complex and all intermediate Switches indicate support for LTR. - * PCIe r3.1, sec 6.18. - */ - if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) - dev->ltr_path = 1; - else { + pcie_capability_read_dword(dev, PCI_EXP_DEVCTL2, &ctl); + if (ctl & PCI_EXP_DEVCTL2_LTR_EN) { + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) { + dev->ltr_path = 1; + return; + } + bridge = pci_upstream_bridge(dev); if (bridge && bridge->ltr_path) dev->ltr_path = 1; + + return; } - if (dev->ltr_path) + if (!host->native_ltr) + return; + + /* + * Software must not enable LTR in an Endpoint unless the Root + * Complex and all intermediate Switches indicate support for LTR. + * PCIe r4.0, sec 6.18. + */ + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + ((bridge = pci_upstream_bridge(dev)) && + bridge->ltr_path)) { pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); + dev->ltr_path = 1; + } #endif } -- cgit v1.2.3 From 85b0cae89d5266e6a7abb2e83c6f716326fc494c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 Jan 2019 19:38:56 +0300 Subject: PCI: Blacklist power management of Gigabyte X299 DESIGNARE EX PCIe ports Gigabyte X299 DESIGNARE EX motherboard has one PCIe root port that is connected to an Alpine Ridge Thunderbolt controller. This port has slot implemented bit set in the config space but other than that it is not hotplug capable in the sense we are expecting in Linux (it has dev->is_hotplug_bridge set to 0): 00:1c.4 PCI bridge: Intel Corporation 200 Series PCH PCI Express Root Port #5 Bus: primary=00, secondary=05, subordinate=46, sec-latency=0 Memory behind bridge: 78000000-8fffffff [size=384M] Prefetchable memory behind bridge: 00003800f8000000-00003800ffffffff [size=128M] ... Capabilities: [40] Express (v2) Root Port (Slot+), MSI 00 ... SltCap: AttnBtn- PwrCtrl- MRL- AttnInd- PwrInd- HotPlug- Surprise- Slot #8, PowerLimit 25.000W; Interlock- NoCompl+ SltCtl: Enable: AttnBtn- PwrFlt- MRL- PresDet- CmdCplt- HPIrq- LinkChg- Control: AttnInd Unknown, PwrInd Unknown, Power- Interlock- SltSta: Status: AttnBtn- PowerFlt- MRL- CmdCplt- PresDet- Interlock- Changed: MRL- PresDet+ LinkState+ This system is using ACPI based hotplug to notify the OS that it needs to rescan the PCI bus (ACPI hotplug). If there is nothing connected in any of the Thunderbolt ports the root port will not have any runtime PM active children and is thus automatically runtime suspended pretty soon after boot by PCI PM core. Now, when a device is connected the BIOS SMI handler responsible for enumerating newly added devices is not able to find anything because the port is in D3. Prevent this from happening by blacklisting PCI power management of this particular Gigabyte system. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202031 Reported-by: Kedar A Dongre Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..0f29cd1d36d3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2501,6 +2501,25 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev) pm_runtime_put_sync(parent); } +static const struct dmi_system_id bridge_d3_blacklist[] = { +#ifdef CONFIG_X86 + { + /* + * Gigabyte X299 root port is not marked as hotplug capable + * which allows Linux to power manage it. However, this + * confuses the BIOS SMI handler so don't power manage root + * ports on that system. + */ + .ident = "X299 DESIGNARE EX-CF", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"), + }, + }, +#endif + { } +}; + /** * pci_bridge_d3_possible - Is it possible to put the bridge into D3 * @bridge: Bridge to check @@ -2546,6 +2565,9 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) if (bridge->is_hotplug_bridge) return false; + if (dmi_check_system(bridge_d3_blacklist)) + return false; + /* * It should be safe to put PCIe ports from 2015 or newer * to D3. -- cgit v1.2.3 From dbbfadf2319005cf528b0f15f12a05d4e4644303 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 9 Jan 2019 08:22:08 -0600 Subject: PCI/ASPM: Save LTR Capability for suspend/resume Latency Tolerance Reporting (LTR) allows Endpoints and Switch Upstream Ports to report their latency requirements to upstream components. If ASPM L1 PM substates are enabled, the LTR information helps determine when a Link enters L1.2 [1]. Software must set the maximum latency values in the LTR Capability based on characteristics of the platform, then set LTR Mechanism Enable in the Device Control 2 register in the PCIe Capability. The device can then use LTR to report its latency tolerance. If the device reports a maximum latency value of zero, that means the device requires the highest possible performance and the ASPM L1.2 substate is effectively disabled. We put devices in D3 for suspend, and we assume their internal state is lost. On resume, previously we did not restore the LTR Capability, but we did restore the LTR Mechanism Enable bit, so devices would request the highest possible performance and ASPM L1.2 wouldn't be used. [1] PCIe r4.0, sec 5.5.1 Link: https://bugzilla.kernel.org/show_bug.cgi?id=201469 Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..13d65991c77b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1233,7 +1233,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev) pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]); } - static int pci_save_pcix_state(struct pci_dev *dev) { int pos; @@ -1270,6 +1269,45 @@ static void pci_restore_pcix_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]); } +static void pci_save_ltr_state(struct pci_dev *dev) +{ + int ltr; + struct pci_cap_saved_state *save_state; + u16 *cap; + + if (!pci_is_pcie(dev)) + return; + + ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); + if (!ltr) + return; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR); + if (!save_state) { + pci_err(dev, "no suspend buffer for LTR; ASPM issues possible after resume\n"); + return; + } + + cap = (u16 *)&save_state->cap.data[0]; + pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap++); + pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, cap++); +} + +static void pci_restore_ltr_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state; + int ltr; + u16 *cap; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR); + ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); + if (!save_state || !ltr) + return; + + cap = (u16 *)&save_state->cap.data[0]; + pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap++); + pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, *cap++); +} /** * pci_save_state - save the PCI configuration space of a device before suspending @@ -1291,6 +1329,7 @@ int pci_save_state(struct pci_dev *dev) if (i != 0) return i; + pci_save_ltr_state(dev); pci_save_dpc_state(dev); return pci_save_vc_state(dev); } @@ -1390,7 +1429,12 @@ void pci_restore_state(struct pci_dev *dev) if (!dev->state_saved) return; - /* PCI Express register must be restored first */ + /* + * Restore max latencies (in the LTR capability) before enabling + * LTR itself (in the PCIe capability). + */ + pci_restore_ltr_state(dev); + pci_restore_pcie_state(dev); pci_restore_pasid_state(dev); pci_restore_pri_state(dev); @@ -2998,6 +3042,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) if (error) pci_err(dev, "unable to preallocate PCI-X save buffer\n"); + error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_LTR, + 2 * sizeof(u16)); + if (error) + pci_err(dev, "unable to allocate suspend buffer for LTR\n"); + pci_allocate_vc_save_buffers(dev); } -- cgit v1.2.3 From 0380cf843c54d9ee506f5dadc00dd4fe524c99a6 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Fri, 7 Dec 2018 18:24:37 +0100 Subject: PCI: dwc: Print debug error message when MSI-X entry control mask bit is set Add debug error message when MSI-X entry control mask bit is set, to help debug the reason why a MSI-X interrupt is not being triggered. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Bjorn Helgaas Cc: Joao Pinto --- drivers/pci/controller/dwc/pcie-designware-ep.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index a543c45c7224..5e766af04b66 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -465,8 +465,10 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, iounmap(msix_tbl); - if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) + if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) { + dev_dbg(pci->dev, "MSI-X entry ctrl set\n"); return -EPERM; + } ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, epc->mem->page_size); -- cgit v1.2.3 From 1df82ec4660099fdae42545051cbbeacb5666b50 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 5 Feb 2019 00:17:41 +0000 Subject: PCI: imx: Add workaround for e10728, IMX7d PCIe PLL failure This implements the workound described in the NXP IMX7d erratum e10728. Initial VCO oscillation may fail under corner conditions such as cold temperature. It causes PCIe PLL to fail to lock in the initialization phase, which results in the PCIe link failing to come up. The workaround is to disable Duty-Cycle Corrector (DCC) calibration after G_RST. To do this it is necessary to gain access to the undocumented and currently unused PCIe PHY register bank. A new device tree node of type "fsl,imx7d-pcie-phy" is created for the PHY block and the existing PCIe device uses a phandle named "fsl,imx7d-pcie-phy" to point to it. Signed-off-by: Trent Piepho [lorenzo.pieralisi@arm.com: updated log string, commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach --- drivers/pci/controller/dwc/pci-imx6.c | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index c1d434ba3642..64c74334f7d0 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,7 @@ struct imx6_pcie { u32 tx_swing_low; int link_gen; struct regulator *vpcie; + void __iomem *phy_base; /* power domain for pcie */ struct device *pd_pcie; @@ -134,6 +136,23 @@ struct imx6_pcie { #define PCIE_PHY_RX_ASIC_OUT 0x100D #define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0) +/* iMX7 PCIe PHY registers */ +#define PCIE_PHY_CMN_REG4 0x14 +/* These are probably the bits that *aren't* DCC_FB_EN */ +#define PCIE_PHY_CMN_REG4_DCC_FB_EN 0x29 + +#define PCIE_PHY_CMN_REG15 0x54 +#define PCIE_PHY_CMN_REG15_DLY_4 BIT(2) +#define PCIE_PHY_CMN_REG15_PLL_PD BIT(5) +#define PCIE_PHY_CMN_REG15_OVRD_PLL_PD BIT(7) + +#define PCIE_PHY_CMN_REG24 0x90 +#define PCIE_PHY_CMN_REG24_RX_EQ BIT(6) +#define PCIE_PHY_CMN_REG24_RX_EQ_SEL BIT(3) + +#define PCIE_PHY_CMN_REG26 0x98 +#define PCIE_PHY_CMN_REG26_ATT_MODE 0xBC + #define PHY_RX_OVRD_IN_LO 0x1005 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) @@ -533,6 +552,26 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) break; case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); + + /* Workaround for ERR010728, failure of PCI-e PLL VCO to + * oscillate, especially when cold. This turns off "Duty-cycle + * Corrector" and other mysterious undocumented things. + */ + if (likely(imx6_pcie->phy_base)) { + /* De-assert DCC_FB_EN */ + writel(PCIE_PHY_CMN_REG4_DCC_FB_EN, + imx6_pcie->phy_base + PCIE_PHY_CMN_REG4); + /* Assert RX_EQS and RX_EQS_SEL */ + writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL + | PCIE_PHY_CMN_REG24_RX_EQ, + imx6_pcie->phy_base + PCIE_PHY_CMN_REG24); + /* Assert ATT_MODE */ + writel(PCIE_PHY_CMN_REG26_ATT_MODE, + imx6_pcie->phy_base + PCIE_PHY_CMN_REG26); + } else { + dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n"); + } + imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); break; case IMX6SX: @@ -994,6 +1033,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dw_pcie *pci; struct imx6_pcie *imx6_pcie; + struct device_node *np; struct resource *dbi_base; struct device_node *node = dev->of_node; int ret; @@ -1013,6 +1053,23 @@ static int imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->pci = pci; imx6_pcie->drvdata = of_device_get_match_data(dev); + /* Find the PHY if one is defined, only imx7d uses it */ + np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); + if (np) { + struct resource res; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(dev, "Unable to map PCIe PHY\n"); + return ret; + } + imx6_pcie->phy_base = devm_ioremap_resource(dev, &res); + if (IS_ERR(imx6_pcie->phy_base)) { + dev_err(dev, "Unable to map PCIe PHY\n"); + return PTR_ERR(imx6_pcie->phy_base); + } + } + dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); pci->dbi_base = devm_ioremap_resource(dev, dbi_base); if (IS_ERR(pci->dbi_base)) -- cgit v1.2.3 From 726dabfde6aa35a4f1508e235ae37edbbf9fbc65 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Mon, 11 Feb 2019 17:04:00 +0800 Subject: PCI: endpoint: functions: Use memcpy_fromio()/memcpy_toio() Functions copying from/to IO addresses should use the memcpy_fromio()/memcpy_toio() API rather than plain memcpy(). Fix the issue detected through the sparse tool. Fixes: 349e7a85b25f ("PCI: endpoint: functions: Add an EP function to test PCI") Suggested-by: Kishon Vijay Abraham I Signed-off-by: Wen Yang [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I CC: Lorenzo Pieralisi CC: Bjorn Helgaas CC: Gustavo Pimentel CC: Niklas Cassel CC: Greg Kroah-Hartman CC: Cyrille Pitchen CC: linux-pci@vger.kernel.org CC: linux-kernel@vger.kernel.org --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 3e86fa3c7da3..4bbd26e8a9e2 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -175,7 +175,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) goto err_map_addr; } - memcpy(buf, src_addr, reg->size); + memcpy_fromio(buf, src_addr, reg->size); crc32 = crc32_le(~0, buf, reg->size); if (crc32 != reg->checksum) @@ -230,7 +230,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) get_random_bytes(buf, reg->size); reg->checksum = crc32_le(~0, buf, reg->size); - memcpy(dst_addr, buf, reg->size); + memcpy_toio(dst_addr, buf, reg->size); /* * wait 1ms inorder for the write to complete. Without this delay L3 -- cgit v1.2.3 From 41cb8d189c9d4964df52a6f497cab7b301ae831b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:44:59 +0530 Subject: PCI: endpoint: Add new pci_epc_ops to get EPC features Add a new pci_epc_ops ->get_features() to get the features supported by the EPC. Since EPC can provide different features to different functions, the ->get_features() ops takes _func_no_ as an argument. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epc-core.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci-epc.h | 22 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 094dcc3203b8..5a099479d9ab 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -83,6 +83,36 @@ err: } EXPORT_SYMBOL_GPL(pci_epc_get); +/** + * pci_epc_get_features() - get the features supported by EPC + * @epc: the features supported by *this* EPC device will be returned + * @func_no: the features supported by the EPC device specific to the + * endpoint function with func_no will be returned + * + * Invoke to get the features provided by the EPC which may be + * specific to an endpoint function. Returns pci_epc_features on success + * and NULL for any failures. + */ +const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, + u8 func_no) +{ + const struct pci_epc_features *epc_features; + unsigned long flags; + + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + return NULL; + + if (!epc->ops->get_features) + return NULL; + + spin_lock_irqsave(&epc->lock, flags); + epc_features = epc->ops->get_features(epc, func_no); + spin_unlock_irqrestore(&epc->lock, flags); + + return epc_features; +} +EXPORT_SYMBOL_GPL(pci_epc_get_features); + /** * pci_epc_stop() - stop the PCI link * @epc: the link of the EPC device that has to be stopped diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 37dab8116901..79fbcf94e14d 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -59,6 +59,8 @@ struct pci_epc_ops { enum pci_epc_irq_type type, u16 interrupt_num); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); + const struct pci_epc_features* (*get_features)(struct pci_epc *epc, + u8 func_no); struct module *owner; }; @@ -100,6 +102,24 @@ struct pci_epc { unsigned int features; }; +/** + * struct pci_epc_features - features supported by a EPC device per function + * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up + * @msi_capable: indicate if the endpoint function has MSI capability + * @msix_capable: indicate if the endpoint function has MSI-X capability + * @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver + * @bar_fixed_64bit: bitmap to indicate fixed 64bit BARs + * @bar_fixed_size: Array specifying the size supported by each BAR + */ +struct pci_epc_features { + unsigned int linkup_notifier : 1; + unsigned int msi_capable : 1; + unsigned int msix_capable : 1; + u8 reserved_bar; + u8 bar_fixed_64bit; + u64 bar_fixed_size[BAR_5 + 1]; +}; + #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) @@ -158,6 +178,8 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, 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); +const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, + u8 func_no); struct pci_epc *pci_epc_get(const char *epc_name); void pci_epc_put(struct pci_epc *epc); -- cgit v1.2.3 From fee35cb76a54c87985410ea6aa12002e5d38b367 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:00 +0530 Subject: PCI: dwc: Add ->get_features() callback function to dw_pcie_ep_ops Each platform using Designware PCIe core can support different set of endpoint features. Add a new callback function ->get_features() in dw_pcie_ep_ops so that each platform using Designware PCIe core can advertise its supported features to the endpoint function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-ep.c | 12 ++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index a543c45c7224..7a2925a16ab8 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -355,6 +355,17 @@ static int dw_pcie_ep_start(struct pci_epc *epc) return pci->ops->start_link(pci); } +static const struct pci_epc_features* +dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + + if (!ep->ops->get_features) + return NULL; + + return ep->ops->get_features(ep); +} + static const struct pci_epc_ops epc_ops = { .write_header = dw_pcie_ep_write_header, .set_bar = dw_pcie_ep_set_bar, @@ -368,6 +379,7 @@ static const struct pci_epc_ops epc_ops = { .raise_irq = dw_pcie_ep_raise_irq, .start = dw_pcie_ep_start, .stop = dw_pcie_ep_stop, + .get_features = dw_pcie_ep_get_features, }; int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 9943d8c68335..1f56e6ae34ff 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -192,6 +192,7 @@ 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, u16 interrupt_num); + const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); }; struct dw_pcie_ep { -- cgit v1.2.3 From 3b4322e589a630fe35944ced5852655fcc4a5d24 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:01 +0530 Subject: PCI: designware-plat: Populate ->get_features() dw_pcie_ep_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by Designware PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-plat.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index c12bf794d69c..bd0516afc86f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -100,9 +100,22 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +static const struct pci_epc_features dw_plat_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = true, +}; + +static const struct pci_epc_features* +dw_plat_pcie_get_features(struct dw_pcie_ep *ep) +{ + return &dw_plat_pcie_epc_features; +} + static struct dw_pcie_ep_ops pcie_ep_ops = { .ep_init = dw_plat_pcie_ep_init, .raise_irq = dw_plat_pcie_ep_raise_irq, + .get_features = dw_plat_pcie_get_features, }; static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, -- cgit v1.2.3 From 4894467e78619232a79e39c2f26ae8378c4500ed Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:02 +0530 Subject: PCI: pci-dra7xx: Populate ->get_features() dw_pcie_ep_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by DRA7xx PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index a32d6dde7a57..15620cfa617b 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -389,9 +389,22 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +static const struct pci_epc_features dra7xx_pcie_epc_features = { + .linkup_notifier = true, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +dra7xx_pcie_get_features(struct dw_pcie_ep *ep) +{ + return &dra7xx_pcie_epc_features; +} + static struct dw_pcie_ep_ops pcie_ep_ops = { .ep_init = dra7xx_pcie_ep_init, .raise_irq = dra7xx_pcie_raise_irq, + .get_features = dra7xx_pcie_get_features, }; static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, -- cgit v1.2.3 From 146221768c74bbd969f968b61ec95a0254a6b311 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:03 +0530 Subject: PCI: rockchip: Populate ->get_features() dw_pcie_ep_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by Rockchip PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-rockchip-ep.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index b8163c56a142..9b60ad323ac7 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -505,6 +505,18 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc) return 0; } +static const struct pci_epc_features rockchip_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + return &rockchip_pcie_epc_features; +} + static const struct pci_epc_ops rockchip_pcie_epc_ops = { .write_header = rockchip_pcie_ep_write_header, .set_bar = rockchip_pcie_ep_set_bar, @@ -515,6 +527,7 @@ static const struct pci_epc_ops rockchip_pcie_epc_ops = { .get_msi = rockchip_pcie_ep_get_msi, .raise_irq = rockchip_pcie_ep_raise_irq, .start = rockchip_pcie_ep_start, + .get_features = rockchip_pcie_ep_get_features, }; static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, -- cgit v1.2.3 From 67c777e6015d857a5e9662c68281d83d946d9b70 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:04 +0530 Subject: PCI: cadence: Populate ->get_features() cdns_pcie_epc_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by Cadence PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-ep.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index c3a088910f48..14c2545bb17e 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -411,6 +411,18 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) return 0; } +static const struct pci_epc_features cdns_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + return &cdns_pcie_epc_features; +} + static const struct pci_epc_ops cdns_pcie_epc_ops = { .write_header = cdns_pcie_ep_write_header, .set_bar = cdns_pcie_ep_set_bar, @@ -421,6 +433,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = { .get_msi = cdns_pcie_ep_get_msi, .raise_irq = cdns_pcie_ep_raise_irq, .start = cdns_pcie_ep_start, + .get_features = cdns_pcie_ep_get_features, }; static const struct of_device_id cdns_pcie_ep_of_match[] = { -- cgit v1.2.3 From c528f7bd362b097eeeafa6fbbeccd9750b79c7ba Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 Jan 2019 20:07:45 +0300 Subject: Revert "PCI/PME: Implement runtime PM callbacks" This reverts commit 0e157e52860441cb26051f131dd0b5ae3187a07b. Heiner reported that the commit in question prevents his network adapter from triggering PME and waking up when network cable is plugged. The commit tried to prevent root port waking up from D3cold immediately but looks like disabing root port PME interrupt is not the right way to fix that issue so revert it now. The patch following proposes an alternative solution to that issue. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202103 Fixes: 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") Reported-by: Heiner Kallweit Tested-by: Heiner Kallweit Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki CC: stable@vger.kernel.org # v4.20+ --- drivers/pci/pcie/pme.c | 27 --------------------------- 1 file changed, 27 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 0dbcf429089f..1a8b85051b1b 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -432,31 +432,6 @@ static void pcie_pme_remove(struct pcie_device *srv) kfree(get_service_data(srv)); } -static int pcie_pme_runtime_suspend(struct pcie_device *srv) -{ - struct pcie_pme_service_data *data = get_service_data(srv); - - spin_lock_irq(&data->lock); - pcie_pme_interrupt_enable(srv->port, false); - pcie_clear_root_pme_status(srv->port); - data->noirq = true; - spin_unlock_irq(&data->lock); - - return 0; -} - -static int pcie_pme_runtime_resume(struct pcie_device *srv) -{ - struct pcie_pme_service_data *data = get_service_data(srv); - - spin_lock_irq(&data->lock); - pcie_pme_interrupt_enable(srv->port, true); - data->noirq = false; - spin_unlock_irq(&data->lock); - - return 0; -} - static struct pcie_port_service_driver pcie_pme_driver = { .name = "pcie_pme", .port_type = PCI_EXP_TYPE_ROOT_PORT, @@ -464,8 +439,6 @@ static struct pcie_port_service_driver pcie_pme_driver = { .probe = pcie_pme_probe, .suspend = pcie_pme_suspend, - .runtime_suspend = pcie_pme_runtime_suspend, - .runtime_resume = pcie_pme_runtime_resume, .resume = pcie_pme_resume, .remove = pcie_pme_remove, }; -- cgit v1.2.3 From 1e9efe6c9976552e88c6e6feaca3a78b8cf5aaf6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:05 +0530 Subject: PCI: endpoint: Add helper to get first unreserved BAR Add a helper function pci_epc_get_first_free_bar() to get the first unreserved BAR that can be used for endpoint function. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epc-core.c | 23 +++++++++++++++++++++++ include/linux/pci-epc.h | 2 ++ 2 files changed, 25 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 5a099479d9ab..e4712a0f249c 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -83,6 +83,29 @@ err: } EXPORT_SYMBOL_GPL(pci_epc_get); +/** + * pci_epc_get_first_free_bar() - helper to get first unreserved BAR + * @epc_features: pci_epc_features structure that holds the reserved bar bitmap + * + * Invoke to get the first unreserved BAR that can be used for endpoint + * function. For any incorrect value in reserved_bar return '0'. + */ +unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features + *epc_features) +{ + int free_bar; + + if (!epc_features) + return 0; + + free_bar = ffz(epc_features->reserved_bar); + if (free_bar > 5) + return 0; + + return free_bar; +} +EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar); + /** * pci_epc_get_features() - get the features supported by EPC * @epc: the features supported by *this* EPC device will be returned diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 79fbcf94e14d..94e1ecff98ce 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -180,6 +180,8 @@ int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, u8 func_no); +unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features + *epc_features); struct pci_epc *pci_epc_get(const char *epc_name); void pci_epc_put(struct pci_epc *epc); -- cgit v1.2.3 From 5544d67ed11245ccb64099deb32831308297bf6b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:06 +0530 Subject: PCI: endpoint: Fix pci_epf_alloc_space() to set correct MEM TYPE flags pci_epf_alloc_space() sets the MEM TYPE flags to indicate a 32-bit Base Address Register irrespective of the size. Fix it here to indicate 64-bit BAR if the size is > 2GB. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epf-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 825fa24427a3..8bfdcd291196 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -131,7 +131,9 @@ 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; + epf->bar[bar].flags |= upper_32_bits(size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; return space; } -- cgit v1.2.3 From 0342e9a797db42a7d4d083d10b5d3f38b0cfc193 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:07 +0530 Subject: PCI: pci-epf-test: Remove setting epf_bar flags in function driver Now that pci_epf_alloc_space() sets BAR MEM TYPE flags as 64Bit or 32Bit based on size, remove setting it in function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 4bbd26e8a9e2..ad0efa4446ba 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -406,10 +406,6 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; - 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, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); -- cgit v1.2.3 From b866c56b66d88a632e2fa6b922c4ea051937acbd Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:08 +0530 Subject: PCI: pci-epf-test: Do not allocate next BARs memory if current BAR is 64Bit It's useless to allocate memory for next BAR if the current BAR is a 64Bit BAR. Stop allocating memory for the next BAR, if the current BARs flag indicates this is a 64Bit BAR. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index ad0efa4446ba..a385927a9239 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -429,6 +429,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct device *dev = &epf->dev; + struct pci_epf_bar *epf_bar; void *base; int bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar; @@ -442,6 +443,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) epf_test->reg[test_reg_bar] = base; for (bar = BAR_0; bar <= BAR_5; bar++) { + epf_bar = &epf->bar[bar]; if (bar == test_reg_bar) continue; base = pci_epf_alloc_space(epf, bar_size[bar], bar); @@ -449,6 +451,8 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) dev_err(dev, "Failed to allocate space for BAR%d\n", bar); epf_test->reg[bar] = base; + if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) + bar++; } return 0; -- cgit v1.2.3 From 2c04c5b8eef797dca99699cfb55ff42dd3c12c23 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:09 +0530 Subject: PCI: pci-epf-test: Use pci_epc_get_features() to get EPC features Use pci_epc_get_features() to get EPC features such as linkup notifier support, MSI/MSIX capable, BAR configuration etc and use it for configuring pci-epf-test. Since these features are now obtained directly from EPC driver, remove pci_epf_test_data which was initially added to have EPC features in endpoint function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 87 +++++++++++++++++---------- 1 file changed, 54 insertions(+), 33 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index a385927a9239..d0b91da49bf4 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -47,9 +47,8 @@ struct pci_epf_test { void *reg[6]; struct pci_epf *epf; enum pci_barno test_reg_bar; - bool linkup_notifier; - bool msix_available; struct delayed_work cmd_handler; + const struct pci_epc_features *epc_features; }; struct pci_epf_test_reg { @@ -71,11 +70,6 @@ static struct pci_epf_header test_header = { .interrupt_pin = PCI_INTERRUPT_INTA, }; -struct pci_epf_test_data { - enum pci_barno test_reg_bar; - bool linkup_notifier; -}; - static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; static int pci_epf_test_copy(struct pci_epf_test *epf_test) @@ -402,10 +396,16 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) struct device *dev = &epf->dev; struct pci_epf_test *epf_test = epf_get_drvdata(epf); enum pci_barno test_reg_bar = epf_test->test_reg_bar; + const struct pci_epc_features *epc_features; + + epc_features = epf_test->epc_features; for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; + if (!!(epc_features->reserved_bar & (1 << bar))) + continue; + ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); @@ -433,6 +433,9 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) void *base; int bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar; + const struct pci_epc_features *epc_features; + + epc_features = epf_test->epc_features; base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg), test_reg_bar); @@ -446,6 +449,10 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) epf_bar = &epf->bar[bar]; if (bar == test_reg_bar) continue; + + if (!!(epc_features->reserved_bar & (1 << bar))) + continue; + base = pci_epf_alloc_space(epf, bar_size[bar], bar); if (!base) dev_err(dev, "Failed to allocate space for BAR%d\n", @@ -458,25 +465,50 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) return 0; } +static void pci_epf_configure_bar(struct pci_epf *epf, + const struct pci_epc_features *epc_features) +{ + struct pci_epf_bar *epf_bar; + bool bar_fixed_64bit; + int i; + + for (i = BAR_0; i <= BAR_5; i++) { + epf_bar = &epf->bar[i]; + bar_fixed_64bit = !!(epc_features->bar_fixed_64bit & (1 << i)); + if (bar_fixed_64bit) + epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + if (epc_features->bar_fixed_size[i]) + bar_size[i] = epc_features->bar_fixed_size[i]; + } +} + static int pci_epf_test_bind(struct pci_epf *epf) { int ret; struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epf_header *header = epf->header; + const struct pci_epc_features *epc_features; + enum pci_barno test_reg_bar = BAR_0; struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; + bool linkup_notifier = false; + bool msix_capable = false; + bool msi_capable = true; if (WARN_ON_ONCE(!epc)) return -EINVAL; - if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER) - epf_test->linkup_notifier = false; - else - epf_test->linkup_notifier = true; - - epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE; + epc_features = pci_epc_get_features(epc, epf->func_no); + if (epc_features) { + linkup_notifier = epc_features->linkup_notifier; + msix_capable = epc_features->msix_capable; + msi_capable = epc_features->msi_capable; + test_reg_bar = pci_epc_get_first_free_bar(epc_features); + pci_epf_configure_bar(epf, epc_features); + } - epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features); + epf_test->test_reg_bar = test_reg_bar; + epf_test->epc_features = epc_features; ret = pci_epc_write_header(epc, epf->func_no, header); if (ret) { @@ -492,13 +524,15 @@ static int pci_epf_test_bind(struct pci_epf *epf) if (ret) return ret; - ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); - if (ret) { - dev_err(dev, "MSI configuration failed\n"); - return ret; + if (msi_capable) { + ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); + if (ret) { + dev_err(dev, "MSI configuration failed\n"); + return ret; + } } - if (epf_test->msix_available) { + if (msix_capable) { ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts); if (ret) { dev_err(dev, "MSI-X configuration failed\n"); @@ -506,7 +540,7 @@ static int pci_epf_test_bind(struct pci_epf *epf) } } - if (!epf_test->linkup_notifier) + if (!linkup_notifier) queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); return 0; @@ -523,17 +557,6 @@ static int pci_epf_test_probe(struct pci_epf *epf) { struct pci_epf_test *epf_test; struct device *dev = &epf->dev; - const struct pci_epf_device_id *match; - struct pci_epf_test_data *data; - enum pci_barno test_reg_bar = BAR_0; - bool linkup_notifier = true; - - match = pci_epf_match_device(pci_epf_test_ids, epf); - data = (struct pci_epf_test_data *)match->driver_data; - if (data) { - test_reg_bar = data->test_reg_bar; - linkup_notifier = data->linkup_notifier; - } epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL); if (!epf_test) @@ -541,8 +564,6 @@ static int pci_epf_test_probe(struct pci_epf *epf) epf->header = &test_header; epf_test->epf = epf; - epf_test->test_reg_bar = test_reg_bar; - epf_test->linkup_notifier = linkup_notifier; INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler); -- cgit v1.2.3 From c274c9f4ea758bd6667a363f275e45275f2b5672 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:10 +0530 Subject: PCI: cadence: Remove pci_epf_linkup() from Cadence EP driver pci_epf_linkup() is intended to be invoked if the EPC supports linkup notification. Now that pci-epf-test uses the get_features() callback, which indicates Cadence EP driver doesn't support the linkup notification, remove pci_epf_linkup() from Cadence EP driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-ep.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index 14c2545bb17e..def7820cb824 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -396,18 +396,6 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) cfg |= BIT(epf->func_no); cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); - /* - * The PCIe links are automatically established by the controller - * once for all at powerup: the software can neither start nor stop - * those links later at runtime. - * - * Then we only have to notify the EP core that our links are already - * established. However we don't call directly pci_epc_linkup() because - * we've already locked the epc->lock. - */ - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - return 0; } -- cgit v1.2.3 From f1267978457e77ae7ddabe82ad48cf34f54e96fb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:11 +0530 Subject: PCI: rockchip: Remove pci_epf_linkup() from Rockchip EP driver pci_epf_linkup() is intended to be invoked if the EPC supports linkup notification. Now that pci-epf-test uses get_features callback, which indicates Rockchip EP driver doesn't support linkup notification, remove pci_epf_linkup() from Rockchip EP driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Acked-by: Shawn Lin --- drivers/pci/controller/pcie-rockchip-ep.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 9b60ad323ac7..a5d799e2dff2 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -499,9 +499,6 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc) rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG); - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - return 0; } -- cgit v1.2.3 From 92f2b028418c6f59f92703eed111136ca0d22c39 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:12 +0530 Subject: PCI: designware-plat: Remove setting epc->features in Designware plat EP driver Now that pci-epf-test uses get_features callback and dw_plat_pcie_epc_features in Designware plat EP driver already indicates it doesn't support linkup notification and is MSIX capable, remove setting epc->features which is not used anymore by the endpoint function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-plat.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index bd0516afc86f..3be87126aef3 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -70,14 +70,10 @@ 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; - epc->features |= EPC_FEATURE_MSIX_AVAILABLE; } static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, -- cgit v1.2.3 From c89f7f98c971e0cabc819b6c0fe6bf509287b7e0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 15 Feb 2019 13:49:18 -0600 Subject: PCI/portdrv: Use conventional Device ID table formatting The pci_device_id table was technically correct, but unusually formatted, which made adding entries error-prone. Change the format so it's obvious how to add entries. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 0acca3596807..a289e734b9a4 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -182,10 +182,10 @@ static void pcie_portdrv_err_resume(struct pci_dev *dev) /* * LINUX Device Driver Model */ -static const struct pci_device_id port_pci_ids[] = { { +static const struct pci_device_id port_pci_ids[] = { /* handle any PCI-Express port */ - PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0), - }, { /* end: all zeroes */ } + { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) }, + { }, }; static const struct pci_error_handlers pcie_portdrv_err_handler = { -- cgit v1.2.3 From f0cfecea8d1e8e0cd5d5053f9452b3a450f49eb5 Mon Sep 17 00:00:00 2001 From: Honghui Zhang Date: Thu, 14 Feb 2019 13:21:17 +0800 Subject: PCI/portdrv: Support PCIe services on subtractive decode bridges The Class Code for subtractive decode PCI-to-PCI bridge is 060401h; add an entry to make portdrv support this type of bridge. This allows use of PCIe services on subtractive decode ports. Signed-off-by: Honghui Zhang [bhelgaas: add braces surrounding entry] Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index a289e734b9a4..99d2abe88d0b 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -185,6 +185,8 @@ static void pcie_portdrv_err_resume(struct pci_dev *dev) static const struct pci_device_id port_pci_ids[] = { /* handle any PCI-Express port */ { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) }, + /* subtractive decode PCI-to-PCI bridge, class type is 060401h */ + { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x01), ~0) }, { }, }; -- cgit v1.2.3 From bbe54ea5330d828cc396d451c0e1e5c3f9764c1e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 Jan 2019 20:07:46 +0300 Subject: PCI: pciehp: Disable Data Link Layer State Changed event on suspend Commit 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") tried to solve an issue where the hierarchy immediately wakes up when it is transitioned into D3cold. However, it turns out to prevent PME propagation on some systems that do not support D3cold. I looked more closely at what might cause the immediate wakeup. It happens when the ACPI power resource of the root port is turned off. The AML code associated with the _OFF() method of the ACPI power resource starts a PCIe L2/L3 Ready transition and waits for it to complete. Right after the L2/L3 Ready transition is started the root port receives a PME from the downstream port. The simplest hierarchy where this happens looks like this: 00:1d.0 PCIe Root Port ^ | v 05:00.0 PCIe switch #1 upstream port 06:01.0 PCIe switch #1 downstream hotplug port ^ | v 08:00.0 PCIe switch #2 upstream port It seems that the PCIe link between the two switches, before PME_Turn_Off/PME_TO_Ack is complete for the whole hierarchy, goes inactive and triggers PME towards the root port bringing it back to D0. The L2/L3 Ready sequence is described in PCIe r4.0 spec sections 5.2 and 5.3.3 but unfortunately they do not state what happens if DLLSCE is enabled during the sequence. Disabling Data Link Layer State Changed event (DLLSCE) seems to prevent the issue and still allows the downstream hotplug port to notice when a device is plugged/unplugged. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202593 Fixes: 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki CC: stable@vger.kernel.org # v4.20+ --- drivers/pci/hotplug/pciehp_hpc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7dd443aea5a5..c0fb64ace05a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -736,12 +736,25 @@ void pcie_clear_hotplug_events(struct controller *ctrl) void pcie_enable_interrupt(struct controller *ctrl) { - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE); + u16 mask; + + mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; + pcie_write_cmd(ctrl, mask, mask); } void pcie_disable_interrupt(struct controller *ctrl) { - pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE); + u16 mask; + + /* + * Mask hot-plug interrupt to prevent it triggering immediately + * when the link goes inactive (we still get PME when any of the + * enabled events is detected). Same goes with Link Layer State + * changed event which generates PME immediately when the link goes + * inactive so mask it as well. + */ + mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; + pcie_write_cmd(ctrl, 0, mask); } /* -- cgit v1.2.3 From a805770d8a22c59e72149aabed7e40ece6d627c8 Mon Sep 17 00:00:00 2001 From: Xiaowei Bao Date: Thu, 21 Feb 2019 11:16:19 +0800 Subject: PCI: layerscape: Add EP mode support Add the PCIe EP mode support to the layerscape platform controller. Signed-off-by: Xiaowei Bao Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Zhiqiang Hou Reviewed-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/Makefile | 2 +- drivers/pci/controller/dwc/pci-layerscape-ep.c | 156 +++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/controller/dwc/pci-layerscape-ep.c (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 7bcdcdf5024e..b5f3b83cc2b3 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o -obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c new file mode 100644 index 000000000000..a42c9c3ae1cc --- /dev/null +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe controller EP driver for Freescale Layerscape SoCs + * + * Copyright (C) 2018 NXP Semiconductor. + * + * Author: Xiaowei Bao + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define PCIE_DBI2_OFFSET 0x1000 /* DBI2 base address*/ + +struct ls_pcie_ep { + struct dw_pcie *pci; +}; + +#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev) + +static int ls_pcie_establish_link(struct dw_pcie *pci) +{ + return 0; +} + +static const struct dw_pcie_ops ls_pcie_ep_ops = { + .start_link = ls_pcie_establish_link, +}; + +static const struct of_device_id ls_pcie_ep_of_match[] = { + { .compatible = "fsl,ls-pcie-ep",}, + { }, +}; + +static const struct pci_epc_features ls_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +ls_pcie_ep_get_features(struct dw_pcie_ep *ep) +{ + return &ls_pcie_epc_features; +} + +static void ls_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar; + + for (bar = BAR_0; bar <= BAR_5; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + enum pci_epc_irq_type type, u16 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + 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: + return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + return -EINVAL; + } +} + +static struct dw_pcie_ep_ops pcie_ep_ops = { + .ep_init = ls_pcie_ep_init, + .raise_irq = ls_pcie_ep_raise_irq, + .get_features = ls_pcie_ep_get_features, +}; + +static int __init ls_add_pcie_ep(struct ls_pcie_ep *pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + struct dw_pcie_ep *ep; + struct resource *res; + int ret; + + ep = &pci->ep; + ep->ops = &pcie_ep_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "failed to initialize endpoint\n"); + return ret; + } + + return 0; +} + +static int __init ls_pcie_ep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct ls_pcie_ep *pcie; + struct resource *dbi_base; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_OFFSET; + pci->dev = dev; + pci->ops = &ls_pcie_ep_ops; + pcie->pci = pci; + + platform_set_drvdata(pdev, pcie); + + ret = ls_add_pcie_ep(pcie, pdev); + + return ret; +} + +static struct platform_driver ls_pcie_ep_driver = { + .driver = { + .name = "layerscape-pcie-ep", + .of_match_table = ls_pcie_ep_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe); -- cgit v1.2.3 From 9f08a5d896ce43380314c34ed3f264c8e6075b80 Mon Sep 17 00:00:00 2001 From: Dongdong Liu Date: Mon, 11 Feb 2019 15:02:59 +0800 Subject: PCI/DPC: Fix print AER status in DPC event handling Previously dpc_handler() called aer_get_device_error_info() without initializing info->severity, so aer_get_device_error_info() relied on uninitialized data. Add dpc_get_aer_uncorrect_severity() to read the port's AER status, mask, and severity registers and set info->severity. Also, clear the port's AER fatal error status bits. Fixes: 8aefa9b0d910 ("PCI/DPC: Print AER status in DPC event handling") Signed-off-by: Dongdong Liu [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch Cc: stable@vger.kernel.org # v4.19+ --- drivers/pci/pcie/dpc.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index e435d12e61a0..7b77754a82de 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -202,6 +202,28 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc) pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status); } +static int dpc_get_aer_uncorrect_severity(struct pci_dev *dev, + struct aer_err_info *info) +{ + int pos = dev->aer_cap; + u32 status, mask, sev; + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); + status &= ~mask; + if (!status) + return 0; + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev); + status &= sev; + if (status) + info->severity = AER_FATAL; + else + info->severity = AER_NONFATAL; + + return 1; +} + static irqreturn_t dpc_handler(int irq, void *context) { struct aer_err_info info; @@ -229,9 +251,12 @@ static irqreturn_t dpc_handler(int irq, void *context) /* show RP PIO error detail information */ if (dpc->rp_extensions && reason == 3 && ext_reason == 0) dpc_process_rp_pio_error(dpc); - else if (reason == 0 && aer_get_device_error_info(pdev, &info)) { + else if (reason == 0 && + dpc_get_aer_uncorrect_severity(pdev, &info) && + aer_get_device_error_info(pdev, &info)) { aer_print_error(pdev, &info); pci_cleanup_aer_uncorrect_error_status(pdev); + pci_aer_clear_fatal_status(pdev); } /* We configure DPC so it only triggers on ERR_FATAL */ -- cgit v1.2.3 From 59f81c35e0df840f7112cb296dde48df84a67c79 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 20 Feb 2019 10:48:40 +0100 Subject: PCI: pci-bridge-emul: Create per-bridge copy of register behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The behavior of the different registers of the PCI-to-PCI bridge is currently encoded in two global arrays, shared by all instances of PCI-to-PCI bridge emulation. However, we will need to tweak the behavior on a per-bridge basis, to accommodate for different capabilities of the platforms where this code is used. In preparation for this, create a per-bridge copy of the register behavior arrays, so that they can later be tweaked on a per-bridge basis. Fixes: 1f08673eef123 ("PCI: mvebu: Convert to PCI emulated bridge config space") Reported-by: Luís Mendes Reported-by: Leigh Brown Tested-by: Leigh Brown Tested-by: Luis Mendes Signed-off-by: Thomas Petazzoni Signed-off-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org Cc: Luís Mendes Cc: Leigh Brown --- drivers/pci/pci-bridge-emul.c | 80 ++++++++++++++++++++++++++++--------------- drivers/pci/pci-bridge-emul.h | 8 ++++- 2 files changed, 60 insertions(+), 28 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index 129738362d90..dd8d8060317e 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -24,29 +24,6 @@ #define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2) -/* - * Initialize a pci_bridge_emul structure to represent a fake PCI - * bridge configuration space. The caller needs to have initialized - * the PCI configuration space with whatever values make sense - * (typically at least vendor, device, revision), the ->ops pointer, - * and optionally ->data and ->has_pcie. - */ -void pci_bridge_emul_init(struct pci_bridge_emul *bridge) -{ - bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16; - bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; - bridge->conf.cache_line_size = 0x10; - bridge->conf.status = PCI_STATUS_CAP_LIST; - - if (bridge->has_pcie) { - bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; - bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; - /* Set PCIe v2, root port, slot support */ - bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | - PCI_EXP_FLAGS_SLOT; - } -} - struct pci_bridge_reg_behavior { /* Read-only bits */ u32 ro; @@ -283,6 +260,55 @@ const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = { }, }; +/* + * Initialize a pci_bridge_emul structure to represent a fake PCI + * bridge configuration space. The caller needs to have initialized + * the PCI configuration space with whatever values make sense + * (typically at least vendor, device, revision), the ->ops pointer, + * and optionally ->data and ->has_pcie. + */ +int pci_bridge_emul_init(struct pci_bridge_emul *bridge) +{ + bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16; + bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; + bridge->conf.cache_line_size = 0x10; + bridge->conf.status = PCI_STATUS_CAP_LIST; + bridge->pci_regs_behavior = kmemdup(pci_regs_behavior, + sizeof(pci_regs_behavior), + GFP_KERNEL); + if (!bridge->pci_regs_behavior) + return -ENOMEM; + + if (bridge->has_pcie) { + bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; + bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; + /* Set PCIe v2, root port, slot support */ + bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | + PCI_EXP_FLAGS_SLOT; + bridge->pcie_cap_regs_behavior = + kmemdup(pcie_cap_regs_behavior, + sizeof(pcie_cap_regs_behavior), + GFP_KERNEL); + if (!bridge->pcie_cap_regs_behavior) { + kfree(bridge->pci_regs_behavior); + return -ENOMEM; + } + } + + return 0; +} + +/* + * Cleanup a pci_bridge_emul structure that was previously initilized + * using pci_bridge_emul_init(). + */ +void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge) +{ + if (bridge->has_pcie) + kfree(bridge->pcie_cap_regs_behavior); + kfree(bridge->pci_regs_behavior); +} + /* * Should be called by the PCI controller driver when reading the PCI * configuration space of the fake bridge. It will call back the @@ -312,11 +338,11 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, reg -= PCI_CAP_PCIE_START; read_op = bridge->ops->read_pcie; cfgspace = (u32 *) &bridge->pcie_conf; - behavior = pcie_cap_regs_behavior; + behavior = bridge->pcie_cap_regs_behavior; } else { read_op = bridge->ops->read_base; cfgspace = (u32 *) &bridge->conf; - behavior = pci_regs_behavior; + behavior = bridge->pci_regs_behavior; } if (read_op) @@ -383,11 +409,11 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, reg -= PCI_CAP_PCIE_START; write_op = bridge->ops->write_pcie; cfgspace = (u32 *) &bridge->pcie_conf; - behavior = pcie_cap_regs_behavior; + behavior = bridge->pcie_cap_regs_behavior; } else { write_op = bridge->ops->write_base; cfgspace = (u32 *) &bridge->conf; - behavior = pci_regs_behavior; + behavior = bridge->pci_regs_behavior; } /* Keep all bits, except the RW bits */ diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index 9d510ccf738b..f04637bb3222 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -107,15 +107,21 @@ struct pci_bridge_emul_ops { u32 old, u32 new, u32 mask); }; +struct pci_bridge_reg_behavior; + struct pci_bridge_emul { struct pci_bridge_emul_conf conf; struct pci_bridge_emul_pcie_conf pcie_conf; struct pci_bridge_emul_ops *ops; + struct pci_bridge_reg_behavior *pci_regs_behavior; + struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; void *data; bool has_pcie; }; -void pci_bridge_emul_init(struct pci_bridge_emul *bridge); +int pci_bridge_emul_init(struct pci_bridge_emul *bridge); +void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge); + int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, int size, u32 *value); int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, -- cgit v1.2.3 From 33776d059630e5045ea9ccf756c74de8f9cc86de Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 20 Feb 2019 10:48:41 +0100 Subject: PCI: pci-bridge-emul: Extend pci_bridge_emul_init() with flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Depending on the capabilities of the PCI controller/platform, the PCI-to-PCI bridge emulation behavior might need to be different. For example, on platforms that use the pci-mvebu code, we currently don't support prefetchable memory BARs, so the corresponding fields in the PCI-to-PCI bridge configuration space should be read-only. To implement this, extend pci_bridge_emul_init() to take a "flags" argument, with currently one flag supported: PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR that will make the prefetchable memory base and limit registers read-only. The pci-mvebu and pci-aardvark drivers are updated accordingly. Fixes: 1f08673eef123 ("PCI: mvebu: Convert to PCI emulated bridge config space") Reported-by: Luís Mendes Reported-by: Leigh Brown Tested-by: Leigh Brown Tested-by: Luis Mendes Signed-off-by: Thomas Petazzoni Signed-off-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org Cc: Luís Mendes Cc: Leigh Brown --- drivers/pci/controller/pci-aardvark.c | 2 +- drivers/pci/controller/pci-mvebu.c | 2 +- drivers/pci/pci-bridge-emul.c | 8 +++++++- drivers/pci/pci-bridge-emul.h | 7 ++++++- 4 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 750081c1cb48..6eecae447af3 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -499,7 +499,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie) bridge->data = pcie; bridge->ops = &advk_pci_bridge_emul_ops; - pci_bridge_emul_init(bridge); + pci_bridge_emul_init(bridge, 0); } diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index fa0fc46edb0c..d3a0419e42f2 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -583,7 +583,7 @@ static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops; - pci_bridge_emul_init(bridge); + pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR); } static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index dd8d8060317e..83fb077d0b41 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -267,7 +267,8 @@ const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = { * (typically at least vendor, device, revision), the ->ops pointer, * and optionally ->data and ->has_pcie. */ -int pci_bridge_emul_init(struct pci_bridge_emul *bridge) +int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + unsigned int flags) { bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16; bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; @@ -295,6 +296,11 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge) } } + if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) { + bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0; + bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0; + } + return 0; } diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index f04637bb3222..e65b1b79899d 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -119,7 +119,12 @@ struct pci_bridge_emul { bool has_pcie; }; -int pci_bridge_emul_init(struct pci_bridge_emul *bridge); +enum { + PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR = BIT(0), +}; + +int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + unsigned int flags); void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge); int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, -- cgit v1.2.3 From 02b485e31d98265189b91f3e69c43df2ed50610c Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 25 Jan 2019 15:26:16 -0800 Subject: PCI: qcom: Don't deassert reset GPIO during probe Acquiring the reset GPIO low means that reset is being deasserted, this is followed almost immediately with qcom_pcie_host_init() asserting it, initializing it and then finally deasserting it again, for the link to come up. Some PCIe devices requires a minimum time between the initial deassert and subsequent reset cycles. In a platform that boots with the reset GPIO asserted this requirement is being violated by this deassert/assert pulse. Acquire the reset GPIO high to prevent this situation by matching the state to the subsequent asserted state. Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Signed-off-by: Bjorn Andersson [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov Cc: stable@vger.kernel.org --- drivers/pci/controller/dwc/pcie-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index d185ea5fe996..a7f703556790 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1228,7 +1228,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) pcie->ops = of_device_get_match_data(dev); - pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); + pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); if (IS_ERR(pcie->reset)) { ret = PTR_ERR(pcie->reset); goto err_pm_runtime_put; -- cgit v1.2.3 From 95c80bc6952b6a5badc7b702d23e5bf14d251e7c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 28 Feb 2019 13:56:27 -0600 Subject: PCI/PME: Fix hotplug/sysfs remove deadlock in pcie_pme_remove() Dongdong reported a deadlock triggered by a hotplug event during a sysfs "remove" operation: pciehp 0000:00:0c.0:pcie004: Slot(0-1): Link Up # echo 1 > 0000:00:0c.0/remove PME and hotplug share an MSI/MSI-X vector. The sysfs "remove" side is: remove_store pci_stop_and_remove_bus_device_locked pci_lock_rescan_remove pci_stop_and_remove_bus_device ... pcie_pme_remove pcie_pme_suspend synchronize_irq # wait for hotplug IRQ handler pci_unlock_rescan_remove The hotplug side is: pciehp_ist pciehp_handle_presence_or_link_change pciehp_configure_device pci_lock_rescan_remove # wait for pci_unlock_rescan_remove() INFO: task bash:10913 blocked for more than 120 seconds. # ps -ax |grep D PID TTY STAT TIME COMMAND 10913 ttyAMA0 Ds+ 0:00 -bash 14022 ? D 0:00 [irq/745-pciehp] # cat /proc/14022/stack __switch_to+0x94/0xd8 pci_lock_rescan_remove+0x20/0x28 pciehp_configure_device+0x30/0x140 pciehp_handle_presence_or_link_change+0x324/0x458 pciehp_ist+0x1dc/0x1e0 # cat /proc/10913/stack __switch_to+0x94/0xd8 synchronize_irq+0x8c/0xc0 pcie_pme_suspend+0xa4/0x118 pcie_pme_remove+0x20/0x40 pcie_port_remove_service+0x3c/0x58 ... pcie_port_device_remove+0x2c/0x48 pcie_portdrv_remove+0x68/0x78 pci_device_remove+0x48/0x120 ... pci_stop_bus_device+0x84/0xc0 pci_stop_and_remove_bus_device_locked+0x24/0x40 remove_store+0xa4/0xb8 dev_attr_store+0x44/0x60 sysfs_kf_write+0x58/0x80 It is incorrect to call pcie_pme_suspend() from pcie_pme_remove() for two reasons. First, pcie_pme_suspend() calls synchronize_irq(), which will wait for the native hotplug interrupt handler as well as for the PME one, because they share one IRQ (as per the spec). That may deadlock if hotplug is signaled while pcie_pme_remove() is running and the latter calls pci_lock_rescan_remove() before the former. Second, if pcie_pme_suspend() figures out that wakeup needs to be enabled for the port, it will return without disabling the interrupt as expected by pcie_pme_remove() which was overlooked by commit c7b5a4e6e8fb ("PCI / PM: Fix native PME handling during system suspend/resume"). To fix that, rework pcie_pme_remove() to disable the PME interrupt, clear its status and prevent the PME worker function from re-enabling it before calling free_irq() on it, which should be sufficient. Fixes: c7b5a4e6e8fb ("PCI / PM: Fix native PME handling during system suspend/resume") Link: https://lore.kernel.org/linux-pci/c7697e7c-e1af-13e4-8491-0a3996e6ab5d@huawei.com Reported-by: Dongdong Liu Signed-off-by: Rafael J. Wysocki [bhelgaas: add URL and deadlock details from Dongdong] Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/pme.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 1a8b85051b1b..efa5b552914b 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -363,6 +363,16 @@ static bool pcie_pme_check_wakeup(struct pci_bus *bus) return false; } +static void pcie_pme_disable_interrupt(struct pci_dev *port, + struct pcie_pme_service_data *data) +{ + spin_lock_irq(&data->lock); + pcie_pme_interrupt_enable(port, false); + pcie_clear_root_pme_status(port); + data->noirq = true; + spin_unlock_irq(&data->lock); +} + /** * pcie_pme_suspend - Suspend PCIe PME service device. * @srv: PCIe service device to suspend. @@ -387,11 +397,7 @@ static int pcie_pme_suspend(struct pcie_device *srv) return 0; } - spin_lock_irq(&data->lock); - pcie_pme_interrupt_enable(port, false); - pcie_clear_root_pme_status(port); - data->noirq = true; - spin_unlock_irq(&data->lock); + pcie_pme_disable_interrupt(port, data); synchronize_irq(srv->irq); @@ -427,9 +433,11 @@ static int pcie_pme_resume(struct pcie_device *srv) */ static void pcie_pme_remove(struct pcie_device *srv) { - pcie_pme_suspend(srv); + struct pcie_pme_service_data *data = get_service_data(srv); + + pcie_pme_disable_interrupt(srv->port, data); free_irq(srv->irq, srv); - kfree(get_service_data(srv)); + kfree(data); } static struct pcie_port_service_driver pcie_pme_driver = { -- cgit v1.2.3 From 5278f65107b15c999cb8a16ee0142015cff6a690 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 11 Feb 2019 17:51:08 -0800 Subject: PCI: imx6: Add code to request/control "pcie_aux" clock for i.MX8MQ The PCIe IP block has an additional clock, "pcie_aux", that needs to be controlled by the driver. Add code to support it. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu Cc: linux-imx@nxp.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pci@vger.kernel.org Cc: Rob Herring Cc: devicetree@vger.kernel.org --- drivers/pci/controller/dwc/pci-imx6.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 64c74334f7d0..f5a16fd15be5 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -66,6 +66,7 @@ struct imx6_pcie { struct clk *pcie_phy; struct clk *pcie_inbound_axi; struct clk *pcie; + struct clk *pcie_aux; struct regmap *iomuxc_gpr; u32 controller_id; struct reset_control *pciephy_reset; @@ -459,6 +460,12 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) case IMX7D: break; case IMX8MQ: + ret = clk_prepare_enable(imx6_pcie->pcie_aux); + if (ret) { + dev_err(dev, "unable to enable pcie_aux clock\n"); + break; + } + offset = imx6_pcie_grp_offset(imx6_pcie); /* * Set the over ride low and enabled @@ -976,6 +983,9 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); break; + case IMX8MQ: + clk_disable_unprepare(imx6_pcie->pcie_aux); + break; default: break; } @@ -1121,8 +1131,14 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->pcie_inbound_axi); } break; - case IMX7D: case IMX8MQ: + imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux"); + if (IS_ERR(imx6_pcie->pcie_aux)) { + dev_err(dev, "pcie_aux clock source missing or invalid\n"); + return PTR_ERR(imx6_pcie->pcie_aux); + } + /* fall through */ + case IMX7D: if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR) imx6_pcie->controller_id = 1; -- cgit v1.2.3 From 4f8bbd2f8e7c4f3112506bf7362aed3a5495d51b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 12:02:38 -0800 Subject: PCI: dwc: Make use of IS_ALIGNED() Make the intent a bit more clear as well as get rid of explicit arithmetic by using IS_ALIGNED() to determine if "addr" is aligned to "size". No functional change intended. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu Cc: linux-imx@nxp.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pci@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 93ef8c31fb39..67236379c61a 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -22,7 +22,7 @@ int dw_pcie_read(void __iomem *addr, int size, u32 *val) { - if ((uintptr_t)addr & (size - 1)) { + if (!IS_ALIGNED((uintptr_t)addr, size)) { *val = 0; return PCIBIOS_BAD_REGISTER_NUMBER; } @@ -43,7 +43,7 @@ int dw_pcie_read(void __iomem *addr, int size, u32 *val) int dw_pcie_write(void __iomem *addr, int size, u32 val) { - if ((uintptr_t)addr & (size - 1)) + if (!IS_ALIGNED((uintptr_t)addr, size)) return PCIBIOS_BAD_REGISTER_NUMBER; if (size == 4) -- cgit v1.2.3 From 689e349a1a6c495bace83489753ab1c15696f869 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 12:02:39 -0800 Subject: PCI: dwc: Share code for dw_pcie_rd/wr_other_conf() Default implementation of pcie_rd_other_conf() and dw_pcie_wd_other_conf() share more than 80% of their code. Move shared code into a dedicated subroutine and convert pcie_rd_other_conf() and dw_pcie_wd_other_conf() to use it. No functional change intended. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu Cc: linux-imx@nxp.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pci@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 61 +++++++++-------------- 1 file changed, 23 insertions(+), 38 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 45ff5e4f8af6..0c18ab63811f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -512,8 +512,9 @@ error: return ret; } -static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) +static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 *val, + bool write) { int ret, type; u32 busdev, cfg_size; @@ -521,9 +522,6 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, void __iomem *va_cfg_base; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - if (pp->ops->rd_other_conf) - return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val); - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | PCIE_ATU_FUNC(PCI_FUNC(devfn)); @@ -542,7 +540,11 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, type, cpu_addr, busdev, cfg_size); - ret = dw_pcie_read(va_cfg_base + where, size, val); + if (write) + ret = dw_pcie_write(va_cfg_base + where, size, *val); + else + ret = dw_pcie_read(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, PCIE_ATU_TYPE_IO, pp->io_base, @@ -551,43 +553,26 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, return ret; } +static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 *val) +{ + if (pp->ops->rd_other_conf) + return pp->ops->rd_other_conf(pp, bus, devfn, where, + size, val); + + return dw_pcie_access_other_conf(pp, bus, devfn, where, size, val, + false); +} + static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { - int ret, type; - u32 busdev, cfg_size; - u64 cpu_addr; - void __iomem *va_cfg_base; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - if (pp->ops->wr_other_conf) - return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val); - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); + return pp->ops->wr_other_conf(pp, bus, devfn, where, + size, val); - if (bus->parent->number == pp->root_bus_nr) { - type = PCIE_ATU_TYPE_CFG0; - cpu_addr = pp->cfg0_base; - cfg_size = pp->cfg0_size; - va_cfg_base = pp->va_cfg0_base; - } else { - type = PCIE_ATU_TYPE_CFG1; - cpu_addr = pp->cfg1_base; - cfg_size = pp->cfg1_size; - va_cfg_base = pp->va_cfg1_base; - } - - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - type, cpu_addr, - busdev, cfg_size); - ret = dw_pcie_write(va_cfg_base + where, size, val); - if (pci->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); - - return ret; + return dw_pcie_access_other_conf(pp, bus, devfn, where, size, &val, + true); } static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, -- cgit v1.2.3 From 0e11faa48b07a063289d65363015a3d51ca4c337 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 12:02:40 -0800 Subject: PCI: dwc: Make use of BIT() in constant definitions Avoid using explicit left shifts and convert various definitions to use BIT() instead. No functional change intended. Signed-off-by: Andrey Smirnov [lorenzo.pieralisi@arm.com: fixed PORT_LOGIC_SPEED_CHANGE redefinition] Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu Cc: linux-imx@nxp.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pci@vger.kernel.org --- drivers/pci/controller/dwc/pci-imx6.c | 1 - drivers/pci/controller/dwc/pcie-designware.c | 2 +- drivers/pci/controller/dwc/pcie-designware.h | 14 +++++++------- 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index f5a16fd15be5..5ae75f25c6fc 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -121,7 +121,6 @@ struct imx6_pcie { #define PCIE_PHY_STAT_ACK_LOC 16 #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) /* PHY registers (not memory-mapped) */ #define PCIE_PHY_ATEOVRD 0x10 diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 67236379c61a..31f6331ca46f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -306,7 +306,7 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index, } dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, (u32)~PCIE_ATU_ENABLE); } int dw_pcie_wait_for_link(struct dw_pcie *pci) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 279000255ad1..070382869685 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -41,7 +41,7 @@ #define PORT_LOGIC_LTSSM_STATE_L0 0x11 #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) +#define PORT_LOGIC_SPEED_CHANGE BIT(17) #define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) #define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) #define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) @@ -55,8 +55,8 @@ #define PCIE_MSI_INTR0_STATUS 0x830 #define PCIE_ATU_VIEWPORT 0x900 -#define PCIE_ATU_REGION_INBOUND (0x1 << 31) -#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#define PCIE_ATU_REGION_INBOUND BIT(31) +#define PCIE_ATU_REGION_OUTBOUND 0 #define PCIE_ATU_REGION_INDEX2 (0x2 << 0) #define PCIE_ATU_REGION_INDEX1 (0x1 << 0) #define PCIE_ATU_REGION_INDEX0 (0x0 << 0) @@ -66,8 +66,8 @@ #define PCIE_ATU_TYPE_CFG0 (0x4 << 0) #define PCIE_ATU_TYPE_CFG1 (0x5 << 0) #define PCIE_ATU_CR2 0x908 -#define PCIE_ATU_ENABLE (0x1 << 31) -#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_ENABLE BIT(31) +#define PCIE_ATU_BAR_MODE_ENABLE BIT(30) #define PCIE_ATU_LOWER_BASE 0x90C #define PCIE_ATU_UPPER_BASE 0x910 #define PCIE_ATU_LIMIT 0x914 @@ -78,7 +78,7 @@ #define PCIE_ATU_UPPER_TARGET 0x91C #define PCIE_MISC_CONTROL_1_OFF 0x8BC -#define PCIE_DBI_RO_WR_EN (0x1 << 0) +#define PCIE_DBI_RO_WR_EN BIT(0) /* * iATU Unroll-specific register definitions @@ -105,7 +105,7 @@ ((region) << 9) #define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ - (((region) << 9) | (0x1 << 8)) + (((region) << 9) | BIT(8)) #define MAX_MSI_IRQS 256 #define MAX_MSI_IRQS_PER_CTRL 32 -- cgit v1.2.3 From a991f748962da0562bc35d2c62d3853b194072e8 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 12:02:41 -0800 Subject: PCI: dwc: Make use of GENMASK/FIELD_PREP Convert various multi-bit fields to be defined using GENMASK/FIELD_PREP. This way bit field boundaries are defined in a single place only, as well as defined in a way that makes it easier to verify them against the reference manual. No functional change intended. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu Cc: linux-imx@nxp.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pci@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware.h | 29 +++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 070382869685..8ebfb06584d9 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -11,6 +11,7 @@ #ifndef _PCIE_DESIGNWARE_H #define _PCIE_DESIGNWARE_H +#include #include #include #include @@ -30,11 +31,12 @@ /* Synopsys-specific PCIe configuration registers */ #define PCIE_PORT_LINK_CONTROL 0x710 -#define PORT_LINK_MODE_MASK (0x3f << 16) -#define PORT_LINK_MODE_1_LANES (0x1 << 16) -#define PORT_LINK_MODE_2_LANES (0x3 << 16) -#define PORT_LINK_MODE_4_LANES (0x7 << 16) -#define PORT_LINK_MODE_8_LANES (0xf << 16) +#define PORT_LINK_MODE_MASK GENMASK(21, 16) +#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n) +#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) +#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3) +#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7) +#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf) #define PCIE_PORT_DEBUG0 0x728 #define PORT_LOGIC_LTSSM_STATE_MASK 0x1f @@ -42,11 +44,12 @@ #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C #define PORT_LOGIC_SPEED_CHANGE BIT(17) -#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) -#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) -#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) -#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) -#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) +#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8) +#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) +#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1) +#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2) +#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4) +#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8) #define PCIE_MSI_ADDR_LO 0x820 #define PCIE_MSI_ADDR_HI 0x824 @@ -72,9 +75,9 @@ #define PCIE_ATU_UPPER_BASE 0x910 #define PCIE_ATU_LIMIT 0x914 #define PCIE_ATU_LOWER_TARGET 0x918 -#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) -#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) -#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) +#define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x) +#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x) +#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x) #define PCIE_ATU_UPPER_TARGET 0x91C #define PCIE_MISC_CONTROL_1_OFF 0x8BC -- cgit v1.2.3 From 44ddb77b2fdc6352a45ebc7b5804e4afe9161956 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 12:02:42 -0800 Subject: PCI: dwc: Remove superfluous shifting in definitions Surrounding definitions no longer use explicit shift, so "<< 0" here serve no purpose. Remove them. No functional change intended. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu Cc: linux-imx@nxp.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pci@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 8ebfb06584d9..f6fb65a40f10 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -60,14 +60,14 @@ #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_REGION_INBOUND BIT(31) #define PCIE_ATU_REGION_OUTBOUND 0 -#define PCIE_ATU_REGION_INDEX2 (0x2 << 0) -#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) -#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#define PCIE_ATU_REGION_INDEX2 0x2 +#define PCIE_ATU_REGION_INDEX1 0x1 +#define PCIE_ATU_REGION_INDEX0 0x0 #define PCIE_ATU_CR1 0x904 -#define PCIE_ATU_TYPE_MEM (0x0 << 0) -#define PCIE_ATU_TYPE_IO (0x2 << 0) -#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) -#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) +#define PCIE_ATU_TYPE_MEM 0x0 +#define PCIE_ATU_TYPE_IO 0x2 +#define PCIE_ATU_TYPE_CFG0 0x4 +#define PCIE_ATU_TYPE_CFG1 0x5 #define PCIE_ATU_CR2 0x908 #define PCIE_ATU_ENABLE BIT(31) #define PCIE_ATU_BAR_MODE_ENABLE BIT(30) -- cgit v1.2.3 From c61df57343bf05743f8abbb31eec9a6f05820dd1 Mon Sep 17 00:00:00 2001 From: Honghui Zhang Date: Fri, 1 Feb 2019 13:36:06 +0800 Subject: PCI: mediatek: Fix memory mapped IO range size computation Mediatek's HW assigns a MMIO address range (typically starts from 0x20000000 to 0x2fffffff for both mt2712 and mt7622) for PCI usage. This MMIO address space represents the address space that can be allocated to PCI devices through Base Address Registers. Even though the full MMIO address range is available to be allocated, it should be enabled by the PCIE_AHB_TRANS_BASE register in the host controller and the size that is enabled is determined by AHB2PCIE_SIZE bits in this register. Owing to a bug in the MMIO window size computation, current code does not enable the full size of the available MMIO address range in the PCI host controller; if the PCI devices BARs requested size exceeds the size enabled through the PCIE_AHB_TRANS_BASE register the requests targeting the disabled address address space will be blocked by the root complex causing a system error. Existing code has never run into a system error in production because even half of the enabled MMIO range (128MB) is big enough for typical devices BAR requests (4MB) but the full MMIO address range should be enabled regardless. Fix the MMIO window size computation by using resource_size(mem) instead of mem->end - mem->start. Since the MMIO window size for both MT2712 and MT7622 is 0x10000000, this change will update the parameter passed to fls() from 0xfffffff to 0x10000000 and calculate the whole memory mapped IO range size correctly. Detected through coccinelle semantic patch (and related warning): scripts/coccinelle/api/resource_size.cocci: pcie-mediatek.c:720:13-16: WARNING: Suspicious code. resource_size is maybe missing with mem Signed-off-by: Honghui Zhang [lorenzo.pieralisi@arm.com: rewrote the commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-mediatek.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 55e471c18e8d..c42fe5c4319f 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -654,7 +654,6 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) struct resource *mem = &pcie->mem; const struct mtk_pcie_soc *soc = port->pcie->soc; u32 val; - size_t size; int err; /* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */ @@ -706,8 +705,8 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) mtk_pcie_enable_msi(port); /* Set AHB to PCIe translation windows */ - size = mem->end - mem->start; - val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size)); + val = lower_32_bits(mem->start) | + AHB2PCIE_SIZE(fls(resource_size(mem))); writel(val, port->base + PCIE_AHB_TRANS_BASE0_L); val = upper_32_bits(mem->start); -- cgit v1.2.3 From cbe3a7728c7ad4721677208e155db06f67eb57d2 Mon Sep 17 00:00:00 2001 From: Honghui Zhang Date: Fri, 1 Feb 2019 13:36:07 +0800 Subject: PCI: mediatek: Enlarge PCIe2AHB window size to support 4GB DRAM The PCIE_AXI_WINDOW0 register defines the inbound window size for requests coming from PCI endpoints. Requests outside of this window will be treated as unsupported requests. Enlarge this window size from 2^31 to 2^33 to support a 8GB address space (which gives endpoints DMA access to full 4GB DRAM address range - physical DRAM starts at 0x40000000). Reported-by: Bjorn Helgaas Signed-off-by: Honghui Zhang [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-mediatek.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index c42fe5c4319f..0b6c72804e03 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -90,6 +90,12 @@ #define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0)) #define PCIE_AXI_WINDOW0 0x448 #define WIN_ENABLE BIT(7) +/* + * Define PCIe to AHB window size as 2^33 to support max 8GB address space + * translate, support least 4GB DRAM size access from EP DMA(physical DRAM + * start from 0x40000000). + */ +#define PCIE2AHB_SIZE 0x21 /* PCIe V2 configuration transaction header */ #define PCIE_CFG_HEADER0 0x460 @@ -713,7 +719,7 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) writel(val, port->base + PCIE_AHB_TRANS_BASE0_H); /* Set PCIe to AXI translation memory space.*/ - val = fls(0xffffffff) | WIN_ENABLE; + val = PCIE2AHB_SIZE | WIN_ENABLE; writel(val, port->base + PCIE_AXI_WINDOW0); return 0; -- cgit v1.2.3 From 6ae91579061c25f70e4301619ffd16e1b6b5defb Mon Sep 17 00:00:00 2001 From: Maya Nakamura Date: Fri, 1 Mar 2019 06:56:24 +0000 Subject: PCI: hv: Add __aligned(8) to struct retarget_msi_interrupt Because Hyper-V requires that hypercall arguments be aligned on an 8 byte boundary, add __aligned(8) to struct retarget_msi_interrupt. Link: https://lore.kernel.org/lkml/87k1hlqlby.fsf@vitty.brq.redhat.com/ Signed-off-by: Maya Nakamura Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-hyperv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 9ba4d12c179c..73862eef09ec 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -420,7 +420,7 @@ struct retarget_msi_interrupt { struct hv_interrupt_entry int_entry; u64 reserved2; struct hv_device_interrupt_target int_target; -} __packed; +} __packed __aligned(8); /* * Driver specific state. -- cgit v1.2.3 From 9bc1174280ddf7693e8c29a8f1743809e987ac37 Mon Sep 17 00:00:00 2001 From: Maya Nakamura Date: Fri, 1 Mar 2019 06:59:02 +0000 Subject: PCI: hv: Replace hv_vp_set with hv_vpset Remove a duplicate definition of VP set (hv_vp_set) and use the common definition (hv_vpset) that is used in other places. Change the order of the members in struct hv_pcibus_device so that the declaration of retarget_msi_interrupt_params is the last member. Struct hv_vpset, which contains a flexible array, is nested two levels deep in struct hv_pcibus_device via retarget_msi_interrupt_params. Add a comment that retarget_msi_interrupt_params should be the last member of struct hv_pcibus_device. Signed-off-by: Maya Nakamura Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Reviewed-by: Vitaly Kuznetsov Tested-by: Vitaly Kuznetsov --- drivers/pci/controller/pci-hyperv.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 73862eef09ec..d71695db1ba0 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -393,12 +393,6 @@ struct hv_interrupt_entry { #define HV_VP_SET_BANK_COUNT_MAX 5 /* current implementation limit */ -struct hv_vp_set { - u64 format; /* 0 (HvGenericSetSparse4k) */ - u64 valid_banks; - u64 masks[HV_VP_SET_BANK_COUNT_MAX]; -}; - /* * flags for hv_device_interrupt_target.flags */ @@ -410,7 +404,7 @@ struct hv_device_interrupt_target { u32 flags; union { u64 vp_mask; - struct hv_vp_set vp_set; + struct hv_vpset vp_set; }; }; @@ -460,12 +454,16 @@ struct hv_pcibus_device { struct msi_controller msi_chip; struct irq_domain *irq_domain; - /* hypercall arg, must not cross page boundary */ - struct retarget_msi_interrupt retarget_msi_interrupt_params; - spinlock_t retarget_msi_interrupt_lock; struct workqueue_struct *wq; + + /* hypercall arg, must not cross page boundary */ + struct retarget_msi_interrupt retarget_msi_interrupt_params; + + /* + * Don't put anything here: retarget_msi_interrupt_params must be last + */ }; /* @@ -955,12 +953,13 @@ static void hv_irq_unmask(struct irq_data *data) */ params->int_target.flags |= HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET; - params->int_target.vp_set.valid_banks = + params->int_target.vp_set.valid_bank_mask = (1ull << HV_VP_SET_BANK_COUNT_MAX) - 1; /* * var-sized hypercall, var-size starts after vp_mask (thus - * vp_set.format does not count, but vp_set.valid_banks does). + * vp_set.format does not count, but vp_set.valid_bank_mask + * does). */ var_size = 1 + HV_VP_SET_BANK_COUNT_MAX; @@ -974,7 +973,7 @@ static void hv_irq_unmask(struct irq_data *data) goto exit_unlock; } - params->int_target.vp_set.masks[cpu_vmbus / 64] |= + params->int_target.vp_set.bank_contents[cpu_vmbus / 64] |= (1ULL << (cpu_vmbus & 63)); } } else { -- cgit v1.2.3 From c8ccf7599ddac5fa48e06104c44b24709e21eed5 Mon Sep 17 00:00:00 2001 From: Maya Nakamura Date: Fri, 1 Mar 2019 07:04:17 +0000 Subject: PCI: hv: Refactor hv_irq_unmask() to use cpumask_to_vpset() Remove the duplicate implementation of cpumask_to_vpset() and use the shared implementation. Export hv_max_vp_index, which is required by cpumask_to_vpset(). Signed-off-by: Maya Nakamura Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Reviewed-by: Vitaly Kuznetsov Tested-by: Vitaly Kuznetsov --- arch/x86/hyperv/hv_init.c | 1 + drivers/pci/controller/pci-hyperv.c | 38 +++++++++++++++++-------------------- 2 files changed, 18 insertions(+), 21 deletions(-) (limited to 'drivers/pci') diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 7abb09e2eeb8..7f2eed1fc81b 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -96,6 +96,7 @@ void __percpu **hyperv_pcpu_input_arg; EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg); u32 hv_max_vp_index; +EXPORT_SYMBOL_GPL(hv_max_vp_index); static int hv_cpu_init(unsigned int cpu) { diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index d71695db1ba0..95441a35eceb 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -391,8 +391,6 @@ struct hv_interrupt_entry { u32 data; }; -#define HV_VP_SET_BANK_COUNT_MAX 5 /* current implementation limit */ - /* * flags for hv_device_interrupt_target.flags */ @@ -908,12 +906,12 @@ static void hv_irq_unmask(struct irq_data *data) struct retarget_msi_interrupt *params; struct hv_pcibus_device *hbus; struct cpumask *dest; + cpumask_var_t tmp; struct pci_bus *pbus; struct pci_dev *pdev; unsigned long flags; u32 var_size = 0; - int cpu_vmbus; - int cpu; + int cpu, nr_bank; u64 res; dest = irq_data_get_effective_affinity_mask(data); @@ -953,29 +951,27 @@ static void hv_irq_unmask(struct irq_data *data) */ params->int_target.flags |= HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET; - params->int_target.vp_set.valid_bank_mask = - (1ull << HV_VP_SET_BANK_COUNT_MAX) - 1; + + if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) { + res = 1; + goto exit_unlock; + } + + cpumask_and(tmp, dest, cpu_online_mask); + nr_bank = cpumask_to_vpset(¶ms->int_target.vp_set, tmp); + free_cpumask_var(tmp); + + if (nr_bank <= 0) { + res = 1; + goto exit_unlock; + } /* * var-sized hypercall, var-size starts after vp_mask (thus * vp_set.format does not count, but vp_set.valid_bank_mask * does). */ - var_size = 1 + HV_VP_SET_BANK_COUNT_MAX; - - for_each_cpu_and(cpu, dest, cpu_online_mask) { - cpu_vmbus = hv_cpu_number_to_vp_number(cpu); - - if (cpu_vmbus >= HV_VP_SET_BANK_COUNT_MAX * 64) { - dev_err(&hbus->hdev->device, - "too high CPU %d", cpu_vmbus); - res = 1; - goto exit_unlock; - } - - params->int_target.vp_set.bank_contents[cpu_vmbus / 64] |= - (1ULL << (cpu_vmbus & 63)); - } + var_size = 1 + nr_bank; } else { for_each_cpu_and(cpu, dest, cpu_online_mask) { params->int_target.vp_mask |= -- cgit v1.2.3 From 3afc8299f39a27b60e1519a28e18878ce878e7dd Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 27 Feb 2019 17:52:19 +0100 Subject: PCI: dwc: skip MSI init if MSIs have been explicitly disabled Since 7c5925afbc58 (PCI: dwc: Move MSI IRQs allocation to IRQ domains hierarchical API) the MSI init claims one of the controller IRQs as a chained IRQ line for the MSI controller. On some designs, like the i.MX6, this line is shared with a PCIe legacy IRQ. When the line is claimed for the MSI domain, any device trying to use this legacy IRQs will fail to request this IRQ line. As MSI and legacy IRQs are already mutually exclusive on the DWC core, as the core won't forward any legacy IRQs once any MSI has been enabled, users wishing to use legacy IRQs already need to explictly disable MSI support (usually via the pci=nomsi kernel commandline option). To avoid any issues with MSI conflicting with legacy IRQs, just skip all of the DWC MSI initalization, including the IRQ line claim, when MSI is disabled. Fixes: 7c5925afbc58 ("PCI: dwc: Move MSI IRQs allocation to IRQ domains hierarchical API") Tested-by: Tim Harvey Signed-off-by: Lucas Stach Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel Cc: stable@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 0c18ab63811f..25087d3c9a82 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -439,7 +439,7 @@ int dw_pcie_host_init(struct pcie_port *pp) if (ret) pci->num_viewport = 2; - if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (IS_ENABLED(CONFIG_PCI_MSI) && pci_msi_enabled()) { /* * If a specific SoC driver needs to change the * default number of vectors, it needs to implement -- cgit v1.2.3 From d3b34d048b7c1a4aac9f2e2e6ff6b0e21e50719e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 1 Mar 2019 06:58:09 +0000 Subject: PCI: aardvark: Make symbol 'advk_pci_bridge_emul_ops' static Fix the following sparse warning: drivers/pci/controller/pci-aardvark.c:469:28: warning: symbol 'advk_pci_bridge_emul_ops' was not declared. Should it be static? Fixes: 8a3ebd8de328 ("PCI: aardvark: Implement emulated root PCI bridge config space") Signed-off-by: Wei Yongjun Signed-off-by: Lorenzo Pieralisi Acked-by: Thomas Petazzoni --- drivers/pci/controller/pci-aardvark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 6eecae447af3..eb58dfdaba1b 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -466,7 +466,7 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } -struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { +static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { .read_pcie = advk_pci_bridge_emul_pcie_conf_read, .write_pcie = advk_pci_bridge_emul_pcie_conf_write, }; -- cgit v1.2.3 From 7cf58b79b3072029af127ae865ffc6f00f34b1f8 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Fri, 1 Mar 2019 11:54:19 -0500 Subject: PCI/PME: Fix possible use-after-free on remove In remove(), ensure that the PME work cannot run after kfree() is called. Otherwise, this could result in a use-after-free. This issue was detected with the help of Coccinelle. Signed-off-by: Sven Van Asbroeck Signed-off-by: Bjorn Helgaas Cc: Sinan Kaya Cc: Frederick Lawler Cc: Mika Westerberg Cc: Keith Busch Cc: Rafael J. Wysocki --- drivers/pci/pcie/pme.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index efa5b552914b..54d593d10396 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -437,6 +437,7 @@ static void pcie_pme_remove(struct pcie_device *srv) pcie_pme_disable_interrupt(srv->port, data); free_irq(srv->irq, srv); + cancel_work_sync(&data->work); kfree(data); } -- cgit v1.2.3 From 49fdbd7cfece4181b711f02b7f30fd2792700382 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 28 Feb 2019 18:52:50 +0800 Subject: PCI: altera: Add Stratix 10 PCIe support Add PCIe Root Port support for Stratix 10 device. Main differences compared to the PCIe Root Port IP on Cyclone V and Arria 10 devices: - HIP interface to access Root Port configuration register - TLP programming flow: - One REG0 register - Don't need to check alignment Signed-off-by: Ley Foon Tan Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-altera.c | 270 +++++++++++++++++++++++++++++++---- 1 file changed, 246 insertions(+), 24 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 7d05e51205b3..27edcebd1726 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,12 @@ #define RP_LTSSM_MASK 0x1f #define LTSSM_L0 0xf -#define PCIE_CAP_OFFSET 0x80 +#define S10_RP_TX_CNTRL 0x2004 +#define S10_RP_RXCPL_REG 0x2008 +#define S10_RP_RXCPL_STATUS 0x200C +#define S10_RP_CFG_ADDR(pcie, reg) \ + (((pcie)->hip_base) + (reg) + (1 << 20)) + /* TLP configuration type 0 and 1 */ #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ @@ -49,18 +55,19 @@ #define RP_DEVFN 0 #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) #define TLP_CFGRD_DW0(pcie, bus) \ - ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \ - : TLP_FMTTYPE_CFGRD1) << 24) | \ - TLP_PAYLOAD_SIZE) + ((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgrd0 \ + : pcie->pcie_data->cfgrd1) << 24) | \ + TLP_PAYLOAD_SIZE) #define TLP_CFGWR_DW0(pcie, bus) \ - ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \ - : TLP_FMTTYPE_CFGWR1) << 24) | \ - TLP_PAYLOAD_SIZE) + ((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgwr0 \ + : pcie->pcie_data->cfgwr1) << 24) | \ + TLP_PAYLOAD_SIZE) #define TLP_CFG_DW1(pcie, tag, be) \ - (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) + (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) #define TLP_CFG_DW2(bus, devfn, offset) \ (((bus) << 24) | ((devfn) << 16) | (offset)) #define TLP_COMP_STATUS(s) (((s) >> 13) & 7) +#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff) #define TLP_HDR_SIZE 3 #define TLP_LOOP 500 @@ -69,14 +76,47 @@ #define DWORD_MASK 3 +#define S10_TLP_FMTTYPE_CFGRD0 0x05 +#define S10_TLP_FMTTYPE_CFGRD1 0x04 +#define S10_TLP_FMTTYPE_CFGWR0 0x45 +#define S10_TLP_FMTTYPE_CFGWR1 0x44 + +enum altera_pcie_version { + ALTERA_PCIE_V1 = 0, + ALTERA_PCIE_V2, +}; + struct altera_pcie { struct platform_device *pdev; - void __iomem *cra_base; /* DT Cra */ + void __iomem *cra_base; + void __iomem *hip_base; int irq; u8 root_bus_nr; struct irq_domain *irq_domain; struct resource bus_range; struct list_head resources; + const struct altera_pcie_data *pcie_data; +}; + +struct altera_pcie_ops { + int (*tlp_read_pkt)(struct altera_pcie *pcie, u32 *value); + void (*tlp_write_pkt)(struct altera_pcie *pcie, u32 *headers, + u32 data, bool align); + bool (*get_link_status)(struct altera_pcie *pcie); + int (*rp_read_cfg)(struct altera_pcie *pcie, int where, + int size, u32 *value); + int (*rp_write_cfg)(struct altera_pcie *pcie, u8 busno, + int where, int size, u32 value); +}; + +struct altera_pcie_data { + const struct altera_pcie_ops *ops; + enum altera_pcie_version version; + u32 cap_offset; /* PCIe capability structure register offset */ + u32 cfgrd0; + u32 cfgrd1; + u32 cfgwr0; + u32 cfgwr1; }; struct tlp_rp_regpair_t { @@ -101,6 +141,15 @@ static bool altera_pcie_link_up(struct altera_pcie *pcie) return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); } +static bool s10_altera_pcie_link_up(struct altera_pcie *pcie) +{ + void __iomem *addr = S10_RP_CFG_ADDR(pcie, + pcie->pcie_data->cap_offset + + PCI_EXP_LNKSTA); + + return !!(readw(addr) & PCI_EXP_LNKSTA_DLLLA); +} + /* * Altera PCIe port uses BAR0 of RC's configuration space as the translation * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space @@ -128,12 +177,18 @@ static void tlp_write_tx(struct altera_pcie *pcie, cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL); } +static void s10_tlp_write_tx(struct altera_pcie *pcie, u32 reg0, u32 ctrl) +{ + cra_writel(pcie, reg0, RP_TX_REG0); + cra_writel(pcie, ctrl, S10_RP_TX_CNTRL); +} + static bool altera_pcie_valid_device(struct altera_pcie *pcie, struct pci_bus *bus, int dev) { /* If there is no link, then there is no device */ if (bus->number != pcie->root_bus_nr) { - if (!altera_pcie_link_up(pcie)) + if (!pcie->pcie_data->ops->get_link_status(pcie)) return false; } @@ -183,6 +238,53 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) return PCIBIOS_DEVICE_NOT_FOUND; } +static int s10_tlp_read_packet(struct altera_pcie *pcie, u32 *value) +{ + u32 ctrl; + u32 comp_status; + u32 dw[4]; + u32 count; + struct device *dev = &pcie->pdev->dev; + + for (count = 0; count < TLP_LOOP; count++) { + ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS); + if (ctrl & RP_RXCPL_SOP) { + /* Read first DW */ + dw[0] = cra_readl(pcie, S10_RP_RXCPL_REG); + break; + } + + udelay(5); + } + + /* SOP detection failed, return error */ + if (count == TLP_LOOP) + return PCIBIOS_DEVICE_NOT_FOUND; + + count = 1; + + /* Poll for EOP */ + while (count < ARRAY_SIZE(dw)) { + ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS); + dw[count++] = cra_readl(pcie, S10_RP_RXCPL_REG); + if (ctrl & RP_RXCPL_EOP) { + comp_status = TLP_COMP_STATUS(dw[1]); + if (comp_status) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (value && TLP_BYTE_COUNT(dw[1]) == sizeof(u32) && + count == 4) + *value = dw[3]; + + return PCIBIOS_SUCCESSFUL; + } + } + + dev_warn(dev, "Malformed TLP packet\n"); + + return PCIBIOS_DEVICE_NOT_FOUND; +} + static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers, u32 data, bool align) { @@ -210,6 +312,15 @@ static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers, tlp_write_tx(pcie, &tlp_rp_regdata); } +static void s10_tlp_write_packet(struct altera_pcie *pcie, u32 *headers, + u32 data, bool dummy) +{ + s10_tlp_write_tx(pcie, headers[0], RP_TX_SOP); + s10_tlp_write_tx(pcie, headers[1], 0); + s10_tlp_write_tx(pcie, headers[2], 0); + s10_tlp_write_tx(pcie, data, RP_TX_EOP); +} + static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, int where, u8 byte_en, u32 *value) { @@ -219,9 +330,9 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); - tlp_write_packet(pcie, headers, 0, false); + pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, 0, false); - return tlp_read_packet(pcie, value); + return pcie->pcie_data->ops->tlp_read_pkt(pcie, value); } static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, @@ -236,11 +347,13 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, /* check alignment to Qword */ if ((where & 0x7) == 0) - tlp_write_packet(pcie, headers, value, true); + pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, + value, true); else - tlp_write_packet(pcie, headers, value, false); + pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, + value, false); - ret = tlp_read_packet(pcie, NULL); + ret = pcie->pcie_data->ops->tlp_read_pkt(pcie, NULL); if (ret != PCIBIOS_SUCCESSFUL) return ret; @@ -254,6 +367,53 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, return PCIBIOS_SUCCESSFUL; } +static int s10_rp_read_cfg(struct altera_pcie *pcie, int where, + int size, u32 *value) +{ + void __iomem *addr = S10_RP_CFG_ADDR(pcie, where); + + switch (size) { + case 1: + *value = readb(addr); + break; + case 2: + *value = readw(addr); + break; + default: + *value = readl(addr); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int s10_rp_write_cfg(struct altera_pcie *pcie, u8 busno, + int where, int size, u32 value) +{ + void __iomem *addr = S10_RP_CFG_ADDR(pcie, where); + + switch (size) { + case 1: + writeb(value, addr); + break; + case 2: + writew(value, addr); + break; + default: + writel(value, addr); + break; + } + + /* + * Monitor changes to PCI_PRIMARY_BUS register on root port + * and update local copy of root bus number accordingly. + */ + if (busno == pcie->root_bus_nr && where == PCI_PRIMARY_BUS) + pcie->root_bus_nr = value & 0xff; + + return PCIBIOS_SUCCESSFUL; +} + static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, unsigned int devfn, int where, int size, u32 *value) @@ -262,6 +422,10 @@ static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, u32 data; u8 byte_en; + if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_read_cfg) + return pcie->pcie_data->ops->rp_read_cfg(pcie, where, + size, value); + switch (size) { case 1: byte_en = 1 << (where & 3); @@ -302,6 +466,10 @@ static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno, u32 shift = 8 * (where & 3); u8 byte_en; + if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_write_cfg) + return pcie->pcie_data->ops->rp_write_cfg(pcie, busno, + where, size, value); + switch (size) { case 1: data32 = (value & 0xff) << shift; @@ -365,7 +533,8 @@ static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno, int ret; ret = _altera_pcie_cfg_read(pcie, busno, devfn, - PCIE_CAP_OFFSET + offset, sizeof(*value), + pcie->pcie_data->cap_offset + offset, + sizeof(*value), &data); *value = data; return ret; @@ -375,7 +544,8 @@ static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno, unsigned int devfn, int offset, u16 value) { return _altera_pcie_cfg_write(pcie, busno, devfn, - PCIE_CAP_OFFSET + offset, sizeof(value), + pcie->pcie_data->cap_offset + offset, + sizeof(value), value); } @@ -403,7 +573,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie) /* Wait for link is up */ start_jiffies = jiffies; for (;;) { - if (altera_pcie_link_up(pcie)) + if (pcie->pcie_data->ops->get_link_status(pcie)) break; if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { @@ -418,7 +588,7 @@ static void altera_pcie_retrain(struct altera_pcie *pcie) { u16 linkcap, linkstat, linkctl; - if (!altera_pcie_link_up(pcie)) + if (!pcie->pcie_data->ops->get_link_status(pcie)) return; /* @@ -540,12 +710,20 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) struct device *dev = &pcie->pdev->dev; struct platform_device *pdev = pcie->pdev; struct resource *cra; + struct resource *hip; cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra"); pcie->cra_base = devm_ioremap_resource(dev, cra); if (IS_ERR(pcie->cra_base)) return PTR_ERR(pcie->cra_base); + if (pcie->pcie_data->version == ALTERA_PCIE_V2) { + hip = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Hip"); + pcie->hip_base = devm_ioremap_resource(&pdev->dev, hip); + if (IS_ERR(pcie->hip_base)) + return PTR_ERR(pcie->hip_base); + } + /* setup IRQ */ pcie->irq = platform_get_irq(pdev, 0); if (pcie->irq < 0) { @@ -562,6 +740,48 @@ static void altera_pcie_host_init(struct altera_pcie *pcie) altera_pcie_retrain(pcie); } +static const struct altera_pcie_ops altera_pcie_ops_1_0 = { + .tlp_read_pkt = tlp_read_packet, + .tlp_write_pkt = tlp_write_packet, + .get_link_status = altera_pcie_link_up, +}; + +static const struct altera_pcie_ops altera_pcie_ops_2_0 = { + .tlp_read_pkt = s10_tlp_read_packet, + .tlp_write_pkt = s10_tlp_write_packet, + .get_link_status = s10_altera_pcie_link_up, + .rp_read_cfg = s10_rp_read_cfg, + .rp_write_cfg = s10_rp_write_cfg, +}; + +static const struct altera_pcie_data altera_pcie_1_0_data = { + .ops = &altera_pcie_ops_1_0, + .cap_offset = 0x80, + .version = ALTERA_PCIE_V1, + .cfgrd0 = TLP_FMTTYPE_CFGRD0, + .cfgrd1 = TLP_FMTTYPE_CFGRD1, + .cfgwr0 = TLP_FMTTYPE_CFGWR0, + .cfgwr1 = TLP_FMTTYPE_CFGWR1, +}; + +static const struct altera_pcie_data altera_pcie_2_0_data = { + .ops = &altera_pcie_ops_2_0, + .version = ALTERA_PCIE_V2, + .cap_offset = 0x70, + .cfgrd0 = S10_TLP_FMTTYPE_CFGRD0, + .cfgrd1 = S10_TLP_FMTTYPE_CFGRD1, + .cfgwr0 = S10_TLP_FMTTYPE_CFGWR0, + .cfgwr1 = S10_TLP_FMTTYPE_CFGWR1, +}; + +static const struct of_device_id altera_pcie_of_match[] = { + {.compatible = "altr,pcie-root-port-1.0", + .data = &altera_pcie_1_0_data }, + {.compatible = "altr,pcie-root-port-2.0", + .data = &altera_pcie_2_0_data }, + {}, +}; + static int altera_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -570,6 +790,7 @@ static int altera_pcie_probe(struct platform_device *pdev) struct pci_bus *child; struct pci_host_bridge *bridge; int ret; + const struct of_device_id *match; bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); if (!bridge) @@ -578,6 +799,12 @@ static int altera_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->pdev = pdev; + match = of_match_device(altera_pcie_of_match, &pdev->dev); + if (!match) + return -ENODEV; + + pcie->pcie_data = match->data; + ret = altera_pcie_parse_dt(pcie); if (ret) { dev_err(dev, "Parsing DT failed\n"); @@ -628,11 +855,6 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } -static const struct of_device_id altera_pcie_of_match[] = { - { .compatible = "altr,pcie-root-port-1.0", }, - {}, -}; - static struct platform_driver altera_pcie_driver = { .probe = altera_pcie_probe, .driver = { -- cgit v1.2.3 From 5fd831cd16772188c3dd0ed9bb96418d883b4a51 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 28 Feb 2019 18:52:51 +0800 Subject: PCI: altera: Enable driver on ARM64 Enable PCIE_ALTERA on ARM64 platform. Signed-off-by: Ley Foon Tan Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 6671946dbf66..6012f3059acd 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -175,7 +175,7 @@ config PCIE_IPROC_MSI config PCIE_ALTERA bool "Altera PCIe controller" - depends on ARM || NIOS2 || COMPILE_TEST + depends on ARM || NIOS2 || ARM64 || COMPILE_TEST help Say Y here if you want to enable PCIe controller support on Altera FPGA. -- cgit v1.2.3 From e8303bb7a75c113388badcc49b2a84b4121c1b3e Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Wed, 27 Feb 2019 14:58:17 -0600 Subject: PCI/LINK: Report degraded links via link bandwidth notification A warning is generated when a PCIe device is probed with a degraded link, but there was no similar mechanism to warn when the link becomes degraded after probing. The Link Bandwidth Notification provides this mechanism. Use the Link Bandwidth Management Interrupt to detect bandwidth changes, and rescan the bandwidth, looking for the weakest point. This is the same logic used in probe(). Signed-off-by: Alexandru Gagniuc Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner --- drivers/pci/pcie/Makefile | 1 + drivers/pci/pcie/bw_notification.c | 110 +++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/portdrv.h | 6 +- drivers/pci/pcie/portdrv_core.c | 17 ++++-- drivers/pci/pcie/portdrv_pci.c | 1 + 5 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 drivers/pci/pcie/bw_notification.c (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index ab514083d5d4..f1d7bc1e5efa 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -3,6 +3,7 @@ # Makefile for PCI Express features and port driver pcieportdrv-y := portdrv_core.o portdrv_pci.o err.o +pcieportdrv-y += bw_notification.o obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o diff --git a/drivers/pci/pcie/bw_notification.c b/drivers/pci/pcie/bw_notification.c new file mode 100644 index 000000000000..d2eae3b7cc0f --- /dev/null +++ b/drivers/pci/pcie/bw_notification.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCI Express Link Bandwidth Notification services driver + * Author: Alexandru Gagniuc + * + * Copyright (C) 2019, Dell Inc + * + * The PCIe Link Bandwidth Notification provides a way to notify the + * operating system when the link width or data rate changes. This + * capability is required for all root ports and downstream ports + * supporting links wider than x1 and/or multiple link speeds. + * + * This service port driver hooks into the bandwidth notification interrupt + * and warns when links become degraded in operation. + */ + +#include "../pci.h" +#include "portdrv.h" + +static bool pcie_link_bandwidth_notification_supported(struct pci_dev *dev) +{ + int ret; + u32 lnk_cap; + + ret = pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnk_cap); + return (ret == PCIBIOS_SUCCESSFUL) && (lnk_cap & PCI_EXP_LNKCAP_LBNC); +} + +static void pcie_enable_link_bandwidth_notification(struct pci_dev *dev) +{ + u16 lnk_ctl; + + pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl); + lnk_ctl |= PCI_EXP_LNKCTL_LBMIE; + pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl); +} + +static void pcie_disable_link_bandwidth_notification(struct pci_dev *dev) +{ + u16 lnk_ctl; + + pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl); + lnk_ctl &= ~PCI_EXP_LNKCTL_LBMIE; + pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl); +} + +static irqreturn_t pcie_bw_notification_handler(int irq, void *context) +{ + struct pcie_device *srv = context; + struct pci_dev *port = srv->port; + struct pci_dev *dev; + u16 link_status, events; + int ret; + + ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status); + events = link_status & PCI_EXP_LNKSTA_LBMS; + + if (ret != PCIBIOS_SUCCESSFUL || !events) + return IRQ_NONE; + + /* + * Print status from downstream devices, not this root port or + * downstream switch port. + */ + down_read(&pci_bus_sem); + list_for_each_entry(dev, &port->subordinate->devices, bus_list) + __pcie_print_link_status(dev, false); + up_read(&pci_bus_sem); + + pcie_update_link_speed(port->subordinate, link_status); + pcie_capability_write_word(port, PCI_EXP_LNKSTA, events); + return IRQ_HANDLED; +} + +static int pcie_bandwidth_notification_probe(struct pcie_device *srv) +{ + int ret; + + /* Single-width or single-speed ports do not have to support this. */ + if (!pcie_link_bandwidth_notification_supported(srv->port)) + return -ENODEV; + + ret = request_threaded_irq(srv->irq, NULL, pcie_bw_notification_handler, + IRQF_SHARED, "PCIe BW notif", srv); + if (ret) + return ret; + + pcie_enable_link_bandwidth_notification(srv->port); + + return 0; +} + +static void pcie_bandwidth_notification_remove(struct pcie_device *srv) +{ + pcie_disable_link_bandwidth_notification(srv->port); + free_irq(srv->irq, srv); +} + +static struct pcie_port_service_driver pcie_bandwidth_notification_driver = { + .name = "pcie_bw_notification", + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_BWNOTIF, + .probe = pcie_bandwidth_notification_probe, + .remove = pcie_bandwidth_notification_remove, +}; + +int __init pcie_bandwidth_notification_init(void) +{ + return pcie_port_service_register(&pcie_bandwidth_notification_driver); +} diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index fbbf00b0992e..1d50dc58ac40 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -20,8 +20,10 @@ #define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) #define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */ #define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) +#define PCIE_PORT_SERVICE_BWNOTIF_SHIFT 4 /* Bandwidth notification */ +#define PCIE_PORT_SERVICE_BWNOTIF (1 << PCIE_PORT_SERVICE_BWNOTIF_SHIFT) -#define PCIE_PORT_DEVICE_MAXSERVICES 4 +#define PCIE_PORT_DEVICE_MAXSERVICES 5 #ifdef CONFIG_PCIEAER int pcie_aer_init(void); @@ -47,6 +49,8 @@ int pcie_dpc_init(void); static inline int pcie_dpc_init(void) { return 0; } #endif +int pcie_bandwidth_notification_init(void); + /* Port Type */ #define PCIE_ANY_PORT (~0) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index f458ac9cb70c..7d04f9d087a6 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -99,7 +99,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask, */ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) { - int nr_entries, nvec; + int nr_entries, nvec, pcie_irq; u32 pme = 0, aer = 0, dpc = 0; /* Allocate the maximum possible number of MSI/MSI-X vectors */ @@ -135,10 +135,13 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) return nr_entries; } - /* PME and hotplug share an MSI/MSI-X vector */ - if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { - irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme); - irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme); + /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */ + if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | + PCIE_PORT_SERVICE_BWNOTIF)) { + pcie_irq = pci_irq_vector(dev, pme); + irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq; + irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq; + irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq; } if (mask & PCIE_PORT_SERVICE_AER) @@ -250,6 +253,10 @@ static int get_port_device_capability(struct pci_dev *dev) pci_aer_available() && services & PCIE_PORT_SERVICE_AER) services |= PCIE_PORT_SERVICE_DPC; + if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM || + pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) + services |= PCIE_PORT_SERVICE_BWNOTIF; + return services; } diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 99d2abe88d0b..0a87091a0800 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -240,6 +240,7 @@ static void __init pcie_init_services(void) pcie_pme_init(); pcie_dpc_init(); pcie_hp_init(); + pcie_bandwidth_notification_init(); } static int __init pcie_portdrv_init(void) -- cgit v1.2.3 From ddefc033eecf23f1e8b81d0663c5db965adf5516 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 18 Feb 2019 12:46:46 -0700 Subject: PCI: Fix "try" semantics of bus and slot reset The commit referenced below introduced device locking around save and restore of state for each device during a PCI bus "try" reset, making it decidely non-"try" and prone to deadlock in the event that a device is already locked. Restore __pci_reset_bus() and __pci_reset_slot() to their advertised locking semantics by pushing the save and restore functions into the branch where the entire tree is already locked. Extend the helper function names with "_locked" and update the comment to reflect this calling requirement. Fixes: b014e96d1abb ("PCI: Protect pci_error_handlers->reset_notify() usage with device_lock()") Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya --- drivers/pci/pci.c | 54 ++++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 94069fc35e91..a3e8edd62a04 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5058,39 +5058,42 @@ unlock: return 0; } -/* Save and disable devices from the top of the tree down */ -static void pci_bus_save_and_disable(struct pci_bus *bus) +/* + * Save and disable devices from the top of the tree down while holding + * the @dev mutex lock for the entire tree. + */ +static void pci_bus_save_and_disable_locked(struct pci_bus *bus) { struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { - pci_dev_lock(dev); pci_dev_save_and_disable(dev); - pci_dev_unlock(dev); if (dev->subordinate) - pci_bus_save_and_disable(dev->subordinate); + pci_bus_save_and_disable_locked(dev->subordinate); } } /* - * Restore devices from top of the tree down - parent bridges need to be - * restored before we can get to subordinate devices. + * Restore devices from top of the tree down while holding @dev mutex lock + * for the entire tree. Parent bridges need to be restored before we can + * get to subordinate devices. */ -static void pci_bus_restore(struct pci_bus *bus) +static void pci_bus_restore_locked(struct pci_bus *bus) { struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { - pci_dev_lock(dev); pci_dev_restore(dev); - pci_dev_unlock(dev); if (dev->subordinate) - pci_bus_restore(dev->subordinate); + pci_bus_restore_locked(dev->subordinate); } } -/* Save and disable devices from the top of the tree down */ -static void pci_slot_save_and_disable(struct pci_slot *slot) +/* + * Save and disable devices from the top of the tree down while holding + * the @dev mutex lock for the entire tree. + */ +static void pci_slot_save_and_disable_locked(struct pci_slot *slot) { struct pci_dev *dev; @@ -5099,26 +5102,25 @@ static void pci_slot_save_and_disable(struct pci_slot *slot) continue; pci_dev_save_and_disable(dev); if (dev->subordinate) - pci_bus_save_and_disable(dev->subordinate); + pci_bus_save_and_disable_locked(dev->subordinate); } } /* - * Restore devices from top of the tree down - parent bridges need to be - * restored before we can get to subordinate devices. + * Restore devices from top of the tree down while holding @dev mutex lock + * for the entire tree. Parent bridges need to be restored before we can + * get to subordinate devices. */ -static void pci_slot_restore(struct pci_slot *slot) +static void pci_slot_restore_locked(struct pci_slot *slot) { struct pci_dev *dev; list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) continue; - pci_dev_lock(dev); pci_dev_restore(dev); - pci_dev_unlock(dev); if (dev->subordinate) - pci_bus_restore(dev->subordinate); + pci_bus_restore_locked(dev->subordinate); } } @@ -5177,17 +5179,15 @@ static int __pci_reset_slot(struct pci_slot *slot) if (rc) return rc; - pci_slot_save_and_disable(slot); - if (pci_slot_trylock(slot)) { + pci_slot_save_and_disable_locked(slot); might_sleep(); rc = pci_reset_hotplug_slot(slot->hotplug, 0); + pci_slot_restore_locked(slot); pci_slot_unlock(slot); } else rc = -EAGAIN; - pci_slot_restore(slot); - return rc; } @@ -5273,17 +5273,15 @@ static int __pci_reset_bus(struct pci_bus *bus) if (rc) return rc; - pci_bus_save_and_disable(bus); - if (pci_bus_trylock(bus)) { + pci_bus_save_and_disable_locked(bus); might_sleep(); rc = pci_bridge_secondary_bus_reset(bus->self); + pci_bus_restore_locked(bus); pci_bus_unlock(bus); } else rc = -EAGAIN; - pci_bus_restore(bus); - return rc; } -- cgit v1.2.3 From 8f55ed3f55544f61a4282aaeb64d1d903807b5eb Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Wed, 6 Mar 2019 06:09:46 +0000 Subject: PCI: Update PCIEPORTBUS Kconfig help text The Virtual Channel service has been removed and Downstream Port Containment has been added, so update the symbol description to be consistent with the current code. Signed-off-by: Hou Zhiqiang Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/Kconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 44742b2e1126..5cbdbca904ac 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -6,10 +6,9 @@ config PCIEPORTBUS bool "PCI Express Port Bus support" depends on PCI help - This automatically enables PCI Express Port Bus support. Users can - choose Native Hot-Plug support, Advanced Error Reporting support, - Power Management Event support and Virtual Channel support to run - on PCI Express Ports (Root or Switch). + This enables PCI Express Port Bus support. Users can then enable + support for Native Hot-Plug, Advanced Error Reporting, Power + Management Events, and Downstream Port Containment. # # Include service Kconfig here -- cgit v1.2.3