Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: Add Loongson PCI Controller support

This controller can be found on Loongson-2K SoC, Loongson-3
systems with RS780E/LS7A PCH.

The RS780E part of code was previously located at
arch/mips/pci/ops-loongson3.c and now it can use generic PCI
driver implementation.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>

authored by

Jiaxun Yang and committed by
Thomas Bogendoerfer
1f58cca5 b6caa1d8

+258
+10
drivers/pci/controller/Kconfig
··· 258 258 The Hyper-V PCI Interface is a helper driver allows other drivers to 259 259 have a common interface with the Hyper-V PCI frontend driver. 260 260 261 + config PCI_LOONGSON 262 + bool "LOONGSON PCI Controller" 263 + depends on MACH_LOONGSON64 || COMPILE_TEST 264 + depends on OF 265 + depends on PCI_QUIRKS 266 + default MACH_LOONGSON64 267 + help 268 + Say Y here if you want to enable PCI controller support on 269 + Loongson systems. 270 + 261 271 source "drivers/pci/controller/dwc/Kconfig" 262 272 source "drivers/pci/controller/mobiveil/Kconfig" 263 273 source "drivers/pci/controller/cadence/Kconfig"
+1
drivers/pci/controller/Makefile
··· 28 28 obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o 29 29 obj-$(CONFIG_VMD) += vmd.o 30 30 obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o 31 + obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o 31 32 # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW 32 33 obj-y += dwc/ 33 34 obj-y += mobiveil/
+247
drivers/pci/controller/pci-loongson.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Loongson PCI Host Controller Driver 4 + * 5 + * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com> 6 + */ 7 + 8 + #include <linux/of_device.h> 9 + #include <linux/of_pci.h> 10 + #include <linux/pci.h> 11 + #include <linux/pci_ids.h> 12 + 13 + #include "../pci.h" 14 + 15 + /* Device IDs */ 16 + #define DEV_PCIE_PORT_0 0x7a09 17 + #define DEV_PCIE_PORT_1 0x7a19 18 + #define DEV_PCIE_PORT_2 0x7a29 19 + 20 + #define DEV_LS2K_APB 0x7a02 21 + #define DEV_LS7A_CONF 0x7a10 22 + #define DEV_LS7A_LPC 0x7a0c 23 + 24 + #define FLAG_CFG0 BIT(0) 25 + #define FLAG_CFG1 BIT(1) 26 + #define FLAG_DEV_FIX BIT(2) 27 + 28 + struct loongson_pci { 29 + void __iomem *cfg0_base; 30 + void __iomem *cfg1_base; 31 + struct platform_device *pdev; 32 + u32 flags; 33 + }; 34 + 35 + /* Fixup wrong class code in PCIe bridges */ 36 + static void bridge_class_quirk(struct pci_dev *dev) 37 + { 38 + dev->class = PCI_CLASS_BRIDGE_PCI << 8; 39 + } 40 + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 41 + DEV_PCIE_PORT_0, bridge_class_quirk); 42 + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 43 + DEV_PCIE_PORT_1, bridge_class_quirk); 44 + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 45 + DEV_PCIE_PORT_2, bridge_class_quirk); 46 + 47 + static void system_bus_quirk(struct pci_dev *pdev) 48 + { 49 + /* 50 + * The address space consumed by these devices is outside the 51 + * resources of the host bridge. 52 + */ 53 + pdev->mmio_always_on = 1; 54 + pdev->non_compliant_bars = 1; 55 + } 56 + DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, 57 + DEV_LS2K_APB, system_bus_quirk); 58 + DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, 59 + DEV_LS7A_CONF, system_bus_quirk); 60 + DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, 61 + DEV_LS7A_LPC, system_bus_quirk); 62 + 63 + static void loongson_mrrs_quirk(struct pci_dev *dev) 64 + { 65 + struct pci_bus *bus = dev->bus; 66 + struct pci_dev *bridge; 67 + static const struct pci_device_id bridge_devids[] = { 68 + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_0) }, 69 + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_1) }, 70 + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_2) }, 71 + { 0, }, 72 + }; 73 + 74 + /* look for the matching bridge */ 75 + while (!pci_is_root_bus(bus)) { 76 + bridge = bus->self; 77 + bus = bus->parent; 78 + /* 79 + * Some Loongson PCIe ports have a h/w limitation of 80 + * 256 bytes maximum read request size. They can't handle 81 + * anything larger than this. So force this limit on 82 + * any devices attached under these ports. 83 + */ 84 + if (pci_match_id(bridge_devids, bridge)) { 85 + if (pcie_get_readrq(dev) > 256) { 86 + pci_info(dev, "limiting MRRS to 256\n"); 87 + pcie_set_readrq(dev, 256); 88 + } 89 + break; 90 + } 91 + } 92 + } 93 + DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_mrrs_quirk); 94 + 95 + static void __iomem *cfg1_map(struct loongson_pci *priv, int bus, 96 + unsigned int devfn, int where) 97 + { 98 + unsigned long addroff = 0x0; 99 + 100 + if (bus != 0) 101 + addroff |= BIT(28); /* Type 1 Access */ 102 + addroff |= (where & 0xff) | ((where & 0xf00) << 16); 103 + addroff |= (bus << 16) | (devfn << 8); 104 + return priv->cfg1_base + addroff; 105 + } 106 + 107 + static void __iomem *cfg0_map(struct loongson_pci *priv, int bus, 108 + unsigned int devfn, int where) 109 + { 110 + unsigned long addroff = 0x0; 111 + 112 + if (bus != 0) 113 + addroff |= BIT(24); /* Type 1 Access */ 114 + addroff |= (bus << 16) | (devfn << 8) | where; 115 + return priv->cfg0_base + addroff; 116 + } 117 + 118 + static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, unsigned int devfn, 119 + int where) 120 + { 121 + unsigned char busnum = bus->number; 122 + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); 123 + struct loongson_pci *priv = pci_host_bridge_priv(bridge); 124 + 125 + /* 126 + * Do not read more than one device on the bus other than 127 + * the host bus. For our hardware the root bus is always bus 0. 128 + */ 129 + if (priv->flags & FLAG_DEV_FIX && busnum != 0 && 130 + PCI_SLOT(devfn) > 0) 131 + return NULL; 132 + 133 + /* CFG0 can only access standard space */ 134 + if (where < PCI_CFG_SPACE_SIZE && priv->cfg0_base) 135 + return cfg0_map(priv, busnum, devfn, where); 136 + 137 + /* CFG1 can access extended space */ 138 + if (where < PCI_CFG_SPACE_EXP_SIZE && priv->cfg1_base) 139 + return cfg1_map(priv, busnum, devfn, where); 140 + 141 + return NULL; 142 + } 143 + 144 + static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 145 + { 146 + int irq; 147 + u8 val; 148 + 149 + irq = of_irq_parse_and_map_pci(dev, slot, pin); 150 + if (irq > 0) 151 + return irq; 152 + 153 + /* Care i8259 legacy systems */ 154 + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &val); 155 + /* i8259 only have 15 IRQs */ 156 + if (val > 15) 157 + return 0; 158 + 159 + return val; 160 + } 161 + 162 + /* H/w only accept 32-bit PCI operations */ 163 + static struct pci_ops loongson_pci_ops = { 164 + .map_bus = pci_loongson_map_bus, 165 + .read = pci_generic_config_read32, 166 + .write = pci_generic_config_write32, 167 + }; 168 + 169 + static const struct of_device_id loongson_pci_of_match[] = { 170 + { .compatible = "loongson,ls2k-pci", 171 + .data = (void *)(FLAG_CFG0 | FLAG_CFG1 | FLAG_DEV_FIX), }, 172 + { .compatible = "loongson,ls7a-pci", 173 + .data = (void *)(FLAG_CFG0 | FLAG_CFG1 | FLAG_DEV_FIX), }, 174 + { .compatible = "loongson,rs780e-pci", 175 + .data = (void *)(FLAG_CFG0), }, 176 + {} 177 + }; 178 + 179 + static int loongson_pci_probe(struct platform_device *pdev) 180 + { 181 + struct loongson_pci *priv; 182 + struct device *dev = &pdev->dev; 183 + struct device_node *node = dev->of_node; 184 + struct pci_host_bridge *bridge; 185 + struct resource *regs; 186 + int err; 187 + 188 + if (!node) 189 + return -ENODEV; 190 + 191 + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*priv)); 192 + if (!bridge) 193 + return -ENODEV; 194 + 195 + priv = pci_host_bridge_priv(bridge); 196 + priv->pdev = pdev; 197 + priv->flags = (unsigned long)of_device_get_match_data(dev); 198 + 199 + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 200 + if (!regs) { 201 + dev_err(dev, "missing mem resources for cfg0\n"); 202 + return -EINVAL; 203 + } 204 + 205 + priv->cfg0_base = devm_pci_remap_cfg_resource(dev, regs); 206 + if (IS_ERR(priv->cfg0_base)) 207 + return PTR_ERR(priv->cfg0_base); 208 + 209 + /* CFG1 is optional */ 210 + if (priv->flags & FLAG_CFG1) { 211 + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); 212 + if (!regs) 213 + dev_info(dev, "missing mem resource for cfg1\n"); 214 + else { 215 + priv->cfg1_base = devm_pci_remap_cfg_resource(dev, regs); 216 + if (IS_ERR(priv->cfg1_base)) 217 + priv->cfg1_base = NULL; 218 + } 219 + } 220 + 221 + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, 222 + &bridge->dma_ranges, NULL); 223 + if (err) { 224 + dev_err(dev, "failed to get bridge resources\n"); 225 + return err; 226 + } 227 + 228 + bridge->dev.parent = dev; 229 + bridge->sysdata = priv; 230 + bridge->ops = &loongson_pci_ops; 231 + bridge->map_irq = loongson_map_irq; 232 + 233 + err = pci_host_probe(bridge); 234 + if (err) 235 + return err; 236 + 237 + return 0; 238 + } 239 + 240 + static struct platform_driver loongson_pci_driver = { 241 + .driver = { 242 + .name = "loongson-pci", 243 + .of_match_table = loongson_pci_of_match, 244 + }, 245 + .probe = loongson_pci_probe, 246 + }; 247 + builtin_platform_driver(loongson_pci_driver);