Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Loongson PCI Host Controller Driver
0004  *
0005  * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
0006  */
0007 
0008 #include <linux/of_device.h>
0009 #include <linux/of_pci.h>
0010 #include <linux/pci.h>
0011 #include <linux/pci_ids.h>
0012 #include <linux/pci-acpi.h>
0013 #include <linux/pci-ecam.h>
0014 
0015 #include "../pci.h"
0016 
0017 /* Device IDs */
0018 #define DEV_PCIE_PORT_0 0x7a09
0019 #define DEV_PCIE_PORT_1 0x7a19
0020 #define DEV_PCIE_PORT_2 0x7a29
0021 
0022 #define DEV_LS2K_APB    0x7a02
0023 #define DEV_LS7A_GMAC   0x7a03
0024 #define DEV_LS7A_DC1    0x7a06
0025 #define DEV_LS7A_LPC    0x7a0c
0026 #define DEV_LS7A_AHCI   0x7a08
0027 #define DEV_LS7A_CONF   0x7a10
0028 #define DEV_LS7A_GNET   0x7a13
0029 #define DEV_LS7A_EHCI   0x7a14
0030 #define DEV_LS7A_DC2    0x7a36
0031 #define DEV_LS7A_HDMI   0x7a37
0032 
0033 #define FLAG_CFG0   BIT(0)
0034 #define FLAG_CFG1   BIT(1)
0035 #define FLAG_DEV_FIX    BIT(2)
0036 #define FLAG_DEV_HIDDEN BIT(3)
0037 
0038 struct loongson_pci_data {
0039     u32 flags;
0040     struct pci_ops *ops;
0041 };
0042 
0043 struct loongson_pci {
0044     void __iomem *cfg0_base;
0045     void __iomem *cfg1_base;
0046     struct platform_device *pdev;
0047     const struct loongson_pci_data *data;
0048 };
0049 
0050 /* Fixup wrong class code in PCIe bridges */
0051 static void bridge_class_quirk(struct pci_dev *dev)
0052 {
0053     dev->class = PCI_CLASS_BRIDGE_PCI_NORMAL;
0054 }
0055 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
0056             DEV_PCIE_PORT_0, bridge_class_quirk);
0057 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
0058             DEV_PCIE_PORT_1, bridge_class_quirk);
0059 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
0060             DEV_PCIE_PORT_2, bridge_class_quirk);
0061 
0062 static void system_bus_quirk(struct pci_dev *pdev)
0063 {
0064     /*
0065      * The address space consumed by these devices is outside the
0066      * resources of the host bridge.
0067      */
0068     pdev->mmio_always_on = 1;
0069     pdev->non_compliant_bars = 1;
0070 }
0071 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
0072             DEV_LS2K_APB, system_bus_quirk);
0073 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
0074             DEV_LS7A_CONF, system_bus_quirk);
0075 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
0076             DEV_LS7A_LPC, system_bus_quirk);
0077 
0078 static void loongson_mrrs_quirk(struct pci_dev *dev)
0079 {
0080     struct pci_bus *bus = dev->bus;
0081     struct pci_dev *bridge;
0082     static const struct pci_device_id bridge_devids[] = {
0083         { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_0) },
0084         { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_1) },
0085         { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_2) },
0086         { 0, },
0087     };
0088 
0089     /* look for the matching bridge */
0090     while (!pci_is_root_bus(bus)) {
0091         bridge = bus->self;
0092         bus = bus->parent;
0093         /*
0094          * Some Loongson PCIe ports have a h/w limitation of
0095          * 256 bytes maximum read request size. They can't handle
0096          * anything larger than this. So force this limit on
0097          * any devices attached under these ports.
0098          */
0099         if (pci_match_id(bridge_devids, bridge)) {
0100             if (pcie_get_readrq(dev) > 256) {
0101                 pci_info(dev, "limiting MRRS to 256\n");
0102                 pcie_set_readrq(dev, 256);
0103             }
0104             break;
0105         }
0106     }
0107 }
0108 DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_mrrs_quirk);
0109 
0110 static void loongson_pci_pin_quirk(struct pci_dev *pdev)
0111 {
0112     pdev->pin = 1 + (PCI_FUNC(pdev->devfn) & 3);
0113 }
0114 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0115             DEV_LS7A_DC1, loongson_pci_pin_quirk);
0116 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0117             DEV_LS7A_DC2, loongson_pci_pin_quirk);
0118 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0119             DEV_LS7A_GMAC, loongson_pci_pin_quirk);
0120 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0121             DEV_LS7A_AHCI, loongson_pci_pin_quirk);
0122 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0123             DEV_LS7A_EHCI, loongson_pci_pin_quirk);
0124 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0125             DEV_LS7A_GNET, loongson_pci_pin_quirk);
0126 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
0127             DEV_LS7A_HDMI, loongson_pci_pin_quirk);
0128 
0129 static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus)
0130 {
0131     struct pci_config_window *cfg;
0132 
0133     if (acpi_disabled)
0134         return (struct loongson_pci *)(bus->sysdata);
0135 
0136     cfg = bus->sysdata;
0137     return (struct loongson_pci *)(cfg->priv);
0138 }
0139 
0140 static void __iomem *cfg0_map(struct loongson_pci *priv, struct pci_bus *bus,
0141                   unsigned int devfn, int where)
0142 {
0143     unsigned long addroff = 0x0;
0144     unsigned char busnum = bus->number;
0145 
0146     if (!pci_is_root_bus(bus)) {
0147         addroff |= BIT(24); /* Type 1 Access */
0148         addroff |= (busnum << 16);
0149     }
0150     addroff |= (devfn << 8) | where;
0151     return priv->cfg0_base + addroff;
0152 }
0153 
0154 static void __iomem *cfg1_map(struct loongson_pci *priv, struct pci_bus *bus,
0155                   unsigned int devfn, int where)
0156 {
0157     unsigned long addroff = 0x0;
0158     unsigned char busnum = bus->number;
0159 
0160     if (!pci_is_root_bus(bus)) {
0161         addroff |= BIT(28); /* Type 1 Access */
0162         addroff |= (busnum << 16);
0163     }
0164     addroff |= (devfn << 8) | (where & 0xff) | ((where & 0xf00) << 16);
0165     return priv->cfg1_base + addroff;
0166 }
0167 
0168 static bool pdev_may_exist(struct pci_bus *bus, unsigned int device,
0169                unsigned int function)
0170 {
0171     return !(pci_is_root_bus(bus) &&
0172         (device >= 9 && device <= 20) && (function > 0));
0173 }
0174 
0175 static void __iomem *pci_loongson_map_bus(struct pci_bus *bus,
0176                       unsigned int devfn, int where)
0177 {
0178     unsigned int device = PCI_SLOT(devfn);
0179     unsigned int function = PCI_FUNC(devfn);
0180     struct loongson_pci *priv = pci_bus_to_loongson_pci(bus);
0181 
0182     /*
0183      * Do not read more than one device on the bus other than
0184      * the host bus.
0185      */
0186     if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) {
0187         if (!pci_is_root_bus(bus) && (device > 0))
0188             return NULL;
0189     }
0190 
0191     /* Don't access non-existent devices */
0192     if (priv->data->flags & FLAG_DEV_HIDDEN) {
0193         if (!pdev_may_exist(bus, device, function))
0194             return NULL;
0195     }
0196 
0197     /* CFG0 can only access standard space */
0198     if (where < PCI_CFG_SPACE_SIZE && priv->cfg0_base)
0199         return cfg0_map(priv, bus, devfn, where);
0200 
0201     /* CFG1 can access extended space */
0202     if (where < PCI_CFG_SPACE_EXP_SIZE && priv->cfg1_base)
0203         return cfg1_map(priv, bus, devfn, where);
0204 
0205     return NULL;
0206 }
0207 
0208 #ifdef CONFIG_OF
0209 
0210 static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
0211 {
0212     int irq;
0213     u8 val;
0214 
0215     irq = of_irq_parse_and_map_pci(dev, slot, pin);
0216     if (irq > 0)
0217         return irq;
0218 
0219     /* Care i8259 legacy systems */
0220     pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &val);
0221     /* i8259 only have 15 IRQs */
0222     if (val > 15)
0223         return 0;
0224 
0225     return val;
0226 }
0227 
0228 /* LS2K/LS7A accept 8/16/32-bit PCI config operations */
0229 static struct pci_ops loongson_pci_ops = {
0230     .map_bus = pci_loongson_map_bus,
0231     .read   = pci_generic_config_read,
0232     .write  = pci_generic_config_write,
0233 };
0234 
0235 /* RS780/SR5690 only accept 32-bit PCI config operations */
0236 static struct pci_ops loongson_pci_ops32 = {
0237     .map_bus = pci_loongson_map_bus,
0238     .read   = pci_generic_config_read32,
0239     .write  = pci_generic_config_write32,
0240 };
0241 
0242 static const struct loongson_pci_data ls2k_pci_data = {
0243     .flags = FLAG_CFG1 | FLAG_DEV_FIX | FLAG_DEV_HIDDEN,
0244     .ops = &loongson_pci_ops,
0245 };
0246 
0247 static const struct loongson_pci_data ls7a_pci_data = {
0248     .flags = FLAG_CFG1 | FLAG_DEV_FIX | FLAG_DEV_HIDDEN,
0249     .ops = &loongson_pci_ops,
0250 };
0251 
0252 static const struct loongson_pci_data rs780e_pci_data = {
0253     .flags = FLAG_CFG0,
0254     .ops = &loongson_pci_ops32,
0255 };
0256 
0257 static const struct of_device_id loongson_pci_of_match[] = {
0258     { .compatible = "loongson,ls2k-pci",
0259         .data = &ls2k_pci_data, },
0260     { .compatible = "loongson,ls7a-pci",
0261         .data = &ls7a_pci_data, },
0262     { .compatible = "loongson,rs780e-pci",
0263         .data = &rs780e_pci_data, },
0264     {}
0265 };
0266 
0267 static int loongson_pci_probe(struct platform_device *pdev)
0268 {
0269     struct loongson_pci *priv;
0270     struct device *dev = &pdev->dev;
0271     struct device_node *node = dev->of_node;
0272     struct pci_host_bridge *bridge;
0273     struct resource *regs;
0274 
0275     if (!node)
0276         return -ENODEV;
0277 
0278     bridge = devm_pci_alloc_host_bridge(dev, sizeof(*priv));
0279     if (!bridge)
0280         return -ENODEV;
0281 
0282     priv = pci_host_bridge_priv(bridge);
0283     priv->pdev = pdev;
0284     priv->data = of_device_get_match_data(dev);
0285 
0286     if (priv->data->flags & FLAG_CFG0) {
0287         regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0288         if (!regs)
0289             dev_err(dev, "missing mem resources for cfg0\n");
0290         else {
0291             priv->cfg0_base = devm_pci_remap_cfg_resource(dev, regs);
0292             if (IS_ERR(priv->cfg0_base))
0293                 return PTR_ERR(priv->cfg0_base);
0294         }
0295     }
0296 
0297     if (priv->data->flags & FLAG_CFG1) {
0298         regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
0299         if (!regs)
0300             dev_info(dev, "missing mem resource for cfg1\n");
0301         else {
0302             priv->cfg1_base = devm_pci_remap_cfg_resource(dev, regs);
0303             if (IS_ERR(priv->cfg1_base))
0304                 priv->cfg1_base = NULL;
0305         }
0306     }
0307 
0308     bridge->sysdata = priv;
0309     bridge->ops = priv->data->ops;
0310     bridge->map_irq = loongson_map_irq;
0311 
0312     return pci_host_probe(bridge);
0313 }
0314 
0315 static struct platform_driver loongson_pci_driver = {
0316     .driver = {
0317         .name = "loongson-pci",
0318         .of_match_table = loongson_pci_of_match,
0319     },
0320     .probe = loongson_pci_probe,
0321 };
0322 builtin_platform_driver(loongson_pci_driver);
0323 
0324 #endif
0325 
0326 #ifdef CONFIG_ACPI
0327 
0328 static int loongson_pci_ecam_init(struct pci_config_window *cfg)
0329 {
0330     struct device *dev = cfg->parent;
0331     struct loongson_pci *priv;
0332     struct loongson_pci_data *data;
0333 
0334     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0335     if (!priv)
0336         return -ENOMEM;
0337 
0338     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0339     if (!data)
0340         return -ENOMEM;
0341 
0342     cfg->priv = priv;
0343     data->flags = FLAG_CFG1 | FLAG_DEV_HIDDEN;
0344     priv->data = data;
0345     priv->cfg1_base = cfg->win - (cfg->busr.start << 16);
0346 
0347     return 0;
0348 }
0349 
0350 const struct pci_ecam_ops loongson_pci_ecam_ops = {
0351     .bus_shift = 16,
0352     .init      = loongson_pci_ecam_init,
0353     .pci_ops   = {
0354         .map_bus = pci_loongson_map_bus,
0355         .read    = pci_generic_config_read,
0356         .write   = pci_generic_config_write,
0357     }
0358 };
0359 
0360 #endif