From patchwork Tue Feb 19 15:57:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Bogendoerfer X-Patchwork-Id: 10820099 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 590FE922 for ; Tue, 19 Feb 2019 15:58:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 47CE92C621 for ; Tue, 19 Feb 2019 15:58:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3BA002C634; Tue, 19 Feb 2019 15:58:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EBBED2C621 for ; Tue, 19 Feb 2019 15:58:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728150AbfBSP6G (ORCPT ); Tue, 19 Feb 2019 10:58:06 -0500 Received: from mx2.suse.de ([195.135.220.15]:51914 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728642AbfBSP5j (ORCPT ); Tue, 19 Feb 2019 10:57:39 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id E78BCAF5F; Tue, 19 Feb 2019 15:57:38 +0000 (UTC) From: Thomas Bogendoerfer To: Bjorn Helgaas , linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 07/10] PCI: call add_bus method also for root bus Date: Tue, 19 Feb 2019 16:57:21 +0100 Message-Id: <20190219155728.19163-8-tbogendoerfer@suse.de> X-Mailer: git-send-email 2.13.7 In-Reply-To: <20190219155728.19163-1-tbogendoerfer@suse.de> References: <20190219155728.19163-1-tbogendoerfer@suse.de> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP pci-xtalk controller code uses the add_bus method to set node of the bus device, which then is used for pcibus_to_node() implementation. Signed-off-by: Thomas Bogendoerfer --- drivers/pci/probe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..456448d5f46d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -837,6 +837,12 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) pcibios_add_bus(bus); + if (bus->ops->add_bus) { + err = bus->ops->add_bus(bus); + if (WARN_ON(err < 0)) + dev_err(&bus->dev, "failed to add bus: %d\n", err); + } + /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(bus); From patchwork Tue Feb 19 15:57:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Bogendoerfer X-Patchwork-Id: 10820095 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D88BF6CB for ; Tue, 19 Feb 2019 15:58:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C2E142C621 for ; Tue, 19 Feb 2019 15:58:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B69AD2C62B; Tue, 19 Feb 2019 15:58:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4C89E2C634 for ; Tue, 19 Feb 2019 15:58:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728913AbfBSP5m (ORCPT ); Tue, 19 Feb 2019 10:57:42 -0500 Received: from mx2.suse.de ([195.135.220.15]:51914 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726357AbfBSP5m (ORCPT ); Tue, 19 Feb 2019 10:57:42 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id B1C2AAF5F; Tue, 19 Feb 2019 15:57:40 +0000 (UTC) From: Thomas Bogendoerfer To: Ralf Baechle , Paul Burton , James Hogan , Lorenzo Pieralisi , Bjorn Helgaas , linux-mips@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org Subject: [PATCH v2 08/10] MIPS: SGI-IP27: use generic PCI driver Date: Tue, 19 Feb 2019 16:57:22 +0100 Message-Id: <20190219155728.19163-9-tbogendoerfer@suse.de> X-Mailer: git-send-email 2.13.7 In-Reply-To: <20190219155728.19163-1-tbogendoerfer@suse.de> References: <20190219155728.19163-1-tbogendoerfer@suse.de> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Converted bridge code to a platform driver using the PCI generic driver framework and use adding platform devices during xtalk scan. This allows easier sharing bridge driver for other SGI platforms like IP30 (Octane) and IP35 (Origin 3k, Fuel, Tezro). Signed-off-by: Thomas Bogendoerfer --- arch/mips/Kconfig | 2 + arch/mips/include/asm/dma-direct.h | 2 + arch/mips/include/asm/mach-generic/dma-direct.h | 7 + arch/mips/include/asm/mach-ip27/dma-direct.h | 20 ++ arch/mips/include/asm/mach-ip27/topology.h | 13 +- arch/mips/include/asm/pci.h | 8 + arch/mips/include/asm/pci/bridge.h | 6 +- arch/mips/include/asm/xtalk/xtalk.h | 9 - arch/mips/pci/Makefile | 1 - arch/mips/pci/pci-ip27.c | 214 ---------------- arch/mips/sgi-ip27/ip27-init.c | 2 + arch/mips/sgi-ip27/ip27-xtalk.c | 31 ++- drivers/pci/controller/Kconfig | 3 + drivers/pci/controller/Makefile | 1 + .../pci/controller/pci-xtalk-bridge.c | 278 +++++++++++++++++---- include/linux/platform_data/xtalk-bridge.h | 17 ++ 16 files changed, 312 insertions(+), 302 deletions(-) create mode 100644 arch/mips/include/asm/mach-generic/dma-direct.h create mode 100644 arch/mips/include/asm/mach-ip27/dma-direct.h delete mode 100644 arch/mips/pci/pci-ip27.c rename arch/mips/pci/ops-bridge.c => drivers/pci/controller/pci-xtalk-bridge.c (52%) create mode 100644 include/linux/platform_data/xtalk-bridge.h diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 93b88e8e72d0..57a424e82a18 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -678,6 +678,8 @@ config SGI_IP27 select HAVE_PCI select IRQ_MIPS_CPU select NR_CPUS_DEFAULT_64 + select PCI_DRIVERS_GENERIC + select PCI_XTALK_BRIDGE select SYS_HAS_CPU_R10000 select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_BIG_ENDIAN diff --git a/arch/mips/include/asm/dma-direct.h b/arch/mips/include/asm/dma-direct.h index b5c240806e1b..bd11e7934df1 100644 --- a/arch/mips/include/asm/dma-direct.h +++ b/arch/mips/include/asm/dma-direct.h @@ -2,6 +2,8 @@ #ifndef _MIPS_DMA_DIRECT_H #define _MIPS_DMA_DIRECT_H 1 +#include + static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) { if (!dev->dma_mask) diff --git a/arch/mips/include/asm/mach-generic/dma-direct.h b/arch/mips/include/asm/mach-generic/dma-direct.h new file mode 100644 index 000000000000..2d2c187dcd42 --- /dev/null +++ b/arch/mips/include/asm/mach-generic/dma-direct.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MACH_GENERIC_DMA_DIRECT_H +#define __ASM_MACH_GENERIC_DMA_DIRECT_H + +/* Intentionally empty file ... */ + +#endif /* __ASM_MACH_GENERIC_DMA_DIRECT_H */ diff --git a/arch/mips/include/asm/mach-ip27/dma-direct.h b/arch/mips/include/asm/mach-ip27/dma-direct.h new file mode 100644 index 000000000000..ec9856dfe2db --- /dev/null +++ b/arch/mips/include/asm/mach-ip27/dma-direct.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MACH_IP27_DMA_DIRECT_H +#define __ASM_MACH_IP27_DMA_DIRECT_H + +#include + +static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus); + + return bc->baddr + paddr; +} + +static inline phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t dma_addr) +{ + return dma_addr & ~(0xffUL << 56); +} + +#endif /* __ASM_MACH_IP27_DMA_DIRECT_H */ diff --git a/arch/mips/include/asm/mach-ip27/topology.h b/arch/mips/include/asm/mach-ip27/topology.h index 42ea1313626c..3cd03a74187e 100644 --- a/arch/mips/include/asm/mach-ip27/topology.h +++ b/arch/mips/include/asm/mach-ip27/topology.h @@ -7,18 +7,9 @@ #include struct cpuinfo_ip27 { -// cpuid_t p_cpuid; /* PROM assigned cpuid */ cnodeid_t p_nodeid; /* my node ID in compact-id-space */ nasid_t p_nasid; /* my node ID in numa-as-id-space */ unsigned char p_slice; /* Physical position on node board */ -#if 0 - unsigned long loops_per_sec; - unsigned long ipi_count; - unsigned long irq_attempt[NR_IRQS]; - unsigned long smp_local_irq_count; - unsigned long prof_multiplier; - unsigned long prof_counter; -#endif }; extern struct cpuinfo_ip27 sn_cpu_info[NR_CPUS]; @@ -27,10 +18,8 @@ extern struct cpuinfo_ip27 sn_cpu_info[NR_CPUS]; #define cpumask_of_node(node) ((node) == -1 ? \ cpu_all_mask : \ &hub_data(node)->h_cpus) -struct pci_bus; -extern int pcibus_to_node(struct pci_bus *); -#define cpumask_of_pcibus(bus) (cpu_online_mask) +#define cpumask_of_pcibus(bus) (cpumask_of_node(pcibus_to_node(bus))) extern unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES]; diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index 436099883022..70d1e51a7ca2 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -147,4 +147,12 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) return channel ? 15 : 14; } +#ifdef CONFIG_SGI_IP27 +/* Returns the node based on pci bus */ +static inline int pcibus_to_node(struct pci_bus *bus) +{ + return dev_to_node(&bus->dev); +} +#endif + #endif /* _ASM_PCI_H */ diff --git a/arch/mips/include/asm/pci/bridge.h b/arch/mips/include/asm/pci/bridge.h index 23574c27eb40..457ff868a027 100644 --- a/arch/mips/include/asm/pci/bridge.h +++ b/arch/mips/include/asm/pci/bridge.h @@ -801,15 +801,13 @@ struct bridge_err_cmdword { #define PCI64_ATTR_RMF_SHFT 48 struct bridge_controller { - struct pci_controller pc; struct resource mem; struct resource io; struct resource busn; struct bridge_regs *base; - nasid_t nasid; - unsigned int widget_id; u64 baddr; unsigned int pci_int[8]; + nasid_t nasid; }; #define BRIDGE_CONTROLLER(bus) \ @@ -824,6 +822,4 @@ struct bridge_controller { extern int request_bridge_irq(struct bridge_controller *bc, int pin); -extern struct pci_ops bridge_pci_ops; - #endif /* _ASM_PCI_BRIDGE_H */ diff --git a/arch/mips/include/asm/xtalk/xtalk.h b/arch/mips/include/asm/xtalk/xtalk.h index 26d2ed1fa917..680e7efebbaf 100644 --- a/arch/mips/include/asm/xtalk/xtalk.h +++ b/arch/mips/include/asm/xtalk/xtalk.h @@ -47,15 +47,6 @@ typedef struct xtalk_piomap_s *xtalk_piomap_t; #define XIO_PORT(x) ((xwidgetnum_t)(((x)&XIO_PORT_BITS) >> XIO_PORT_SHIFT)) #define XIO_PACK(p, o) ((((uint64_t)(p))< -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Max #PCI busses we can handle; ie, max #PCI bridges. - */ -#define MAX_PCI_BUSSES 40 - -/* - * XXX: No kmalloc available when we do our crosstalk scan, - * we should try to move it later in the boot process. - */ -static struct bridge_controller bridges[MAX_PCI_BUSSES]; - -extern struct pci_ops bridge_pci_ops; - -int bridge_probe(nasid_t nasid, int widget_id, int masterwid) -{ - unsigned long offset = NODE_OFFSET(nasid); - struct bridge_controller *bc; - static int num_bridges = 0; - int slot; - - pci_set_flags(PCI_PROBE_ONLY); - - printk("a bridge\n"); - - /* XXX: kludge alert.. */ - if (!num_bridges) - ioport_resource.end = ~0UL; - - bc = &bridges[num_bridges]; - - bc->pc.pci_ops = &bridge_pci_ops; - bc->pc.mem_resource = &bc->mem; - bc->pc.io_resource = &bc->io; - - bc->pc.index = num_bridges; - - bc->mem.name = "Bridge PCI MEM"; - bc->pc.mem_offset = offset; - bc->mem.start = 0; - bc->mem.end = ~0UL; - bc->mem.flags = IORESOURCE_MEM; - - bc->io.name = "Bridge IO MEM"; - bc->pc.io_offset = offset; - bc->io.start = 0UL; - bc->io.end = ~0UL; - bc->io.flags = IORESOURCE_IO; - - bc->widget_id = widget_id; - bc->nasid = nasid; - - bc->baddr = (u64)masterwid << 60 | PCI64_ATTR_BAR; - - /* - * point to this bridge - */ - bc->base = (struct bridge_regs *)RAW_NODE_SWIN_BASE(nasid, widget_id); - - /* - * Clear all pending interrupts. - */ - bridge_write(bc, b_int_rst_stat, BRIDGE_IRR_ALL_CLR); - - /* - * Until otherwise set up, assume all interrupts are from slot 0 - */ - bridge_write(bc, b_int_device, 0x0); - - /* - * swap pio's to pci mem and io space (big windows) - */ - bridge_set(bc, b_wid_control, BRIDGE_CTRL_IO_SWAP | - BRIDGE_CTRL_MEM_SWAP); -#ifdef CONFIG_PAGE_SIZE_4KB - bridge_clr(bc, b_wid_control, BRIDGE_CTRL_PAGE_SIZE); -#else /* 16kB or larger */ - bridge_set(bc, b_wid_control, BRIDGE_CTRL_PAGE_SIZE); -#endif - - /* - * Hmm... IRIX sets additional bits in the address which - * are documented as reserved in the bridge docs. - */ - bridge_write(bc, b_wid_int_upper, 0x8000 | (masterwid << 16)); - bridge_write(bc, b_wid_int_lower, 0x01800090); /* PI_INT_PEND_MOD off*/ - bridge_write(bc, b_dir_map, (masterwid << 20)); /* DMA */ - bridge_write(bc, b_int_enable, 0); - - for (slot = 0; slot < 8; slot ++) { - bridge_set(bc, b_device[slot].reg, BRIDGE_DEV_SWAP_DIR); - bc->pci_int[slot] = -1; - } - bridge_read(bc, b_wid_tflush); /* wait until Bridge PIO complete */ - - register_pci_controller(&bc->pc); - - num_bridges++; - - return 0; -} - -/* - * All observed requests have pin == 1. We could have a global here, that - * gets incremented and returned every time - unfortunately, pci_map_irq - * may be called on the same device over and over, and need to return the - * same value. On O2000, pin can be 0 or 1, and PCI slots can be [0..7]. - * - * A given PCI device, in general, should be able to intr any of the cpus - * on any one of the hubs connected to its xbow. - */ -int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - return 0; -} - -static inline struct pci_dev *bridge_root_dev(struct pci_dev *dev) -{ - while (dev->bus->parent) { - /* Move up the chain of bridges. */ - dev = dev->bus->self; - } - - return dev; -} - -/* Do platform specific device initialization at pci_enable_device() time */ -int pcibios_plat_dev_init(struct pci_dev *dev) -{ - struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); - struct pci_dev *rdev = bridge_root_dev(dev); - int slot = PCI_SLOT(rdev->devfn); - int irq; - - irq = bc->pci_int[slot]; - if (irq == -1) { - irq = request_bridge_irq(bc, slot); - if (irq < 0) - return irq; - - bc->pci_int[slot] = irq; - } - dev->irq = irq; - - return 0; -} - -dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus); - - return bc->baddr + paddr; -} - -phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t dma_addr) -{ - return dma_addr & ~(0xffUL << 56); -} - -/* - * Device might live on a subordinate PCI bus. XXX Walk up the chain of buses - * to find the slot number in sense of the bridge device register. - * XXX This also means multiple devices might rely on conflicting bridge - * settings. - */ - -static inline void pci_disable_swapping(struct pci_dev *dev) -{ - struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); - struct bridge_regs *bridge = bc->base; - int slot = PCI_SLOT(dev->devfn); - - /* Turn off byte swapping */ - bridge->b_device[slot].reg &= ~BRIDGE_DEV_SWAP_DIR; - bridge->b_widget.w_tflush; /* Flush */ -} - -static void pci_fixup_ioc3(struct pci_dev *d) -{ - pci_disable_swapping(d); -} - -#ifdef CONFIG_NUMA -int pcibus_to_node(struct pci_bus *bus) -{ - struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); - - return bc->nasid; -} -EXPORT_SYMBOL(pcibus_to_node); -#endif /* CONFIG_NUMA */ - -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - pci_fixup_ioc3); diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c index 6074efeff894..066b33f50bcc 100644 --- a/arch/mips/sgi-ip27/ip27-init.c +++ b/arch/mips/sgi-ip27/ip27-init.c @@ -184,5 +184,7 @@ void __init plat_mem_setup(void) ioc3_eth_init(); + ioport_resource.start = 0; + ioport_resource.end = ~0UL; set_io_port_base(IO_BASE); } diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c index ce06aaa115ae..8579b651d862 100644 --- a/arch/mips/sgi-ip27/ip27-xtalk.c +++ b/arch/mips/sgi-ip27/ip27-xtalk.c @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -20,7 +22,19 @@ #define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbow in Xbridge */ #define BASE_XBOW_PORT 8 /* Lowest external port */ -extern int bridge_probe(nasid_t nasid, int widget, int masterwid); +static void bridge_platform_create(nasid_t nasid, int widget, int masterwid) +{ + struct platform_device *pdev; + struct xtalk_bridge_platform_data bridge_data; + + pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); + bridge_data.nasid = nasid; + bridge_data.widget = widget; + bridge_data.masterwid = masterwid; + platform_device_add_data(pdev, &bridge_data, sizeof(bridge_data)); + platform_device_add(pdev); + pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget); +} static int probe_one_port(nasid_t nasid, int widget, int masterwid) { @@ -31,13 +45,10 @@ static int probe_one_port(nasid_t nasid, int widget, int masterwid) (RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID); partnum = XWIDGET_PART_NUM(widget_id); - printk(KERN_INFO "Cpu %d, Nasid 0x%x, widget 0x%x (partnum 0x%x) is ", - smp_processor_id(), nasid, widget, partnum); - switch (partnum) { case BRIDGE_WIDGET_PART_NUM: case XBRIDGE_WIDGET_PART_NUM: - bridge_probe(nasid, widget, masterwid); + bridge_platform_create(nasid, widget, masterwid); break; default: break; @@ -52,8 +63,6 @@ static int xbow_probe(nasid_t nasid) klxbow_t *xbow_p; unsigned masterwid, i; - printk("is xbow\n"); - /* * found xbow, so may have multiple bridges * need to probe xbow @@ -117,19 +126,17 @@ static void xtalk_probe_node(cnodeid_t nid) (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID); partnum = XWIDGET_PART_NUM(widget_id); - printk(KERN_INFO "Cpu %d, Nasid 0x%x: partnum 0x%x is ", - smp_processor_id(), nasid, partnum); - switch (partnum) { case BRIDGE_WIDGET_PART_NUM: - bridge_probe(nasid, 0x8, 0xa); + bridge_platform_create(nasid, 0x8, 0xa); break; case XBOW_WIDGET_PART_NUM: case XXBOW_WIDGET_PART_NUM: + pr_info("xtalk:n%d/0 xbow widget\n", nasid); xbow_probe(nasid); break; default: - printk(" unknown widget??\n"); + pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum); break; } } diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 6671946dbf66..c3815353f2f8 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -265,6 +265,9 @@ config PCIE_TANGO_SMP8759 This can lead to data corruption if drivers perform concurrent config and MMIO accesses. +config PCI_XTALK_BRIDGE + bool + config VMD depends on PCI_MSI && X86_64 && SRCU tristate "Intel Volume Management Device Driver" diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index d56a507495c5..bcbd740f878c 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o +obj-$(CONFIG_PCI_XTALK_BRIDGE) += pci-xtalk-bridge.o obj-$(CONFIG_VMD) += vmd.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/arch/mips/pci/ops-bridge.c b/drivers/pci/controller/pci-xtalk-bridge.c similarity index 52% rename from arch/mips/pci/ops-bridge.c rename to drivers/pci/controller/pci-xtalk-bridge.c index df95b0da08f2..77e42c46937b 100644 --- a/arch/mips/pci/ops-bridge.c +++ b/drivers/pci/controller/pci-xtalk-bridge.c @@ -3,13 +3,21 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1999, 2000, 04, 06 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2003 Christoph Hellwig (hch@lst.de) + * Copyright (C) 1999, 2000, 04 Ralf Baechle (ralf@linux-mips.org) * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ +#include +#include #include -#include -#include +#include +#include +#include +#include + #include +#include +#include #include #include @@ -29,6 +37,20 @@ static u32 emulate_ioc3_cfg(int where, int size) return 0; } +static void bridge_disable_swapping(struct pci_dev *dev) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); + int slot = PCI_SLOT(dev->devfn); + + /* Turn off byte swapping */ + bridge_clr(bc, b_device[slot].reg, BRIDGE_DEV_SWAP_DIR); + bridge_read(bc, b_widget.w_tflush); /* Flush */ +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + bridge_disable_swapping); + + /* * The Bridge ASIC supports both type 0 and type 1 access. Type 1 is * not really documented, so right now I can't write code which uses it. @@ -39,20 +61,19 @@ static u32 emulate_ioc3_cfg(int where, int size) * which is used in SGI systems. The IOC3 can only handle 32-bit PCI * accesses and does only decode parts of it's address space. */ - static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * value) + int where, int size, u32 *value) { struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); struct bridge_regs *bridge = bc->base; int slot = PCI_SLOT(devfn); int fn = PCI_FUNC(devfn); - volatile void *addr; + void *addr; u32 cf, shift, mask; int res; addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID]; - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; /* @@ -65,11 +86,11 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn, addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)]; if (size == 1) - res = get_dbe(*value, (u8 *) addr); + res = get_dbe(*value, (u8 *)addr); else if (size == 2) - res = get_dbe(*value, (u16 *) addr); + res = get_dbe(*value, (u16 *)addr); else - res = get_dbe(*value, (u32 *) addr); + res = get_dbe(*value, (u32 *)addr); return res ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; @@ -84,8 +105,7 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn, } addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; - - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; shift = ((where & 3) << 3); @@ -96,20 +116,20 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn, } static int pci_conf1_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * value) + int where, int size, u32 *value) { struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); struct bridge_regs *bridge = bc->base; int busno = bus->number; int slot = PCI_SLOT(devfn); int fn = PCI_FUNC(devfn); - volatile void *addr; + void *addr; u32 cf, shift, mask; int res; bridge_write(bc, b_pci_cfg, (busno << 16) | (slot << 11)); addr = &bridge->b_type1_cfg.c[(fn << 8) | PCI_VENDOR_ID]; - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; /* @@ -119,15 +139,14 @@ static int pci_conf1_read_config(struct pci_bus *bus, unsigned int devfn, if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) goto is_ioc3; - bridge_write(bc, b_pci_cfg, (busno << 16) | (slot << 11)); addr = &bridge->b_type1_cfg.c[(fn << 8) | (where ^ (4 - size))]; if (size == 1) - res = get_dbe(*value, (u8 *) addr); + res = get_dbe(*value, (u8 *)addr); else if (size == 2) - res = get_dbe(*value, (u16 *) addr); + res = get_dbe(*value, (u16 *)addr); else - res = get_dbe(*value, (u32 *) addr); + res = get_dbe(*value, (u32 *)addr); return res ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; @@ -141,10 +160,8 @@ static int pci_conf1_read_config(struct pci_bus *bus, unsigned int devfn, return PCIBIOS_SUCCESSFUL; } - bridge_write(bc, b_pci_cfg, (busno << 16) | (slot << 11)); addr = &bridge->b_type1_cfg.c[(fn << 8) | where]; - - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; shift = ((where & 3) << 3); @@ -155,7 +172,7 @@ static int pci_conf1_read_config(struct pci_bus *bus, unsigned int devfn, } static int pci_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * value) + int where, int size, u32 *value) { if (!pci_is_root_bus(bus)) return pci_conf1_read_config(bus, devfn, where, size, value); @@ -170,12 +187,12 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn, struct bridge_regs *bridge = bc->base; int slot = PCI_SLOT(devfn); int fn = PCI_FUNC(devfn); - volatile void *addr; + void *addr; u32 cf, shift, mask, smask; int res; addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID]; - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; /* @@ -187,13 +204,12 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn, addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)]; - if (size == 1) { - res = put_dbe(value, (u8 *) addr); - } else if (size == 2) { - res = put_dbe(value, (u16 *) addr); - } else { - res = put_dbe(value, (u32 *) addr); - } + if (size == 1) + res = put_dbe(value, (u8 *)addr); + else if (size == 2) + res = put_dbe(value, (u16 *)addr); + else + res = put_dbe(value, (u32 *)addr); if (res) return PCIBIOS_DEVICE_NOT_FOUND; @@ -210,7 +226,7 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn, addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; shift = ((where & 3) << 3); @@ -218,7 +234,7 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn, smask = mask << shift; cf = (cf & ~smask) | ((value & mask) << shift); - if (put_dbe(cf, (u32 *) addr)) + if (put_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_SUCCESSFUL; @@ -232,13 +248,13 @@ static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn, int slot = PCI_SLOT(devfn); int fn = PCI_FUNC(devfn); int busno = bus->number; - volatile void *addr; + void *addr; u32 cf, shift, mask, smask; int res; bridge_write(bc, b_pci_cfg, (busno << 16) | (slot << 11)); addr = &bridge->b_type1_cfg.c[(fn << 8) | PCI_VENDOR_ID]; - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; /* @@ -250,13 +266,12 @@ static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn, addr = &bridge->b_type1_cfg.c[(fn << 8) | (where ^ (4 - size))]; - if (size == 1) { - res = put_dbe(value, (u8 *) addr); - } else if (size == 2) { - res = put_dbe(value, (u16 *) addr); - } else { - res = put_dbe(value, (u32 *) addr); - } + if (size == 1) + res = put_dbe(value, (u8 *)addr); + else if (size == 2) + res = put_dbe(value, (u16 *)addr); + else + res = put_dbe(value, (u32 *)addr); if (res) return PCIBIOS_DEVICE_NOT_FOUND; @@ -272,8 +287,7 @@ static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn, return PCIBIOS_SUCCESSFUL; addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; - - if (get_dbe(cf, (u32 *) addr)) + if (get_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; shift = ((where & 3) << 3); @@ -281,7 +295,7 @@ static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn, smask = mask << shift; cf = (cf & ~smask) | ((value & mask) << shift); - if (put_dbe(cf, (u32 *) addr)) + if (put_dbe(cf, (u32 *)addr)) return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_SUCCESSFUL; @@ -296,7 +310,173 @@ static int pci_write_config(struct pci_bus *bus, unsigned int devfn, return pci_conf0_write_config(bus, devfn, where, size, value); } -struct pci_ops bridge_pci_ops = { - .read = pci_read_config, - .write = pci_write_config, +static int bridge_add_bus(struct pci_bus *bus) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + + set_dev_node(&bus->dev, bc->nasid); + return 0; +} + +static struct pci_ops bridge_pci_ops = { + .add_bus = bridge_add_bus, + .read = pci_read_config, + .write = pci_write_config, +}; + +/* + * All observed requests have pin == 1. We could have a global here, that + * gets incremented and returned every time - unfortunately, pci_map_irq + * may be called on the same device over and over, and need to return the + * same value. On O2000, pin can be 0 or 1, and PCI slots can be [0..7]. + * + * A given PCI device, in general, should be able to intr any of the cpus + * on any one of the hubs connected to its xbow. + */ +static int bridge_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); + int irq; + + irq = bc->pci_int[slot]; + if (irq == -1) { + irq = request_bridge_irq(bc, slot); + if (irq < 0) + return irq; + + bc->pci_int[slot] = irq; + } + return irq; +} + +static int bridge_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bridge_controller *bc; + struct pci_host_bridge *host; + unsigned long offset; + int slot; + int err; + struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev); + + offset = NODE_OFFSET(bd->nasid); + + pci_set_flags(PCI_PROBE_ONLY); + + host = devm_pci_alloc_host_bridge(dev, sizeof(*bc)); + if (!host) + return -ENOMEM; + + bc = pci_host_bridge_priv(host); + + bc->mem.name = "Bridge PCI MEM"; + bc->mem.start = offset + (bd->widget << SWIN_SIZE_BITS); + bc->mem.end = bc->mem.start + SWIN_SIZE; + bc->mem.flags = IORESOURCE_MEM; + + bc->io.name = "Bridge PCI IO"; + bc->io.start = offset + (bd->widget << SWIN_SIZE_BITS); + bc->io.end = bc->io.start + SWIN_SIZE; + bc->io.flags = IORESOURCE_IO; + + bc->busn.name = "Bridge PCI busn"; + bc->busn.start = 0; + bc->busn.end = 0xff; + bc->busn.flags = IORESOURCE_BUS; + + pci_add_resource_offset(&host->windows, &bc->mem, offset); + pci_add_resource_offset(&host->windows, &bc->io, offset); + pci_add_resource(&host->windows, &bc->busn); + + err = devm_request_pci_bus_resources(dev, &host->windows); + if (err < 0) { + pci_free_resource_list(&host->windows); + return err; + } + + bc->nasid = bd->nasid; + + bc->baddr = (u64)bd->masterwid << 60 | PCI64_ATTR_BAR; + + /* + * point to this bridge + */ + bc->base = (struct bridge_regs *)RAW_NODE_SWIN_BASE(bd->nasid, + bd->widget); + + /* + * Clear all pending interrupts. + */ + bridge_write(bc, b_int_rst_stat, BRIDGE_IRR_ALL_CLR); + + /* + * Until otherwise set up, assume all interrupts are from slot 0 + */ + bridge_write(bc, b_int_device, 0x0); + + /* + * disable swapping for big windows + */ + bridge_clr(bc, b_wid_control, + BRIDGE_CTRL_IO_SWAP | BRIDGE_CTRL_MEM_SWAP); +#ifdef CONFIG_PAGE_SIZE_4KB + bridge_clr(bc, b_wid_control, BRIDGE_CTRL_PAGE_SIZE); +#else /* 16kB or larger */ + bridge_set(bc, b_wid_control, BRIDGE_CTRL_PAGE_SIZE); +#endif + + /* + * Hmm... IRIX sets additional bits in the address which + * are documented as reserved in the bridge docs. + */ + bridge_write(bc, b_wid_int_upper, 0x8000 | (bd->masterwid << 16)); + bridge_write(bc, b_wid_int_lower, 0x01800090); /* PI_INT_PEND_MOD off*/ + bridge_write(bc, b_dir_map, (bd->masterwid << 20)); /* DMA */ + bridge_write(bc, b_int_enable, 0); + + for (slot = 0; slot < 8; slot++) { + bridge_set(bc, b_device[slot].reg, BRIDGE_DEV_SWAP_DIR); + bc->pci_int[slot] = -1; + } + bridge_read(bc, b_wid_tflush); /* wait until Bridge PIO complete */ + + host->dev.parent = dev; + host->sysdata = bc; + host->busnr = 0; + host->ops = &bridge_pci_ops; + host->map_irq = bridge_map_irq; + host->swizzle_irq = pci_common_swizzle; + + err = pci_scan_root_bus_bridge(host); + if (err < 0) + return err; + + pci_bus_claim_resources(host->bus); + pci_bus_add_devices(host->bus); + + platform_set_drvdata(pdev, host->bus); + + return 0; +} + +static int bridge_remove(struct platform_device *pdev) +{ + struct pci_bus *bus = platform_get_drvdata(pdev); + + pci_lock_rescan_remove(); + pci_stop_root_bus(bus); + pci_remove_root_bus(bus); + pci_unlock_rescan_remove(); + + return 0; +} + +static struct platform_driver bridge_driver = { + .probe = bridge_probe, + .remove = bridge_remove, + .driver = { + .name = "xtalk-bridge", + } }; + +builtin_platform_driver(bridge_driver); diff --git a/include/linux/platform_data/xtalk-bridge.h b/include/linux/platform_data/xtalk-bridge.h new file mode 100644 index 000000000000..818d4b457429 --- /dev/null +++ b/include/linux/platform_data/xtalk-bridge.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SGI PCI Xtalk Bridge + */ + +#ifndef PLATFORM_DATA_XTALK_BRIDGE_H +#define PLATFORM_DATA_XTALK_BRIDGE_H + +#include + +struct xtalk_bridge_platform_data { + nasid_t nasid; + int widget; + int masterwid; +}; + +#endif /* PLATFORM_DATA_XTALK_BRIDGE_H */ From patchwork Tue Feb 19 15:57:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Bogendoerfer X-Patchwork-Id: 10820089 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 04E93922 for ; Tue, 19 Feb 2019 15:58:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E11D12C621 for ; Tue, 19 Feb 2019 15:58:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D07562C677; Tue, 19 Feb 2019 15:58:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A558D2C621 for ; Tue, 19 Feb 2019 15:58:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728956AbfBSP5n (ORCPT ); Tue, 19 Feb 2019 10:57:43 -0500 Received: from mx2.suse.de ([195.135.220.15]:51930 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728893AbfBSP5n (ORCPT ); Tue, 19 Feb 2019 10:57:43 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 9201CAFB5; Tue, 19 Feb 2019 15:57:41 +0000 (UTC) From: Thomas Bogendoerfer To: Ralf Baechle , Paul Burton , James Hogan , Lorenzo Pieralisi , Bjorn Helgaas , linux-mips@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org Subject: [PATCH v2 10/10] MIPS: SGI-IP27: abstract chipset irq from bridge Date: Tue, 19 Feb 2019 16:57:24 +0100 Message-Id: <20190219155728.19163-11-tbogendoerfer@suse.de> X-Mailer: git-send-email 2.13.7 In-Reply-To: <20190219155728.19163-1-tbogendoerfer@suse.de> References: <20190219155728.19163-1-tbogendoerfer@suse.de> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Bridge ASIC is widely used in different SGI systems, but the connected chipset is either HUB, HEART or BEDROCK. This commit switches to irq domain hierarchy for hub and bridge interrupts to get bridge setup out of hub interrupt code. Signed-off-by: Thomas Bogendoerfer --- arch/mips/Kconfig | 1 + arch/mips/include/asm/pci/bridge.h | 3 +- arch/mips/include/asm/sn/intr.h | 6 + arch/mips/sgi-ip27/ip27-irq.c | 178 +++++++++++++----------------- drivers/pci/controller/pci-xtalk-bridge.c | 145 +++++++++++++++++++++++- 5 files changed, 229 insertions(+), 104 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 57a424e82a18..ba55c4af8798 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -677,6 +677,7 @@ config SGI_IP27 select SYS_HAS_EARLY_PRINTK select HAVE_PCI select IRQ_MIPS_CPU + select IRQ_DOMAIN_HIERARCHY select NR_CPUS_DEFAULT_64 select PCI_DRIVERS_GENERIC select PCI_XTALK_BRIDGE diff --git a/arch/mips/include/asm/pci/bridge.h b/arch/mips/include/asm/pci/bridge.h index 457ff868a027..4b8570e4fb48 100644 --- a/arch/mips/include/asm/pci/bridge.h +++ b/arch/mips/include/asm/pci/bridge.h @@ -806,6 +806,7 @@ struct bridge_controller { struct resource busn; struct bridge_regs *base; u64 baddr; + struct irq_domain *domain; unsigned int pci_int[8]; nasid_t nasid; }; @@ -820,6 +821,4 @@ struct bridge_controller { #define bridge_clr(bc, reg, val) \ __raw_writel(__raw_readl(&bc->base->reg) & ~(val), &bc->base->reg) -extern int request_bridge_irq(struct bridge_controller *bc, int pin); - #endif /* _ASM_PCI_BRIDGE_H */ diff --git a/arch/mips/include/asm/sn/intr.h b/arch/mips/include/asm/sn/intr.h index fc1348193957..bdb8ca95183a 100644 --- a/arch/mips/include/asm/sn/intr.h +++ b/arch/mips/include/asm/sn/intr.h @@ -126,4 +126,10 @@ do { \ #define NI_ERROR_INTR 62 #define MSC_PANIC_INTR 63 +struct irq_alloc_info { + void *ctrl; + nasid_t nasid; + int pin; +}; + #endif /* __ASM_SN_INTR_H */ diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c index 710a59764b01..b992c2b08e3b 100644 --- a/arch/mips/sgi-ip27/ip27-irq.c +++ b/arch/mips/sgi-ip27/ip27-irq.c @@ -12,10 +12,10 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -23,11 +23,9 @@ #include struct hub_irq_data { - struct bridge_controller *bc; u64 *irq_mask[2]; cpuid_t cpu; int bit; - int pin; }; static DECLARE_BITMAP(hub_irq_map, IP27_HUB_IRQ_COUNT); @@ -69,67 +67,6 @@ static void disable_hub_irq(struct irq_data *d) __raw_writeq(mask[1], hd->irq_mask[1]); } -static unsigned int startup_bridge_irq(struct irq_data *d) -{ - struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); - struct bridge_controller *bc; - nasid_t nasid; - u32 device; - int pin; - - if (!hd) - return -EINVAL; - - pin = hd->pin; - bc = hd->bc; - - nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(hd->cpu)); - bridge_write(bc, b_int_addr[pin].addr, - (0x20000 | hd->bit | (nasid << 8))); - bridge_set(bc, b_int_enable, (1 << pin)); - bridge_set(bc, b_int_enable, 0x7ffffe00); /* more stuff in int_enable */ - - /* - * Enable sending of an interrupt clear packt to the hub on a high to - * low transition of the interrupt pin. - * - * IRIX sets additional bits in the address which are documented as - * reserved in the bridge docs. - */ - bridge_set(bc, b_int_mode, (1UL << pin)); - - /* - * We assume the bridge to have a 1:1 mapping between devices - * (slots) and intr pins. - */ - device = bridge_read(bc, b_int_device); - device &= ~(7 << (pin*3)); - device |= (pin << (pin*3)); - bridge_write(bc, b_int_device, device); - - bridge_read(bc, b_wid_tflush); - - enable_hub_irq(d); - - return 0; /* Never anything pending. */ -} - -static void shutdown_bridge_irq(struct irq_data *d) -{ - struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); - struct bridge_controller *bc; - int pin = hd->pin; - - if (!hd) - return; - - disable_hub_irq(d); - - bc = hd->bc; - bridge_clr(bc, b_int_enable, (1 << pin)); - bridge_read(bc, b_wid_tflush); -} - static void setup_hub_mask(struct hub_irq_data *hd, const struct cpumask *mask) { nasid_t nasid; @@ -164,7 +101,7 @@ static int set_affinity_hub_irq(struct irq_data *d, const struct cpumask *mask, setup_hub_mask(hd, mask); if (irqd_is_started(d)) - startup_bridge_irq(d); + enable_hub_irq(d); irq_data_update_effective_affinity(d, cpumask_of(hd->cpu)); @@ -173,20 +110,22 @@ static int set_affinity_hub_irq(struct irq_data *d, const struct cpumask *mask, static struct irq_chip hub_irq_type = { .name = "HUB", - .irq_startup = startup_bridge_irq, - .irq_shutdown = shutdown_bridge_irq, .irq_mask = disable_hub_irq, .irq_unmask = enable_hub_irq, .irq_set_affinity = set_affinity_hub_irq, }; -int request_bridge_irq(struct bridge_controller *bc, int pin) +static int hub_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) { + struct irq_alloc_info *info = arg; struct hub_irq_data *hd; struct hub_data *hub; struct irq_desc *desc; int swlevel; - int irq; + + if (nr_irqs > 1 || !info) + return -EINVAL; hd = kzalloc(sizeof(*hd), GFP_KERNEL); if (!hd) @@ -197,46 +136,39 @@ int request_bridge_irq(struct bridge_controller *bc, int pin) kfree(hd); return -EAGAIN; } - irq = swlevel + IP27_HUB_IRQ_BASE; - - hd->bc = bc; hd->bit = swlevel; - hd->pin = pin; - irq_set_chip_data(irq, hd); + irq_domain_set_info(domain, virq, swlevel, &hub_irq_type, hd, + handle_level_irq, NULL, NULL); /* use CPU connected to nearest hub */ - hub = hub_data(NASID_TO_COMPACT_NODEID(bc->nasid)); + hub = hub_data(NASID_TO_COMPACT_NODEID(info->nasid)); setup_hub_mask(hd, &hub->h_cpus); - desc = irq_to_desc(irq); - desc->irq_common_data.node = bc->nasid; + desc = irq_to_desc(virq); + desc->irq_common_data.node = info->nasid; cpumask_copy(desc->irq_common_data.affinity, &hub->h_cpus); - return irq; + return 0; } -void ip27_hub_irq_init(void) +static void hub_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) { - int i; + struct irq_data *irqd; - for (i = IP27_HUB_IRQ_BASE; - i < (IP27_HUB_IRQ_BASE + IP27_HUB_IRQ_COUNT); i++) - irq_set_chip_and_handler(i, &hub_irq_type, handle_level_irq); - - /* - * Some interrupts are reserved by hardware or by software convention. - * Mark these as reserved right away so they won't be used accidentally - * later. - */ - for (i = 0; i <= BASE_PCI_IRQ; i++) - set_bit(i, hub_irq_map); - - set_bit(IP_PEND0_6_63, hub_irq_map); + if (nr_irqs > 1) + return; - for (i = NI_BRDCAST_ERR_A; i <= MSC_PANIC_INTR; i++) - set_bit(i, hub_irq_map); + irqd = irq_domain_get_irq_data(domain, virq); + if (irqd && irqd->chip_data) + kfree(irqd->chip_data); } +static const struct irq_domain_ops hub_domain_ops = { + .alloc = hub_domain_alloc, + .free = hub_domain_free, +}; + /* * This code is unnecessarily complex, because we do * intr enabling. Basically, once we grab the set of intrs we need @@ -253,7 +185,9 @@ static void ip27_do_irq_mask0(struct irq_desc *desc) { cpuid_t cpu = smp_processor_id(); unsigned long *mask = per_cpu(irq_enable_mask, cpu); + struct irq_domain *domain; u64 pend0; + int irq; /* copied from Irix intpend0() */ pend0 = LOCAL_HUB_L(PI_INT_PEND0); @@ -277,7 +211,14 @@ static void ip27_do_irq_mask0(struct irq_desc *desc) generic_smp_call_function_interrupt(); } else #endif - generic_handle_irq(__ffs(pend0) + IP27_HUB_IRQ_BASE); + { + domain = irq_desc_get_handler_data(desc); + irq = irq_linear_revmap(domain, __ffs(pend0)); + if (irq) + generic_handle_irq(irq); + else + spurious_interrupt(); + } LOCAL_HUB_L(PI_INT_PEND0); } @@ -286,7 +227,9 @@ static void ip27_do_irq_mask1(struct irq_desc *desc) { cpuid_t cpu = smp_processor_id(); unsigned long *mask = per_cpu(irq_enable_mask, cpu); + struct irq_domain *domain; u64 pend1; + int irq; /* copied from Irix intpend0() */ pend1 = LOCAL_HUB_L(PI_INT_PEND1); @@ -295,7 +238,12 @@ static void ip27_do_irq_mask1(struct irq_desc *desc) if (!pend1) return; - generic_handle_irq(__ffs(pend1) + IP27_HUB_IRQ_BASE + 64); + domain = irq_desc_get_handler_data(desc); + irq = irq_linear_revmap(domain, __ffs(pend1) + 64); + if (irq) + generic_handle_irq(irq); + else + spurious_interrupt(); LOCAL_HUB_L(PI_INT_PEND1); } @@ -326,11 +274,41 @@ void install_ipi(void) void __init arch_init_irq(void) { + struct irq_domain *domain; + struct fwnode_handle *fn; + int i; + mips_cpu_irq_init(); - ip27_hub_irq_init(); + + /* + * Some interrupts are reserved by hardware or by software convention. + * Mark these as reserved right away so they won't be used accidentally + * later. + */ + for (i = 0; i <= BASE_PCI_IRQ; i++) + set_bit(i, hub_irq_map); + + set_bit(IP_PEND0_6_63, hub_irq_map); + + for (i = NI_BRDCAST_ERR_A; i <= MSC_PANIC_INTR; i++) + set_bit(i, hub_irq_map); + + fn = irq_domain_alloc_named_fwnode("HUB"); + WARN_ON(fn == NULL); + if (!fn) + return; + domain = irq_domain_create_linear(fn, IP27_HUB_IRQ_COUNT, + &hub_domain_ops, NULL); + WARN_ON(domain == NULL); + if (!domain) + return; + + irq_set_default_host(domain); irq_set_percpu_devid(IP27_HUB_PEND0_IRQ); - irq_set_chained_handler(IP27_HUB_PEND0_IRQ, ip27_do_irq_mask0); + irq_set_chained_handler_and_data(IP27_HUB_PEND0_IRQ, ip27_do_irq_mask0, + domain); irq_set_percpu_devid(IP27_HUB_PEND1_IRQ); - irq_set_chained_handler(IP27_HUB_PEND1_IRQ, ip27_do_irq_mask1); + irq_set_chained_handler_and_data(IP27_HUB_PEND1_IRQ, ip27_do_irq_mask1, + domain); } diff --git a/drivers/pci/controller/pci-xtalk-bridge.c b/drivers/pci/controller/pci-xtalk-bridge.c index 77e42c46937b..0e3a4bd9484d 100644 --- a/drivers/pci/controller/pci-xtalk-bridge.c +++ b/drivers/pci/controller/pci-xtalk-bridge.c @@ -324,6 +324,129 @@ static struct pci_ops bridge_pci_ops = { .write = pci_write_config, }; +struct bridge_irq_chip_data { + struct bridge_controller *bc; + nasid_t nasid; +}; + +static int bridge_set_affinity(struct irq_data *d, const struct cpumask *mask, + bool force) +{ + struct bridge_irq_chip_data *data = d->chip_data; + int bit = d->parent_data->hwirq; + int pin = d->hwirq; + nasid_t nasid; + int ret, cpu; + + ret = irq_chip_set_affinity_parent(d, mask, force); + if (ret >= 0) { + cpu = cpumask_first_and(mask, cpu_online_mask); + nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); + bridge_write(data->bc, b_int_addr[pin].addr, + (0x20000 | bit | (nasid << 8))); + bridge_read(data->bc, b_wid_tflush); + } + return ret; +} + +struct irq_chip bridge_irq_chip = { + .name = "BRIDGE", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_affinity = bridge_set_affinity +}; + +static int bridge_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct bridge_irq_chip_data *data; + struct irq_alloc_info *info = arg; + int ret; + + if (nr_irqs > 1 || !info) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret >= 0) { + data->bc = info->ctrl; + data->nasid = info->nasid; + irq_domain_set_info(domain, virq, info->pin, &bridge_irq_chip, + data, handle_level_irq, NULL, NULL); + } else { + kfree(data); + } + + return ret; +} + +static void bridge_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *irqd = irq_domain_get_irq_data(domain, virq); + + if (nr_irqs) + return; + + kfree(irqd->chip_data); + irq_domain_free_irqs_top(domain, virq, nr_irqs); +} + +static int bridge_domain_activate(struct irq_domain *domain, + struct irq_data *irqd, bool reserve) +{ + struct bridge_irq_chip_data *data = irqd->chip_data; + struct bridge_controller *bc = data->bc; + int bit = irqd->parent_data->hwirq; + int pin = irqd->hwirq; + u32 device; + + bridge_write(bc, b_int_addr[pin].addr, + (0x20000 | bit | (data->nasid << 8))); + bridge_set(bc, b_int_enable, (1 << pin)); + bridge_set(bc, b_int_enable, 0x7ffffe00); /* more stuff in int_enable */ + + /* + * Enable sending of an interrupt clear packt to the hub on a high to + * low transition of the interrupt pin. + * + * IRIX sets additional bits in the address which are documented as + * reserved in the bridge docs. + */ + bridge_set(bc, b_int_mode, (1UL << pin)); + + /* + * We assume the bridge to have a 1:1 mapping between devices + * (slots) and intr pins. + */ + device = bridge_read(bc, b_int_device); + device &= ~(7 << (pin*3)); + device |= (pin << (pin*3)); + bridge_write(bc, b_int_device, device); + + bridge_read(bc, b_wid_tflush); + return 0; +} + +static void bridge_domain_deactivate(struct irq_domain *domain, + struct irq_data *irqd) +{ + struct bridge_irq_chip_data *data = irqd->chip_data; + + bridge_clr(data->bc, b_int_enable, (1 << irqd->hwirq)); + bridge_read(data->bc, b_wid_tflush); +} + +static const struct irq_domain_ops bridge_domain_ops = { + .alloc = bridge_domain_alloc, + .free = bridge_domain_free, + .activate = bridge_domain_activate, + .deactivate = bridge_domain_deactivate +}; + /* * All observed requests have pin == 1. We could have a global here, that * gets incremented and returned every time - unfortunately, pci_map_irq @@ -336,11 +459,16 @@ static struct pci_ops bridge_pci_ops = { static int bridge_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); + struct irq_alloc_info info; int irq; irq = bc->pci_int[slot]; if (irq == -1) { - irq = request_bridge_irq(bc, slot); + info.ctrl = bc; + info.nasid = bc->nasid; + info.pin = slot; + + irq = irq_domain_alloc_irqs(bc->domain, 1, bc->nasid, &info); if (irq < 0) return irq; @@ -351,13 +479,22 @@ static int bridge_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) static int bridge_probe(struct platform_device *pdev) { + struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; struct bridge_controller *bc; struct pci_host_bridge *host; + struct irq_domain *domain; + struct fwnode_handle *fn; unsigned long offset; int slot; int err; - struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev); + + fn = irq_domain_alloc_named_fwnode("BRIDGE"); + domain = irq_domain_create_hierarchy(NULL, 0, 8, fn, + &bridge_domain_ops, NULL); + irq_domain_free_fwnode(fn); + if (!domain) + return -ENOMEM; offset = NODE_OFFSET(bd->nasid); @@ -384,6 +521,8 @@ static int bridge_probe(struct platform_device *pdev) bc->busn.end = 0xff; bc->busn.flags = IORESOURCE_BUS; + bc->domain = domain; + pci_add_resource_offset(&host->windows, &bc->mem, offset); pci_add_resource_offset(&host->windows, &bc->io, offset); pci_add_resource(&host->windows, &bc->busn); @@ -462,7 +601,9 @@ static int bridge_probe(struct platform_device *pdev) static int bridge_remove(struct platform_device *pdev) { struct pci_bus *bus = platform_get_drvdata(pdev); + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + irq_domain_remove(bc->domain); pci_lock_rescan_remove(); pci_stop_root_bus(bus); pci_remove_root_bus(bus);