diff options
Diffstat (limited to 'arch/mips/pci')
31 files changed, 1057 insertions, 170 deletions
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile index 719e4557e22..ff8a5539b36 100644 --- a/arch/mips/pci/Makefile +++ b/arch/mips/pci/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_BCM63XX) += pci-bcm63xx.o fixup-bcm63xx.o \ obj-$(CONFIG_MIPS_ALCHEMY) += pci-alchemy.o obj-$(CONFIG_SOC_AR71XX) += pci-ar71xx.o obj-$(CONFIG_PCI_AR724X) += pci-ar724x.o - +obj-$(CONFIG_MIPS_PCI_VIRTIO) += pci-virtio-guest.o # # These are still pretty much in the old state, watch, go blind. # @@ -29,6 +29,7 @@ obj-$(CONFIG_LASAT) += pci-lasat.o obj-$(CONFIG_MIPS_COBALT) += fixup-cobalt.o obj-$(CONFIG_LEMOTE_FULOONG2E) += fixup-fuloong2e.o ops-loongson2.o obj-$(CONFIG_LEMOTE_MACH2F) += fixup-lemote2f.o ops-loongson2.o +obj-$(CONFIG_LEMOTE_MACH3A) += fixup-loongson3.o ops-loongson3.o obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o pci-malta.o obj-$(CONFIG_PMC_MSP7120_GW) += fixup-pmcmsp.o ops-pmcmsp.o obj-$(CONFIG_PMC_MSP7120_EVAL) += fixup-pmcmsp.o ops-pmcmsp.o @@ -60,4 +61,5 @@ obj-$(CONFIG_CPU_XLP) += pci-xlp.o ifdef CONFIG_PCI_MSI obj-$(CONFIG_CAVIUM_OCTEON_SOC) += msi-octeon.o +obj-$(CONFIG_CPU_XLP) += msi-xlp.o endif diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c index 6c829df28dc..c2ce41ea61d 100644 --- a/arch/mips/pci/fixup-lantiq.c +++ b/arch/mips/pci/fixup-lantiq.c @@ -25,16 +25,5 @@ int pcibios_plat_dev_init(struct pci_dev *dev) int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_irq dev_irq; - int irq; - - if (of_irq_map_pci(dev, &dev_irq)) { - dev_err(&dev->dev, "trying to map irq for unknown slot:%d pin:%d\n", - slot, pin); - return 0; - } - irq = irq_create_of_mapping(dev_irq.controller, dev_irq.specifier, - dev_irq.size); - dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, irq); - return irq; + return of_irq_parse_and_map_pci(dev, slot, pin); } diff --git a/arch/mips/pci/fixup-loongson3.c b/arch/mips/pci/fixup-loongson3.c new file mode 100644 index 00000000000..d708ae46d32 --- /dev/null +++ b/arch/mips/pci/fixup-loongson3.c @@ -0,0 +1,66 @@ +/* + * fixup-loongson3.c + * + * Copyright (C) 2012 Lemote, Inc. + * Author: Xiang Yu, xiangy@lemote.com + * Chen Huacai, chenhc@lemote.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/pci.h> +#include <boot_param.h> + +static void print_fixup_info(const struct pci_dev *pdev) +{ + dev_info(&pdev->dev, "Device %x:%x, irq %d\n", + pdev->vendor, pdev->device, pdev->irq); +} + +int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + print_fixup_info(dev); + return dev->irq; +} + +static void pci_fixup_radeon(struct pci_dev *pdev) +{ + if (pdev->resource[PCI_ROM_RESOURCE].start) + return; + + if (!loongson_sysconf.vgabios_addr) + return; + + pdev->resource[PCI_ROM_RESOURCE].start = + loongson_sysconf.vgabios_addr; + pdev->resource[PCI_ROM_RESOURCE].end = + loongson_sysconf.vgabios_addr + 256*1024 - 1; + pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_COPY; + + dev_info(&pdev->dev, "BAR %d: assigned %pR for Radeon ROM\n", + PCI_ROM_RESOURCE, &pdev->resource[PCI_ROM_RESOURCE]); +} + +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID, + PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_radeon); + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-malta.c b/arch/mips/pci/fixup-malta.c index 07ada7f8441..40e920c653c 100644 --- a/arch/mips/pci/fixup-malta.c +++ b/arch/mips/pci/fixup-malta.c @@ -1,5 +1,6 @@ #include <linux/init.h> #include <linux/pci.h> +#include <asm/mips-boards/piix4.h> /* PCI interrupt pins */ #define PCIA 1 @@ -50,10 +51,26 @@ int pcibios_plat_dev_init(struct pci_dev *dev) return 0; } +static void malta_piix_func3_base_fixup(struct pci_dev *dev) +{ + /* Set a sane PM I/O base address */ + pci_write_config_word(dev, PIIX4_FUNC3_PMBA, 0x1000); + + /* Enable access to the PM I/O region */ + pci_write_config_byte(dev, PIIX4_FUNC3_PMREGMISC, + PIIX4_FUNC3_PMREGMISC_EN); +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, + malta_piix_func3_base_fixup); + static void malta_piix_func0_fixup(struct pci_dev *pdev) { unsigned char reg_val; - static int piixirqmap[16] = { /* PIIX PIRQC[A:D] irq mappings */ + u32 reg_val32; + u16 reg_val16; + /* PIIX PIRQC[A:D] irq mappings */ + static int piixirqmap[PIIX4_FUNC0_PIRQRC_IRQ_ROUTING_MAX] = { 0, 0, 0, 3, 4, 5, 6, 7, 0, 9, 10, 11, @@ -63,11 +80,12 @@ static void malta_piix_func0_fixup(struct pci_dev *pdev) /* Interrogate PIIX4 to get PCI IRQ mapping */ for (i = 0; i <= 3; i++) { - pci_read_config_byte(pdev, 0x60+i, ®_val); - if (reg_val & 0x80) + pci_read_config_byte(pdev, PIIX4_FUNC0_PIRQRC+i, ®_val); + if (reg_val & PIIX4_FUNC0_PIRQRC_IRQ_ROUTING_DISABLE) pci_irq[PCIA+i] = 0; /* Disabled */ else - pci_irq[PCIA+i] = piixirqmap[reg_val & 15]; + pci_irq[PCIA+i] = piixirqmap[reg_val & + PIIX4_FUNC0_PIRQRC_IRQ_ROUTING_MASK]; } /* Done by YAMON 2.00 onwards */ @@ -76,9 +94,25 @@ static void malta_piix_func0_fixup(struct pci_dev *pdev) * Set top of main memory accessible by ISA or DMA * devices to 16 Mb. */ - pci_read_config_byte(pdev, 0x69, ®_val); - pci_write_config_byte(pdev, 0x69, reg_val | 0xf0); + pci_read_config_byte(pdev, PIIX4_FUNC0_TOM, ®_val); + pci_write_config_byte(pdev, PIIX4_FUNC0_TOM, reg_val | + PIIX4_FUNC0_TOM_TOP_OF_MEMORY_MASK); } + + /* Mux SERIRQ to its pin */ + pci_read_config_dword(pdev, PIIX4_FUNC0_GENCFG, ®_val32); + pci_write_config_dword(pdev, PIIX4_FUNC0_GENCFG, + reg_val32 | PIIX4_FUNC0_GENCFG_SERIRQ); + + /* Enable SERIRQ */ + pci_read_config_byte(pdev, PIIX4_FUNC0_SERIRQC, ®_val); + reg_val |= PIIX4_FUNC0_SERIRQC_EN | PIIX4_FUNC0_SERIRQC_CONT; + pci_write_config_byte(pdev, PIIX4_FUNC0_SERIRQC, reg_val); + + /* Enable response to special cycles */ + pci_read_config_word(pdev, PCI_COMMAND, ®_val16); + pci_write_config_word(pdev, PCI_COMMAND, + reg_val16 | PCI_COMMAND_SPECIAL); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, @@ -93,10 +127,14 @@ static void malta_piix_func1_fixup(struct pci_dev *pdev) /* * IDE Decode enable. */ - pci_read_config_byte(pdev, 0x41, ®_val); - pci_write_config_byte(pdev, 0x41, reg_val|0x80); - pci_read_config_byte(pdev, 0x43, ®_val); - pci_write_config_byte(pdev, 0x43, reg_val|0x80); + pci_read_config_byte(pdev, PIIX4_FUNC1_IDETIM_PRIMARY_HI, + ®_val); + pci_write_config_byte(pdev, PIIX4_FUNC1_IDETIM_PRIMARY_HI, + reg_val|PIIX4_FUNC1_IDETIM_PRIMARY_HI_IDE_DECODE_EN); + pci_read_config_byte(pdev, PIIX4_FUNC1_IDETIM_SECONDARY_HI, + ®_val); + pci_write_config_byte(pdev, PIIX4_FUNC1_IDETIM_SECONDARY_HI, + reg_val|PIIX4_FUNC1_IDETIM_SECONDARY_HI_IDE_DECODE_EN); } } @@ -108,10 +146,12 @@ static void quirk_dlcsetup(struct pci_dev *dev) { u8 odlc, ndlc; - (void) pci_read_config_byte(dev, 0x82, &odlc); + (void) pci_read_config_byte(dev, PIIX4_FUNC0_DLC, &odlc); /* Enable passive releases and delayed transaction */ - ndlc = odlc | 7; - (void) pci_write_config_byte(dev, 0x82, ndlc); + ndlc = odlc | PIIX4_FUNC0_DLC_USBPR_EN | + PIIX4_FUNC0_DLC_PASSIVE_RELEASE_EN | + PIIX4_FUNC0_DLC_DELAYED_TRANSACTION_EN; + (void) pci_write_config_byte(dev, PIIX4_FUNC0_DLC, ndlc); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, diff --git a/arch/mips/pci/fixup-rc32434.c b/arch/mips/pci/fixup-rc32434.c index d0f6ecbf35f..7fcafd5da7d 100644 --- a/arch/mips/pci/fixup-rc32434.c +++ b/arch/mips/pci/fixup-rc32434.c @@ -27,7 +27,6 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/init.h> #include <asm/mach-rc32434/rc32434.h> #include <asm/mach-rc32434/irq.h> diff --git a/arch/mips/pci/fixup-sb1250.c b/arch/mips/pci/fixup-sb1250.c index 1441becdcb6..8feae9154ba 100644 --- a/arch/mips/pci/fixup-sb1250.c +++ b/arch/mips/pci/fixup-sb1250.c @@ -8,7 +8,6 @@ * 2 of the License, or (at your option) any later version. */ -#include <linux/init.h> #include <linux/pci.h> /* diff --git a/arch/mips/pci/msi-octeon.c b/arch/mips/pci/msi-octeon.c index d37be36dc65..ab0c5d14c6f 100644 --- a/arch/mips/pci/msi-octeon.c +++ b/arch/mips/pci/msi-octeon.c @@ -15,6 +15,7 @@ #include <asm/octeon/cvmx-npi-defs.h> #include <asm/octeon/cvmx-pci-defs.h> #include <asm/octeon/cvmx-npei-defs.h> +#include <asm/octeon/cvmx-sli-defs.h> #include <asm/octeon/cvmx-pexp-defs.h> #include <asm/octeon/pci-octeon.h> @@ -150,6 +151,7 @@ msi_irq_allocated: msg.address_lo = ((128ul << 20) + CVMX_PCI_MSI_RCV) & 0xffffffff; msg.address_hi = ((128ul << 20) + CVMX_PCI_MSI_RCV) >> 32; + break; case OCTEON_DMA_BAR_TYPE_BIG: /* When using big bar, Bar 0 is based at 0 */ msg.address_lo = (0 + CVMX_PCI_MSI_RCV) & 0xffffffff; @@ -161,6 +163,11 @@ msi_irq_allocated: msg.address_lo = (0 + CVMX_NPEI_PCIE_MSI_RCV) & 0xffffffff; msg.address_hi = (0 + CVMX_NPEI_PCIE_MSI_RCV) >> 32; break; + case OCTEON_DMA_BAR_TYPE_PCIE2: + /* When using PCIe2, Bar 0 is based at 0 */ + msg.address_lo = (0 + CVMX_SLI_PCIE_MSI_RCV) & 0xffffffff; + msg.address_hi = (0 + CVMX_SLI_PCIE_MSI_RCV) >> 32; + break; default: panic("arch_setup_msi_irq: Invalid octeon_dma_bar_type"); } diff --git a/arch/mips/pci/msi-xlp.c b/arch/mips/pci/msi-xlp.c new file mode 100644 index 00000000000..fa374fe3746 --- /dev/null +++ b/arch/mips/pci/msi-xlp.c @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2003-2012 Broadcom Corporation + * All Rights Reserved + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the Broadcom + * license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/msi.h> +#include <linux/mm.h> +#include <linux/irq.h> +#include <linux/irqdesc.h> +#include <linux/console.h> + +#include <asm/io.h> + +#include <asm/netlogic/interrupt.h> +#include <asm/netlogic/haldefs.h> +#include <asm/netlogic/common.h> +#include <asm/netlogic/mips-extns.h> + +#include <asm/netlogic/xlp-hal/iomap.h> +#include <asm/netlogic/xlp-hal/xlp.h> +#include <asm/netlogic/xlp-hal/pic.h> +#include <asm/netlogic/xlp-hal/pcibus.h> +#include <asm/netlogic/xlp-hal/bridge.h> + +#define XLP_MSIVEC_PER_LINK 32 +#define XLP_MSIXVEC_TOTAL (cpu_is_xlp9xx() ? 128 : 32) +#define XLP_MSIXVEC_PER_LINK (cpu_is_xlp9xx() ? 32 : 8) + +/* 128 MSI irqs per node, mapped starting at NLM_MSI_VEC_BASE */ +static inline int nlm_link_msiirq(int link, int msivec) +{ + return NLM_MSI_VEC_BASE + link * XLP_MSIVEC_PER_LINK + msivec; +} + +/* get the link MSI vector from irq number */ +static inline int nlm_irq_msivec(int irq) +{ + return (irq - NLM_MSI_VEC_BASE) % XLP_MSIVEC_PER_LINK; +} + +/* get the link from the irq number */ +static inline int nlm_irq_msilink(int irq) +{ + int total_msivec = XLP_MSIVEC_PER_LINK * PCIE_NLINKS; + + return ((irq - NLM_MSI_VEC_BASE) % total_msivec) / + XLP_MSIVEC_PER_LINK; +} + +/* + * For XLP 8xx/4xx/3xx/2xx, only 32 MSI-X vectors are possible because + * there are only 32 PIC interrupts for MSI. We split them statically + * and use 8 MSI-X vectors per link - this keeps the allocation and + * lookup simple. + * On XLP 9xx, there are 32 vectors per link, and the interrupts are + * not routed thru PIC, so we can use all 128 MSI-X vectors. + */ +static inline int nlm_link_msixirq(int link, int bit) +{ + return NLM_MSIX_VEC_BASE + link * XLP_MSIXVEC_PER_LINK + bit; +} + +/* get the link MSI vector from irq number */ +static inline int nlm_irq_msixvec(int irq) +{ + return (irq - NLM_MSIX_VEC_BASE) % XLP_MSIXVEC_TOTAL; +} + +/* get the link from MSIX vec */ +static inline int nlm_irq_msixlink(int msixvec) +{ + return msixvec / XLP_MSIXVEC_PER_LINK; +} + +/* + * Per link MSI and MSI-X information, set as IRQ handler data for + * MSI and MSI-X interrupts. + */ +struct xlp_msi_data { + struct nlm_soc_info *node; + uint64_t lnkbase; + uint32_t msi_enabled_mask; + uint32_t msi_alloc_mask; + uint32_t msix_alloc_mask; + spinlock_t msi_lock; +}; + +/* + * MSI Chip definitions + * + * On XLP, there is a PIC interrupt associated with each PCIe link on the + * chip (which appears as a PCI bridge to us). This gives us 32 MSI irqa + * per link and 128 overall. + * + * When a device connected to the link raises a MSI interrupt, we get a + * link interrupt and we then have to look at PCIE_MSI_STATUS register at + * the bridge to map it to the IRQ + */ +static void xlp_msi_enable(struct irq_data *d) +{ + struct xlp_msi_data *md = irq_data_get_irq_handler_data(d); + unsigned long flags; + int vec; + + vec = nlm_irq_msivec(d->irq); + spin_lock_irqsave(&md->msi_lock, flags); + md->msi_enabled_mask |= 1u << vec; + if (cpu_is_xlp9xx()) + nlm_write_reg(md->lnkbase, PCIE_9XX_MSI_EN, + md->msi_enabled_mask); + else + nlm_write_reg(md->lnkbase, PCIE_MSI_EN, md->msi_enabled_mask); + spin_unlock_irqrestore(&md->msi_lock, flags); +} + +static void xlp_msi_disable(struct irq_data *d) +{ + struct xlp_msi_data *md = irq_data_get_irq_handler_data(d); + unsigned long flags; + int vec; + + vec = nlm_irq_msivec(d->irq); + spin_lock_irqsave(&md->msi_lock, flags); + md->msi_enabled_mask &= ~(1u << vec); + if (cpu_is_xlp9xx()) + nlm_write_reg(md->lnkbase, PCIE_9XX_MSI_EN, + md->msi_enabled_mask); + else + nlm_write_reg(md->lnkbase, PCIE_MSI_EN, md->msi_enabled_mask); + spin_unlock_irqrestore(&md->msi_lock, flags); +} + +static void xlp_msi_mask_ack(struct irq_data *d) +{ + struct xlp_msi_data *md = irq_data_get_irq_handler_data(d); + int link, vec; + + link = nlm_irq_msilink(d->irq); + vec = nlm_irq_msivec(d->irq); + xlp_msi_disable(d); + + /* Ack MSI on bridge */ + if (cpu_is_xlp9xx()) + nlm_write_reg(md->lnkbase, PCIE_9XX_MSI_STATUS, 1u << vec); + else + nlm_write_reg(md->lnkbase, PCIE_MSI_STATUS, 1u << vec); + + /* Ack at eirr and PIC */ + ack_c0_eirr(PIC_PCIE_LINK_MSI_IRQ(link)); + if (cpu_is_xlp9xx()) + nlm_pic_ack(md->node->picbase, + PIC_9XX_IRT_PCIE_LINK_INDEX(link)); + else + nlm_pic_ack(md->node->picbase, PIC_IRT_PCIE_LINK_INDEX(link)); +} + +static struct irq_chip xlp_msi_chip = { + .name = "XLP-MSI", + .irq_enable = xlp_msi_enable, + .irq_disable = xlp_msi_disable, + .irq_mask_ack = xlp_msi_mask_ack, + .irq_unmask = xlp_msi_enable, +}; + +/* + * XLP8XX/4XX/3XX/2XX: + * The MSI-X interrupt handling is different from MSI, there are 32 MSI-X + * interrupts generated by the PIC and each of these correspond to a MSI-X + * vector (0-31) that can be assigned. + * + * We divide the MSI-X vectors to 8 per link and do a per-link allocation + * + * XLP9XX: + * 32 MSI-X vectors are available per link, and the interrupts are not routed + * thru the PIC. PIC ack not needed. + * + * Enable and disable done using standard MSI functions. + */ +static void xlp_msix_mask_ack(struct irq_data *d) +{ + struct xlp_msi_data *md; + int link, msixvec; + uint32_t status_reg, bit; + + msixvec = nlm_irq_msixvec(d->irq); + link = nlm_irq_msixlink(msixvec); + mask_msi_irq(d); + md = irq_data_get_irq_handler_data(d); + + /* Ack MSI on bridge */ + if (cpu_is_xlp9xx()) { + status_reg = PCIE_9XX_MSIX_STATUSX(link); + bit = msixvec % XLP_MSIXVEC_PER_LINK; + } else { + status_reg = PCIE_MSIX_STATUS; + bit = msixvec; + } + nlm_write_reg(md->lnkbase, status_reg, 1u << bit); + + /* Ack at eirr and PIC */ + ack_c0_eirr(PIC_PCIE_MSIX_IRQ(link)); + if (!cpu_is_xlp9xx()) + nlm_pic_ack(md->node->picbase, + PIC_IRT_PCIE_MSIX_INDEX(msixvec)); +} + +static struct irq_chip xlp_msix_chip = { + .name = "XLP-MSIX", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask_ack = xlp_msix_mask_ack, + .irq_unmask = unmask_msi_irq, +}; + +void arch_teardown_msi_irq(unsigned int irq) +{ +} + +/* + * Setup a PCIe link for MSI. By default, the links are in + * legacy interrupt mode. We will switch them to MSI mode + * at the first MSI request. + */ +static void xlp_config_link_msi(uint64_t lnkbase, int lirq, uint64_t msiaddr) +{ + u32 val; + + if (cpu_is_xlp9xx()) { + val = nlm_read_reg(lnkbase, PCIE_9XX_INT_EN0); + if ((val & 0x200) == 0) { + val |= 0x200; /* MSI Interrupt enable */ + nlm_write_reg(lnkbase, PCIE_9XX_INT_EN0, val); + } + } else { + val = nlm_read_reg(lnkbase, PCIE_INT_EN0); + if ((val & 0x200) == 0) { + val |= 0x200; + nlm_write_reg(lnkbase, PCIE_INT_EN0, val); + } + } + + val = nlm_read_reg(lnkbase, 0x1); /* CMD */ + if ((val & 0x0400) == 0) { + val |= 0x0400; + nlm_write_reg(lnkbase, 0x1, val); + } + + /* Update IRQ in the PCI irq reg */ + val = nlm_read_pci_reg(lnkbase, 0xf); + val &= ~0x1fu; + val |= (1 << 8) | lirq; + nlm_write_pci_reg(lnkbase, 0xf, val); + + /* MSI addr */ + nlm_write_reg(lnkbase, PCIE_BRIDGE_MSI_ADDRH, msiaddr >> 32); + nlm_write_reg(lnkbase, PCIE_BRIDGE_MSI_ADDRL, msiaddr & 0xffffffff); + + /* MSI cap for bridge */ + val = nlm_read_reg(lnkbase, PCIE_BRIDGE_MSI_CAP); + if ((val & (1 << 16)) == 0) { + val |= 0xb << 16; /* mmc32, msi enable */ + nlm_write_reg(lnkbase, PCIE_BRIDGE_MSI_CAP, val); + } +} + +/* + * Allocate a MSI vector on a link + */ +static int xlp_setup_msi(uint64_t lnkbase, int node, int link, + struct msi_desc *desc) +{ + struct xlp_msi_data *md; + struct msi_msg msg; + unsigned long flags; + int msivec, irt, lirq, xirq, ret; + uint64_t msiaddr; + + /* Get MSI data for the link */ + lirq = PIC_PCIE_LINK_MSI_IRQ(link); + xirq = nlm_irq_to_xirq(node, nlm_link_msiirq(link, 0)); + md = irq_get_handler_data(xirq); + msiaddr = MSI_LINK_ADDR(node, link); + + spin_lock_irqsave(&md->msi_lock, flags); + if (md->msi_alloc_mask == 0) { + xlp_config_link_msi(lnkbase, lirq, msiaddr); + /* switch the link IRQ to MSI range */ + if (cpu_is_xlp9xx()) + irt = PIC_9XX_IRT_PCIE_LINK_INDEX(link); + else + irt = PIC_IRT_PCIE_LINK_INDEX(link); + nlm_setup_pic_irq(node, lirq, lirq, irt); + nlm_pic_init_irt(nlm_get_node(node)->picbase, irt, lirq, + node * nlm_threads_per_node(), 1 /*en */); + } + + /* allocate a MSI vec, and tell the bridge about it */ + msivec = fls(md->msi_alloc_mask); + if (msivec == XLP_MSIVEC_PER_LINK) { + spin_unlock_irqrestore(&md->msi_lock, flags); + return -ENOMEM; + } + md->msi_alloc_mask |= (1u << msivec); + spin_unlock_irqrestore(&md->msi_lock, flags); + + msg.address_hi = msiaddr >> 32; + msg.address_lo = msiaddr & 0xffffffff; + msg.data = 0xc00 | msivec; + + xirq = xirq + msivec; /* msi mapped to global irq space */ + ret = irq_set_msi_desc(xirq, desc); + if (ret < 0) + return ret; + + write_msi_msg(xirq, &msg); + return 0; +} + +/* + * Switch a link to MSI-X mode + */ +static void xlp_config_link_msix(uint64_t lnkbase, int lirq, uint64_t msixaddr) +{ + u32 val; + + val = nlm_read_reg(lnkbase, 0x2C); + if ((val & 0x80000000U) == 0) { + val |= 0x80000000U; + nlm_write_reg(lnkbase, 0x2C, val); + } + + if (cpu_is_xlp9xx()) { + val = nlm_read_reg(lnkbase, PCIE_9XX_INT_EN0); + if ((val & 0x200) == 0) { + val |= 0x200; /* MSI Interrupt enable */ + nlm_write_reg(lnkbase, PCIE_9XX_INT_EN0, val); + } + } else { + val = nlm_read_reg(lnkbase, PCIE_INT_EN0); + if ((val & 0x200) == 0) { + val |= 0x200; /* MSI Interrupt enable */ + nlm_write_reg(lnkbase, PCIE_INT_EN0, val); + } + } + + val = nlm_read_reg(lnkbase, 0x1); /* CMD */ + if ((val & 0x0400) == 0) { + val |= 0x0400; + nlm_write_reg(lnkbase, 0x1, val); + } + + /* Update IRQ in the PCI irq reg */ + val = nlm_read_pci_reg(lnkbase, 0xf); + val &= ~0x1fu; + val |= (1 << 8) | lirq; + nlm_write_pci_reg(lnkbase, 0xf, val); + + if (cpu_is_xlp9xx()) { + /* MSI-X addresses */ + nlm_write_reg(lnkbase, PCIE_9XX_BRIDGE_MSIX_ADDR_BASE, + msixaddr >> 8); + nlm_write_reg(lnkbase, PCIE_9XX_BRIDGE_MSIX_ADDR_LIMIT, + (msixaddr + MSI_ADDR_SZ) >> 8); + } else { + /* MSI-X addresses */ + nlm_write_reg(lnkbase, PCIE_BRIDGE_MSIX_ADDR_BASE, + msixaddr >> 8); + nlm_write_reg(lnkbase, PCIE_BRIDGE_MSIX_ADDR_LIMIT, + (msixaddr + MSI_ADDR_SZ) >> 8); + } +} + +/* + * Allocate a MSI-X vector + */ +static int xlp_setup_msix(uint64_t lnkbase, int node, int link, + struct msi_desc *desc) +{ + struct xlp_msi_data *md; + struct msi_msg msg; + unsigned long flags; + int t, msixvec, lirq, xirq, ret; + uint64_t msixaddr; + + /* Get MSI data for the link */ + lirq = PIC_PCIE_MSIX_IRQ(link); + xirq = nlm_irq_to_xirq(node, nlm_link_msixirq(link, 0)); + md = irq_get_handler_data(xirq); + msixaddr = MSIX_LINK_ADDR(node, link); + + spin_lock_irqsave(&md->msi_lock, flags); + /* switch the PCIe link to MSI-X mode at the first alloc */ + if (md->msix_alloc_mask == 0) + xlp_config_link_msix(lnkbase, lirq, msixaddr); + + /* allocate a MSI-X vec, and tell the bridge about it */ + t = fls(md->msix_alloc_mask); + if (t == XLP_MSIXVEC_PER_LINK) { + spin_unlock_irqrestore(&md->msi_lock, flags); + return -ENOMEM; + } + md->msix_alloc_mask |= (1u << t); + spin_unlock_irqrestore(&md->msi_lock, flags); + + xirq += t; + msixvec = nlm_irq_msixvec(xirq); + + msg.address_hi = msixaddr >> 32; + msg.address_lo = msixaddr & 0xffffffff; + msg.data = 0xc00 | msixvec; + + ret = irq_set_msi_desc(xirq, desc); + if (ret < 0) { + destroy_irq(xirq); + return ret; + } + + write_msi_msg(xirq, &msg); + return 0; +} + +int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) +{ + struct pci_dev *lnkdev; + uint64_t lnkbase; + int node, link, slot; + + lnkdev = xlp_get_pcie_link(dev); + if (lnkdev == NULL) { + dev_err(&dev->dev, "Could not find bridge\n"); + return 1; + } + slot = PCI_SLOT(lnkdev->devfn); + link = PCI_FUNC(lnkdev->devfn); + node = slot / 8; + lnkbase = nlm_get_pcie_base(node, link); + + if (desc->msi_attrib.is_msix) + return xlp_setup_msix(lnkbase, node, link, desc); + else + return xlp_setup_msi(lnkbase, node, link, desc); +} + +void __init xlp_init_node_msi_irqs(int node, int link) +{ + struct nlm_soc_info *nodep; + struct xlp_msi_data *md; + int irq, i, irt, msixvec, val; + + pr_info("[%d %d] Init node PCI IRT\n", node, link); + nodep = nlm_get_node(node); + + /* Alloc an MSI block for the link */ + md = kzalloc(sizeof(*md), GFP_KERNEL); + spin_lock_init(&md->msi_lock); + md->msi_enabled_mask = 0; + md->msi_alloc_mask = 0; + md->msix_alloc_mask = 0; + md->node = nodep; + md->lnkbase = nlm_get_pcie_base(node, link); + + /* extended space for MSI interrupts */ + irq = nlm_irq_to_xirq(node, nlm_link_msiirq(link, 0)); + for (i = irq; i < irq + XLP_MSIVEC_PER_LINK; i++) { + irq_set_chip_and_handler(i, &xlp_msi_chip, handle_level_irq); + irq_set_handler_data(i, md); + } + + for (i = 0; i < XLP_MSIXVEC_PER_LINK ; i++) { + if (cpu_is_xlp9xx()) { + val = ((node * nlm_threads_per_node()) << 7 | + PIC_PCIE_MSIX_IRQ(link) << 1 | 0 << 0); + nlm_write_pcie_reg(md->lnkbase, PCIE_9XX_MSIX_VECX(i + + (link * XLP_MSIXVEC_PER_LINK)), val); + } else { + /* Initialize MSI-X irts to generate one interrupt + * per link + */ + msixvec = link * XLP_MSIXVEC_PER_LINK + i; + irt = PIC_IRT_PCIE_MSIX_INDEX(msixvec); + nlm_pic_init_irt(nodep->picbase, irt, + PIC_PCIE_MSIX_IRQ(link), + node * nlm_threads_per_node(), 1); + } + + /* Initialize MSI-X extended irq space for the link */ + irq = nlm_irq_to_xirq(node, nlm_link_msixirq(link, i)); + irq_set_chip_and_handler(irq, &xlp_msix_chip, handle_level_irq); + irq_set_handler_data(irq, md); + } +} + +void nlm_dispatch_msi(int node, int lirq) +{ + struct xlp_msi_data *md; + int link, i, irqbase; + u32 status; + + link = lirq - PIC_PCIE_LINK_MSI_IRQ_BASE; + irqbase = nlm_irq_to_xirq(node, nlm_link_msiirq(link, 0)); + md = irq_get_handler_data(irqbase); + if (cpu_is_xlp9xx()) + status = nlm_read_reg(md->lnkbase, PCIE_9XX_MSI_STATUS) & + md->msi_enabled_mask; + else + status = nlm_read_reg(md->lnkbase, PCIE_MSI_STATUS) & + md->msi_enabled_mask; + while (status) { + i = __ffs(status); + do_IRQ(irqbase + i); + status &= status - 1; + } +} + +void nlm_dispatch_msix(int node, int lirq) +{ + struct xlp_msi_data *md; + int link, i, irqbase; + u32 status; + + link = lirq - PIC_PCIE_MSIX_IRQ_BASE; + irqbase = nlm_irq_to_xirq(node, nlm_link_msixirq(link, 0)); + md = irq_get_handler_data(irqbase); + if (cpu_is_xlp9xx()) + status = nlm_read_reg(md->lnkbase, PCIE_9XX_MSIX_STATUSX(link)); + else + status = nlm_read_reg(md->lnkbase, PCIE_MSIX_STATUS); + + /* narrow it down to the MSI-x vectors for our link */ + if (!cpu_is_xlp9xx()) + status = (status >> (link * XLP_MSIXVEC_PER_LINK)) & + ((1 << XLP_MSIXVEC_PER_LINK) - 1); + + while (status) { + i = __ffs(status); + do_IRQ(irqbase + i); + status &= status - 1; + } +} diff --git a/arch/mips/pci/ops-bcm63xx.c b/arch/mips/pci/ops-bcm63xx.c index 6144bb337e4..13eea696bbe 100644 --- a/arch/mips/pci/ops-bcm63xx.c +++ b/arch/mips/pci/ops-bcm63xx.c @@ -9,7 +9,6 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/io.h> diff --git a/arch/mips/pci/ops-bonito64.c b/arch/mips/pci/ops-bonito64.c index 830352e3aed..c06205a8734 100644 --- a/arch/mips/pci/ops-bonito64.c +++ b/arch/mips/pci/ops-bonito64.c @@ -22,7 +22,6 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/init.h> #include <asm/mips-boards/bonito64.h> diff --git a/arch/mips/pci/ops-lantiq.c b/arch/mips/pci/ops-lantiq.c index 16e7c2526d7..e5738ee26f4 100644 --- a/arch/mips/pci/ops-lantiq.c +++ b/arch/mips/pci/ops-lantiq.c @@ -9,7 +9,6 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/mm.h> #include <asm/addrspace.h> diff --git a/arch/mips/pci/ops-loongson2.c b/arch/mips/pci/ops-loongson2.c index 98254afa028..24138bb0cbe 100644 --- a/arch/mips/pci/ops-loongson2.c +++ b/arch/mips/pci/ops-loongson2.c @@ -14,7 +14,6 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/export.h> #include <loongson.h> diff --git a/arch/mips/pci/ops-loongson3.c b/arch/mips/pci/ops-loongson3.c new file mode 100644 index 00000000000..46ed541a3ec --- /dev/null +++ b/arch/mips/pci/ops-loongson3.c @@ -0,0 +1,101 @@ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> + +#include <asm/mips-boards/bonito64.h> + +#include <loongson.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +#define HT1LO_PCICFG_BASE 0x1a000000 +#define HT1LO_PCICFG_BASE_TP1 0x1b000000 + +static int loongson3_pci_config_access(unsigned char access_type, + struct pci_bus *bus, unsigned int devfn, + int where, u32 *data) +{ + unsigned char busnum = bus->number; + u_int64_t addr, type; + void *addrp; + int device = PCI_SLOT(devfn); + int function = PCI_FUNC(devfn); + int reg = where & ~3; + + addr = (busnum << 16) | (device << 11) | (function << 8) | reg; + if (busnum == 0) { + if (device > 31) + return PCIBIOS_DEVICE_NOT_FOUND; + addrp = (void *)(TO_UNCAC(HT1LO_PCICFG_BASE) | (addr & 0xffff)); + type = 0; + + } else { + addrp = (void *)(TO_UNCAC(HT1LO_PCICFG_BASE_TP1) | (addr)); + type = 0x10000; + } + + if (access_type == PCI_ACCESS_WRITE) + writel(*data, addrp); + else { + *data = readl(addrp); + if (*data == 0xffffffff) { + *data = -1; + return PCIBIOS_DEVICE_NOT_FOUND; + } + } + return PCIBIOS_SUCCESSFUL; +} + +static int loongson3_pci_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + u32 data = 0; + int ret = loongson3_pci_config_access(PCI_ACCESS_READ, + bus, devfn, where, &data); + + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int loongson3_pci_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 data = 0; + int ret; + + if (size == 4) + data = val; + else { + ret = loongson3_pci_config_access(PCI_ACCESS_READ, + bus, devfn, where, &data); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + } + + ret = loongson3_pci_config_access(PCI_ACCESS_WRITE, + bus, devfn, where, &data); + + return ret; +} + +struct pci_ops loongson_pci_ops = { + .read = loongson3_pci_pcibios_read, + .write = loongson3_pci_pcibios_write +}; diff --git a/arch/mips/pci/ops-mace.c b/arch/mips/pci/ops-mace.c index 1cfb5588699..6b5821febc3 100644 --- a/arch/mips/pci/ops-mace.c +++ b/arch/mips/pci/ops-mace.c @@ -6,7 +6,6 @@ * Copyright (C) 2000, 2001 Keith M Wesolowski */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/types.h> #include <asm/pci.h> diff --git a/arch/mips/pci/ops-msc.c b/arch/mips/pci/ops-msc.c index 92a8543361b..dbbf3657896 100644 --- a/arch/mips/pci/ops-msc.c +++ b/arch/mips/pci/ops-msc.c @@ -24,7 +24,6 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/init.h> #include <asm/mips-boards/msc01_pci.h> diff --git a/arch/mips/pci/ops-nile4.c b/arch/mips/pci/ops-nile4.c index 499e35c3eb3..a1a7c9f4096 100644 --- a/arch/mips/pci/ops-nile4.c +++ b/arch/mips/pci/ops-nile4.c @@ -1,5 +1,4 @@ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/pci.h> #include <asm/bootinfo.h> diff --git a/arch/mips/pci/ops-pmcmsp.c b/arch/mips/pci/ops-pmcmsp.c index 3d27800edba..50034f985be 100644 --- a/arch/mips/pci/ops-pmcmsp.c +++ b/arch/mips/pci/ops-pmcmsp.c @@ -7,7 +7,7 @@ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * * Much of the code is derived from the original DDB5074 port by - * Geert Uytterhoeven <geert@sonycom.com> + * Geert Uytterhoeven <geert@linux-m68k.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/arch/mips/pci/ops-rc32434.c b/arch/mips/pci/ops-rc32434.c index 7c7182e2350..874ed6df976 100644 --- a/arch/mips/pci/ops-rc32434.c +++ b/arch/mips/pci/ops-rc32434.c @@ -26,7 +26,6 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/delay.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/pci.h> #include <linux/types.h> diff --git a/arch/mips/pci/ops-tx3927.c b/arch/mips/pci/ops-tx3927.c index 02d64f77e96..d35dc9c9ab9 100644 --- a/arch/mips/pci/ops-tx3927.c +++ b/arch/mips/pci/ops-tx3927.c @@ -11,7 +11,7 @@ * Define the pci_ops for TX3927. * * Much of the code is derived from the original DDB5074 port by - * Geert Uytterhoeven <geert@sonycom.com> + * Geert Uytterhoeven <geert@linux-m68k.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/arch/mips/pci/ops-tx4927.c b/arch/mips/pci/ops-tx4927.c index 3d5df514d02..0e046d82e4e 100644 --- a/arch/mips/pci/ops-tx4927.c +++ b/arch/mips/pci/ops-tx4927.c @@ -202,17 +202,20 @@ char *tx4927_pcibios_setup(char *str) unsigned long val; if (!strncmp(str, "trdyto=", 7)) { - if (strict_strtoul(str + 7, 0, &val) == 0) + u8 val = 0; + if (kstrtou8(str + 7, 0, &val) == 0) tx4927_pci_opts.trdyto = val; return NULL; } if (!strncmp(str, "retryto=", 8)) { - if (strict_strtoul(str + 8, 0, &val) == 0) + u8 val = 0; + if (kstrtou8(str + 8, 0, &val) == 0) tx4927_pci_opts.retryto = val; return NULL; } if (!strncmp(str, "gbwc=", 5)) { - if (strict_strtoul(str + 5, 0, &val) == 0) + u16 val; + if (kstrtou16(str + 5, 0, &val) == 0) tx4927_pci_opts.gbwc = val; return NULL; } diff --git a/arch/mips/pci/pci-alchemy.c b/arch/mips/pci/pci-alchemy.c index d1faece21b6..563d1f61d6e 100644 --- a/arch/mips/pci/pci-alchemy.c +++ b/arch/mips/pci/pci-alchemy.c @@ -16,6 +16,7 @@ #include <linux/syscore_ops.h> #include <linux/vmalloc.h> +#include <asm/dma-coherence.h> #include <asm/mach-au1x00/au1000.h> #include <asm/tlbmisc.h> @@ -411,17 +412,15 @@ static int alchemy_pci_probe(struct platform_device *pdev) } ctx->alchemy_pci_ctrl.io_map_base = (unsigned long)virt_io; -#ifdef CONFIG_DMA_NONCOHERENT /* Au1500 revisions older than AD have borked coherent PCI */ if ((alchemy_get_cputype() == ALCHEMY_CPU_AU1500) && - (read_c0_prid() < 0x01030202)) { + (read_c0_prid() < 0x01030202) && !coherentio) { val = __raw_readl(ctx->regs + PCI_REG_CONFIG); val |= PCI_CONFIG_NC; __raw_writel(val, ctx->regs + PCI_REG_CONFIG); wmb(); dev_info(&pdev->dev, "non-coherent PCI on Au1500 AA/AB/AC\n"); } -#endif if (pd->board_map_irq) ctx->board_map_irq = pd->board_map_irq; diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index 18517dd0f70..d471a26dd5f 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -363,9 +363,6 @@ static int ar71xx_pci_probe(struct platform_device *pdev) spin_lock_init(&apc->lock); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); - if (!res) - return -EINVAL; - apc->cfg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(apc->cfg_base)) return PTR_ERR(apc->cfg_base); diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index 65ec032fa0b..785b2659b51 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -362,25 +362,16 @@ static int ar724x_pci_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl_base"); - if (!res) - return -EINVAL; - apc->ctrl_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(apc->ctrl_base)) return PTR_ERR(apc->ctrl_base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); - if (!res) - return -EINVAL; - apc->devcfg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(apc->devcfg_base)) return PTR_ERR(apc->devcfg_base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crp_base"); - if (!res) - return -EINVAL; - apc->crp_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(apc->crp_base)) return PTR_ERR(apc->crp_base); diff --git a/arch/mips/pci/pci-ip27.c b/arch/mips/pci/pci-ip27.c index 162b4cb29db..0f09eafa5e3 100644 --- a/arch/mips/pci/pci-ip27.c +++ b/arch/mips/pci/pci-ip27.c @@ -7,7 +7,6 @@ * Copyright (C) 1999, 2000, 04 Ralf Baechle (ralf@linux-mips.org) * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/export.h> #include <linux/pci.h> diff --git a/arch/mips/pci/pci-malta.c b/arch/mips/pci/pci-malta.c index 37134ddfeaa..cfbbc3e3e91 100644 --- a/arch/mips/pci/pci-malta.c +++ b/arch/mips/pci/pci-malta.c @@ -27,7 +27,7 @@ #include <linux/init.h> #include <asm/gt64120.h> -#include <asm/gcmpregs.h> +#include <asm/mips-cm.h> #include <asm/mips-boards/generic.h> #include <asm/mips-boards/bonito64.h> #include <asm/mips-boards/msc01_pci.h> @@ -201,11 +201,11 @@ void __init mips_pcibios_init(void) msc_mem_resource.start = start & mask; msc_mem_resource.end = (start & mask) | ~mask; msc_controller.mem_offset = (start & mask) - (map & mask); -#ifdef CONFIG_MIPS_CMP - if (gcmp_niocu()) - gcmp_setregion(0, start, mask, - GCMP_GCB_GCMPB_CMDEFTGT_IOCU1); -#endif + if (mips_cm_numiocu()) { + write_gcr_reg0_base(start); + write_gcr_reg0_mask(mask | + CM_GCR_REGn_MASK_CMTGT_IOCU0); + } MSC_READ(MSC01_PCI_SC2PIOBASL, start); MSC_READ(MSC01_PCI_SC2PIOMSKL, mask); MSC_READ(MSC01_PCI_SC2PIOMAPL, map); @@ -213,11 +213,11 @@ void __init mips_pcibios_init(void) msc_io_resource.end = (map & mask) | ~mask; msc_controller.io_offset = 0; ioport_resource.end = ~mask; -#ifdef CONFIG_MIPS_CMP - if (gcmp_niocu()) - gcmp_setregion(1, start, mask, - GCMP_GCB_GCMPB_CMDEFTGT_IOCU1); -#endif + if (mips_cm_numiocu()) { + write_gcr_reg1_base(start); + write_gcr_reg1_mask(mask | + CM_GCR_REGn_MASK_CMTGT_IOCU0); + } /* If ranges overlap I/O takes precedence. */ start = start & mask; end = start | ~mask; @@ -241,9 +241,9 @@ void __init mips_pcibios_init(void) return; } - /* Change start address to avoid conflicts with ACPI and SMB devices */ - if (controller->io_resource->start < 0x00002000UL) - controller->io_resource->start = 0x00002000UL; + /* PIIX4 ACPI starts at 0x1000 */ + if (controller->io_resource->start < 0x00001000UL) + controller->io_resource->start = 0x00001000UL; iomem_resource.end &= 0xfffffffffULL; /* 64 GB */ ioport_resource.end = controller->io_resource->end; diff --git a/arch/mips/pci/pci-rc32434.c b/arch/mips/pci/pci-rc32434.c index b128cb973eb..7f6ce6d734c 100644 --- a/arch/mips/pci/pci-rc32434.c +++ b/arch/mips/pci/pci-rc32434.c @@ -53,7 +53,6 @@ static struct resource rc32434_res_pci_mem1 = { .start = 0x50000000, .end = 0x5FFFFFFF, .flags = IORESOURCE_MEM, - .parent = &rc32434_res_pci_mem1, .sibling = NULL, .child = &rc32434_res_pci_mem2 }; diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c index 95c9d41382e..72919aeef42 100644 --- a/arch/mips/pci/pci-rt3883.c +++ b/arch/mips/pci/pci-rt3883.c @@ -436,9 +436,6 @@ static int rt3883_pci_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - rpc->base = devm_ioremap_resource(dev, res); if (IS_ERR(rpc->base)) return PTR_ERR(rpc->base); @@ -583,29 +580,7 @@ err_put_intc_node: int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_irq dev_irq; - int err; - int irq; - - err = of_irq_map_pci(dev, &dev_irq); - if (err) { - pr_err("pci %s: unable to get irq map, err=%d\n", - pci_name((struct pci_dev *) dev), err); - return 0; - } - - irq = irq_create_of_mapping(dev_irq.controller, - dev_irq.specifier, - dev_irq.size); - - if (irq == 0) - pr_crit("pci %s: no irq found for pin %u\n", - pci_name((struct pci_dev *) dev), pin); - else - pr_info("pci %s: using irq %d for pin %u\n", - pci_name((struct pci_dev *) dev), irq, pin); - - return irq; + return of_irq_parse_and_map_pci(dev, slot, pin); } int pcibios_plat_dev_init(struct pci_dev *dev) diff --git a/arch/mips/pci/pci-virtio-guest.c b/arch/mips/pci/pci-virtio-guest.c new file mode 100644 index 00000000000..40a078bc461 --- /dev/null +++ b/arch/mips/pci/pci-virtio-guest.c @@ -0,0 +1,131 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2013 Cavium, Inc. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> + +#include <uapi/asm/bitfield.h> +#include <asm/byteorder.h> +#include <asm/io.h> + +#define PCI_CONFIG_ADDRESS 0xcf8 +#define PCI_CONFIG_DATA 0xcfc + +union pci_config_address { + struct { + __BITFIELD_FIELD(unsigned enable_bit : 1, /* 31 */ + __BITFIELD_FIELD(unsigned reserved : 7, /* 30 .. 24 */ + __BITFIELD_FIELD(unsigned bus_number : 8, /* 23 .. 16 */ + __BITFIELD_FIELD(unsigned devfn_number : 8, /* 15 .. 8 */ + __BITFIELD_FIELD(unsigned register_number : 8, /* 7 .. 0 */ + ))))); + }; + u32 w; +}; + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + return ((pin + slot) % 4)+ MIPS_IRQ_PCIA; +} + +static void pci_virtio_guest_write_config_addr(struct pci_bus *bus, + unsigned int devfn, int reg) +{ + union pci_config_address pca = { .w = 0 }; + + pca.register_number = reg; + pca.devfn_number = devfn; + pca.bus_number = bus->number; + pca.enable_bit = 1; + + outl(pca.w, PCI_CONFIG_ADDRESS); +} + +static int pci_virtio_guest_write_config(struct pci_bus *bus, + unsigned int devfn, int reg, int size, u32 val) +{ + pci_virtio_guest_write_config_addr(bus, devfn, reg); + + switch (size) { + case 1: + outb(val, PCI_CONFIG_DATA + (reg & 3)); + break; + case 2: + outw(val, PCI_CONFIG_DATA + (reg & 2)); + break; + case 4: + outl(val, PCI_CONFIG_DATA); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_virtio_guest_read_config(struct pci_bus *bus, unsigned int devfn, + int reg, int size, u32 *val) +{ + pci_virtio_guest_write_config_addr(bus, devfn, reg); + + switch (size) { + case 1: + *val = inb(PCI_CONFIG_DATA + (reg & 3)); + break; + case 2: + *val = inw(PCI_CONFIG_DATA + (reg & 2)); + break; + case 4: + *val = inl(PCI_CONFIG_DATA); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops pci_virtio_guest_ops = { + .read = pci_virtio_guest_read_config, + .write = pci_virtio_guest_write_config, +}; + +static struct resource pci_virtio_guest_mem_resource = { + .name = "Virtio MEM", + .flags = IORESOURCE_MEM, + .start = 0x10000000, + .end = 0x1dffffff +}; + +static struct resource pci_virtio_guest_io_resource = { + .name = "Virtio IO", + .flags = IORESOURCE_IO, + .start = 0, + .end = 0xffff +}; + +static struct pci_controller pci_virtio_guest_controller = { + .pci_ops = &pci_virtio_guest_ops, + .mem_resource = &pci_virtio_guest_mem_resource, + .io_resource = &pci_virtio_guest_io_resource, +}; + +static int __init pci_virtio_guest_setup(void) +{ + pr_err("pci_virtio_guest_setup\n"); + + /* Virtio comes pre-assigned */ + pci_set_flags(PCI_PROBE_ONLY); + + pci_virtio_guest_controller.io_map_base = mips_io_port_base; + register_pci_controller(&pci_virtio_guest_controller); + return 0; +} +arch_initcall(pci_virtio_guest_setup); diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index 653d2db9e0c..7babf01600c 100644 --- a/arch/mips/pci/pci-xlp.c +++ b/arch/mips/pci/pci-xlp.c @@ -47,10 +47,11 @@ #include <asm/netlogic/interrupt.h> #include <asm/netlogic/haldefs.h> #include <asm/netlogic/common.h> +#include <asm/netlogic/mips-extns.h> #include <asm/netlogic/xlp-hal/iomap.h> -#include <asm/netlogic/xlp-hal/pic.h> #include <asm/netlogic/xlp-hal/xlp.h> +#include <asm/netlogic/xlp-hal/pic.h> #include <asm/netlogic/xlp-hal/pcibus.h> #include <asm/netlogic/xlp-hal/bridge.h> @@ -66,9 +67,22 @@ static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn, u32 *cfgaddr; where &= ~3; - if (bus->number == 0 && PCI_SLOT(devfn) == 1 && where == 0x954) + if (cpu_is_xlp9xx()) { + /* be very careful on SoC buses */ + if (bus->number == 0) { + /* Scan only existing nodes - uboot bug? */ + if (PCI_SLOT(devfn) != 0 || + !nlm_node_present(PCI_FUNC(devfn))) + return 0xffffffff; + } else if (bus->parent->number == 0) { /* SoC bus */ + if (PCI_SLOT(devfn) == 0) /* b.0.0 hangs */ + return 0xffffffff; + if (devfn == 44) /* b.5.4 hangs */ + return 0xffffffff; + } + } else if (bus->number == 0 && PCI_SLOT(devfn) == 1 && where == 0x954) { return 0xffffffff; - + } cfgaddr = (u32 *)(pci_config_base + pci_cfg_addr(bus->number, devfn, where)); data = *cfgaddr; @@ -162,27 +176,39 @@ struct pci_controller nlm_pci_controller = { .io_offset = 0x00000000UL, }; -static struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev) +struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev) { struct pci_bus *bus, *p; - /* Find the bridge on bus 0 */ bus = dev->bus; - for (p = bus->parent; p && p->number != 0; p = p->parent) - bus = p; - return p ? bus->self : NULL; + if (cpu_is_xlp9xx()) { + /* find bus with grand parent number == 0 */ + for (p = bus->parent; p && p->parent && p->parent->number != 0; + p = p->parent) + bus = p; + return (p && p->parent) ? bus->self : NULL; + } else { + /* Find the bridge on bus 0 */ + for (p = bus->parent; p && p->number != 0; p = p->parent) + bus = p; + + return p ? bus->self : NULL; + } } -static inline int nlm_pci_link_to_irq(int link) +int xlp_socdev_to_node(const struct pci_dev *lnkdev) { - return PIC_PCIE_LINK_0_IRQ + link; + if (cpu_is_xlp9xx()) + return PCI_FUNC(lnkdev->bus->self->devfn); + else + return PCI_SLOT(lnkdev->devfn) / 8; } int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pci_dev *lnkdev; - int lnkslot, lnkfunc; + int lnkfunc, node; /* * For XLP PCIe, there is an IRQ per Link, find out which @@ -191,9 +217,11 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) lnkdev = xlp_get_pcie_link(dev); if (lnkdev == NULL) return 0; + lnkfunc = PCI_FUNC(lnkdev->devfn); - lnkslot = PCI_SLOT(lnkdev->devfn); - return nlm_irq_to_xirq(lnkslot / 8, nlm_pci_link_to_irq(lnkfunc)); + node = xlp_socdev_to_node(lnkdev); + + return nlm_irq_to_xirq(node, PIC_PCIE_LINK_LEGACY_IRQ(lnkfunc)); } /* Do platform specific device initialization at pci_enable_device() time */ @@ -220,17 +248,38 @@ static void xlp_config_pci_bswap(int node, int link) * Enable byte swap in hardware. Program each link's PCIe SWAP regions * from the link's address ranges. */ - reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link); - nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg); - - reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_LIMIT0 + link); - nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff); - - reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link); - nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg); - - reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link); - nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); + if (cpu_is_xlp9xx()) { + reg = nlm_read_bridge_reg(nbubase, + BRIDGE_9XX_PCIEMEM_BASE0 + link); + nlm_write_pci_reg(lnkbase, PCIE_9XX_BYTE_SWAP_MEM_BASE, reg); + + reg = nlm_read_bridge_reg(nbubase, + BRIDGE_9XX_PCIEMEM_LIMIT0 + link); + nlm_write_pci_reg(lnkbase, + PCIE_9XX_BYTE_SWAP_MEM_LIM, reg | 0xfff); + + reg = nlm_read_bridge_reg(nbubase, + BRIDGE_9XX_PCIEIO_BASE0 + link); + nlm_write_pci_reg(lnkbase, PCIE_9XX_BYTE_SWAP_IO_BASE, reg); + + reg = nlm_read_bridge_reg(nbubase, + BRIDGE_9XX_PCIEIO_LIMIT0 + link); + nlm_write_pci_reg(lnkbase, + PCIE_9XX_BYTE_SWAP_IO_LIM, reg | 0xfff); + } else { + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg); + + reg = nlm_read_bridge_reg(nbubase, + BRIDGE_PCIEMEM_LIMIT0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff); + + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg); + + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); + } } #else /* Swap configuration not needed in little-endian mode */ @@ -239,7 +288,6 @@ static inline void xlp_config_pci_bswap(int node, int link) {} static int __init pcibios_init(void) { - struct nlm_soc_info *nodep; uint64_t pciebase; int link, n; u32 reg; @@ -253,20 +301,20 @@ static int __init pcibios_init(void) ioport_resource.end = ~0; for (n = 0; n < NLM_NR_NODES; n++) { - nodep = nlm_get_node(n); - if (!nodep->coremask) - continue; /* node does not exist */ + if (!nlm_node_present(n)) + continue; - for (link = 0; link < 4; link++) { + for (link = 0; link < PCIE_NLINKS; link++) { pciebase = nlm_get_pcie_base(n, link); if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) continue; xlp_config_pci_bswap(n, link); + xlp_init_node_msi_irqs(n, link); /* put in intpin and irq - u-boot does not */ reg = nlm_read_pci_reg(pciebase, 0xf); - reg &= ~0x1fu; - reg |= (1 << 8) | nlm_pci_link_to_irq(link); + reg &= ~0x1ffu; + reg |= (1 << 8) | PIC_PCIE_LINK_LEGACY_IRQ(link); nlm_write_pci_reg(pciebase, 0xf, reg); pr_info("XLP PCIe: Link %d-%d initialized.\n", n, link); } diff --git a/arch/mips/pci/pci-xlr.c b/arch/mips/pci/pci-xlr.c index 4427abbd48b..0dde80332d3 100644 --- a/arch/mips/pci/pci-xlr.c +++ b/arch/mips/pci/pci-xlr.c @@ -214,14 +214,8 @@ static int get_irq_vector(const struct pci_dev *dev) } #ifdef CONFIG_PCI_MSI -void destroy_irq(unsigned int irq) -{ - /* nothing to do yet */ -} - void arch_teardown_msi_irq(unsigned int irq) { - destroy_irq(irq); } int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) @@ -263,10 +257,8 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) MSI_DATA_DELIVERY_FIXED; ret = irq_set_msi_desc(irq, desc); - if (ret < 0) { - destroy_irq(irq); + if (ret < 0) return ret; - } write_msi_msg(irq, &msg); return 0; diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 33e7aa52d9c..1bf60b12737 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -120,51 +120,37 @@ static void pcibios_scanbus(struct pci_controller *hose) #ifdef CONFIG_OF void pci_load_of_ranges(struct pci_controller *hose, struct device_node *node) { - const __be32 *ranges; - int rlen; - int pna = of_n_addr_cells(node); - int np = pna + 5; + struct of_pci_range range; + struct of_pci_range_parser parser; pr_info("PCI host bridge %s ranges:\n", node->full_name); - ranges = of_get_property(node, "ranges", &rlen); - if (ranges == NULL) - return; hose->of_node = node; - while ((rlen -= np * 4) >= 0) { - u32 pci_space; + if (of_pci_range_parser_init(&parser, node)) + return; + + for_each_of_pci_range(&parser, &range) { struct resource *res = NULL; - u64 addr, size; - - pci_space = be32_to_cpup(&ranges[0]); - addr = of_translate_address(node, ranges + 3); - size = of_read_number(ranges + pna + 3, 2); - ranges += np; - switch ((pci_space >> 24) & 0x3) { - case 1: /* PCI IO space */ + + switch (range.flags & IORESOURCE_TYPE_BITS) { + case IORESOURCE_IO: pr_info(" IO 0x%016llx..0x%016llx\n", - addr, addr + size - 1); + range.cpu_addr, + range.cpu_addr + range.size - 1); hose->io_map_base = - (unsigned long)ioremap(addr, size); + (unsigned long)ioremap(range.cpu_addr, + range.size); res = hose->io_resource; - res->flags = IORESOURCE_IO; break; - case 2: /* PCI Memory space */ - case 3: /* PCI 64 bits Memory space */ + case IORESOURCE_MEM: pr_info(" MEM 0x%016llx..0x%016llx\n", - addr, addr + size - 1); + range.cpu_addr, + range.cpu_addr + range.size - 1); res = hose->mem_resource; - res->flags = IORESOURCE_MEM; break; } - if (res != NULL) { - res->start = addr; - res->name = node->full_name; - res->end = res->start + size - 1; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; - } + if (res != NULL) + of_pci_range_to_resource(&range, node, res); } } |
