1 // SPDX-License-Identifier: GPL-2.0
3 * Loongson PCI Host Controller Driver
5 * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
8 #include <linux/of_device.h>
9 #include <linux/of_pci.h>
10 #include <linux/pci.h>
11 #include <linux/pci_ids.h>
16 #define DEV_PCIE_PORT_0 0x7a09
17 #define DEV_PCIE_PORT_1 0x7a19
18 #define DEV_PCIE_PORT_2 0x7a29
20 #define DEV_LS2K_APB 0x7a02
21 #define DEV_LS7A_CONF 0x7a10
22 #define DEV_LS7A_LPC 0x7a0c
24 #define FLAG_CFG0 BIT(0)
25 #define FLAG_CFG1 BIT(1)
26 #define FLAG_DEV_FIX BIT(2)
29 void __iomem
*cfg0_base
;
30 void __iomem
*cfg1_base
;
31 struct platform_device
*pdev
;
35 /* Fixup wrong class code in PCIe bridges */
36 static void bridge_class_quirk(struct pci_dev
*dev
)
38 dev
->class = PCI_CLASS_BRIDGE_PCI
<< 8;
40 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON
,
41 DEV_PCIE_PORT_0
, bridge_class_quirk
);
42 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON
,
43 DEV_PCIE_PORT_1
, bridge_class_quirk
);
44 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON
,
45 DEV_PCIE_PORT_2
, bridge_class_quirk
);
47 static void system_bus_quirk(struct pci_dev
*pdev
)
50 * The address space consumed by these devices is outside the
51 * resources of the host bridge.
53 pdev
->mmio_always_on
= 1;
54 pdev
->non_compliant_bars
= 1;
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
);
63 static void loongson_mrrs_quirk(struct pci_dev
*dev
)
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
) },
74 /* look for the matching bridge */
75 while (!pci_is_root_bus(bus
)) {
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.
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);
93 DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID
, PCI_ANY_ID
, loongson_mrrs_quirk
);
95 static void __iomem
*cfg1_map(struct loongson_pci
*priv
, int bus
,
96 unsigned int devfn
, int where
)
98 unsigned long addroff
= 0x0;
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
;
107 static void __iomem
*cfg0_map(struct loongson_pci
*priv
, int bus
,
108 unsigned int devfn
, int where
)
110 unsigned long addroff
= 0x0;
113 addroff
|= BIT(24); /* Type 1 Access */
114 addroff
|= (bus
<< 16) | (devfn
<< 8) | where
;
115 return priv
->cfg0_base
+ addroff
;
118 static void __iomem
*pci_loongson_map_bus(struct pci_bus
*bus
, unsigned int devfn
,
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
);
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.
129 if (priv
->flags
& FLAG_DEV_FIX
&& busnum
!= 0 &&
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
);
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
);
144 static int loongson_map_irq(const struct pci_dev
*dev
, u8 slot
, u8 pin
)
149 irq
= of_irq_parse_and_map_pci(dev
, slot
, pin
);
153 /* Care i8259 legacy systems */
154 pci_read_config_byte(dev
, PCI_INTERRUPT_LINE
, &val
);
155 /* i8259 only have 15 IRQs */
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
,
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
), },
179 static int loongson_pci_probe(struct platform_device
*pdev
)
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
;
190 bridge
= devm_pci_alloc_host_bridge(dev
, sizeof(*priv
));
194 priv
= pci_host_bridge_priv(bridge
);
196 priv
->flags
= (unsigned long)of_device_get_match_data(dev
);
198 regs
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
200 dev_err(dev
, "missing mem resources for cfg0\n");
204 priv
->cfg0_base
= devm_pci_remap_cfg_resource(dev
, regs
);
205 if (IS_ERR(priv
->cfg0_base
))
206 return PTR_ERR(priv
->cfg0_base
);
208 /* CFG1 is optional */
209 if (priv
->flags
& FLAG_CFG1
) {
210 regs
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
212 dev_info(dev
, "missing mem resource for cfg1\n");
214 priv
->cfg1_base
= devm_pci_remap_cfg_resource(dev
, regs
);
215 if (IS_ERR(priv
->cfg1_base
))
216 priv
->cfg1_base
= NULL
;
220 bridge
->sysdata
= priv
;
221 bridge
->ops
= &loongson_pci_ops
;
222 bridge
->map_irq
= loongson_map_irq
;
224 return pci_host_probe(bridge
);
227 static struct platform_driver loongson_pci_driver
= {
229 .name
= "loongson-pci",
230 .of_match_table
= loongson_pci_of_match
,
232 .probe
= loongson_pci_probe
,
234 builtin_platform_driver(loongson_pci_driver
);