xref: /linux/arch/loongarch/pci/pci.c (revision e80948062dcfff0543c5c60ba8654e825bf73b5a)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/kernel.h>
6 #include <linux/init.h>
7 #include <linux/acpi.h>
8 #include <linux/delay.h>
9 #include <linux/types.h>
10 #include <linux/pci.h>
11 #include <linux/vgaarb.h>
12 #include <linux/io-64-nonatomic-lo-hi.h>
13 #include <asm/cacheflush.h>
14 #include <asm/loongson.h>
15 
16 #define PCI_DEVICE_ID_LOONGSON_HOST     0x7a00
17 #define PCI_DEVICE_ID_LOONGSON_DC1      0x7a06
18 #define PCI_DEVICE_ID_LOONGSON_DC2      0x7a36
19 #define PCI_DEVICE_ID_LOONGSON_DC3      0x7a46
20 #define PCI_DEVICE_ID_LOONGSON_GPU1     0x7a15
21 #define PCI_DEVICE_ID_LOONGSON_GPU2     0x7a25
22 #define PCI_DEVICE_ID_LOONGSON_GPU3     0x7a35
23 
raw_pci_read(unsigned int domain,unsigned int bus,unsigned int devfn,int reg,int len,u32 * val)24 int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
25 						int reg, int len, u32 *val)
26 {
27 	struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
28 
29 	if (bus_tmp)
30 		return bus_tmp->ops->read(bus_tmp, devfn, reg, len, val);
31 	return -EINVAL;
32 }
33 
raw_pci_write(unsigned int domain,unsigned int bus,unsigned int devfn,int reg,int len,u32 val)34 int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
35 						int reg, int len, u32 val)
36 {
37 	struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
38 
39 	if (bus_tmp)
40 		return bus_tmp->ops->write(bus_tmp, devfn, reg, len, val);
41 	return -EINVAL;
42 }
43 
mcfg_addr_init(int node)44 phys_addr_t mcfg_addr_init(int node)
45 {
46 	return (((u64)node << 44) | MCFG_EXT_PCICFG_BASE);
47 }
48 
pcibios_init(void)49 static int __init pcibios_init(void)
50 {
51 	unsigned int lsize;
52 
53 	/*
54 	 * Set PCI cacheline size to that of the last level in the
55 	 * cache hierarchy.
56 	 */
57 	lsize = cpu_last_level_cache_line_size();
58 
59 	if (lsize) {
60 		pci_dfl_cache_line_size = lsize >> 2;
61 
62 		pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
63 	}
64 
65 	return 0;
66 }
67 
68 subsys_initcall(pcibios_init);
69 
pcibios_device_add(struct pci_dev * dev)70 int pcibios_device_add(struct pci_dev *dev)
71 {
72 	int id;
73 	struct irq_domain *dom;
74 
75 	id = pci_domain_nr(dev->bus);
76 	dom = irq_find_matching_fwnode(get_pch_msi_handle(id), DOMAIN_BUS_PCI_MSI);
77 	dev_set_msi_domain(&dev->dev, dom);
78 
79 	return 0;
80 }
81 
pcibios_alloc_irq(struct pci_dev * dev)82 int pcibios_alloc_irq(struct pci_dev *dev)
83 {
84 	if (acpi_disabled)
85 		return 0;
86 	if (pci_dev_msi_enabled(dev))
87 		return 0;
88 	return acpi_pci_irq_enable(dev);
89 }
90 
pci_fixup_vgadev(struct pci_dev * pdev)91 static void pci_fixup_vgadev(struct pci_dev *pdev)
92 {
93 	struct pci_dev *devp = NULL;
94 
95 	while ((devp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, devp))) {
96 		if (devp->vendor != PCI_VENDOR_ID_LOONGSON) {
97 			vga_set_default_device(devp);
98 			dev_info(&pdev->dev,
99 				"Overriding boot device as %X:%X\n",
100 				devp->vendor, devp->device);
101 		}
102 	}
103 }
104 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1, pci_fixup_vgadev);
105 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2, pci_fixup_vgadev);
106 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC3, pci_fixup_vgadev);
107 
108 #define CRTC_NUM_MAX		2
109 #define CRTC_OUTPUT_ENABLE	0x100
110 
loongson_gpu_fixup_dma_hang(struct pci_dev * pdev,bool on)111 static void loongson_gpu_fixup_dma_hang(struct pci_dev *pdev, bool on)
112 {
113 	u32 i, val, count, crtc_offset, device;
114 	void __iomem *crtc_reg, *base, *regbase;
115 	static u32 crtc_status[CRTC_NUM_MAX] = { 0 };
116 
117 	base = pdev->bus->ops->map_bus(pdev->bus, pdev->devfn + 1, 0);
118 	device = readw(base + PCI_DEVICE_ID);
119 
120 	regbase = ioremap(readq(base + PCI_BASE_ADDRESS_0) & ~0xffull, SZ_64K);
121 	if (!regbase) {
122 		pci_err(pdev, "Failed to ioremap()\n");
123 		return;
124 	}
125 
126 	switch (device) {
127 	case PCI_DEVICE_ID_LOONGSON_DC2:
128 		crtc_reg = regbase + 0x1240;
129 		crtc_offset = 0x10;
130 		break;
131 	case PCI_DEVICE_ID_LOONGSON_DC3:
132 		crtc_reg = regbase;
133 		crtc_offset = 0x400;
134 		break;
135 	default:
136 		iounmap(regbase);
137 		return;
138 	}
139 
140 	for (i = 0; i < CRTC_NUM_MAX; i++, crtc_reg += crtc_offset) {
141 		val = readl(crtc_reg);
142 
143 		if (!on)
144 			crtc_status[i] = val;
145 
146 		/* No need to fixup if the status is off at startup. */
147 		if (!(crtc_status[i] & CRTC_OUTPUT_ENABLE))
148 			continue;
149 
150 		if (on)
151 			val |= CRTC_OUTPUT_ENABLE;
152 		else
153 			val &= ~CRTC_OUTPUT_ENABLE;
154 
155 		mb();
156 		writel(val, crtc_reg);
157 
158 		for (count = 0; count < 40; count++) {
159 			val = readl(crtc_reg) & CRTC_OUTPUT_ENABLE;
160 			if ((on && val) || (!on && !val))
161 				break;
162 			udelay(1000);
163 		}
164 
165 		pci_info(pdev, "DMA hang fixup at reg[0x%lx]: 0x%x\n",
166 				(unsigned long)crtc_reg & 0xffff, readl(crtc_reg));
167 	}
168 
169 	iounmap(regbase);
170 }
171 
pci_fixup_dma_hang_early(struct pci_dev * pdev)172 static void pci_fixup_dma_hang_early(struct pci_dev *pdev)
173 {
174 	loongson_gpu_fixup_dma_hang(pdev, false);
175 }
176 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU2, pci_fixup_dma_hang_early);
177 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU3, pci_fixup_dma_hang_early);
178 
pci_fixup_dma_hang_final(struct pci_dev * pdev)179 static void pci_fixup_dma_hang_final(struct pci_dev *pdev)
180 {
181 	loongson_gpu_fixup_dma_hang(pdev, true);
182 }
183 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU2, pci_fixup_dma_hang_final);
184 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU3, pci_fixup_dma_hang_final);
185