]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
014e4678 GG |
2 | /* |
3 | * Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation | |
4 | * | |
5 | * Copyright (C) 2011 Andes Technology Corporation | |
6 | * Gavin Guo, Andes Technology Corporation <[email protected]> | |
7 | * Macpaul Lin, Andes Technology Corporation <[email protected]> | |
014e4678 GG |
8 | */ |
9 | #include <common.h> | |
691d719d | 10 | #include <init.h> |
f7ae49fc | 11 | #include <log.h> |
014e4678 GG |
12 | #include <malloc.h> |
13 | #include <pci.h> | |
14 | ||
8599515f GJ |
15 | #include <faraday/ftpci100.h> |
16 | ||
014e4678 GG |
17 | #include <asm/io.h> |
18 | #include <asm/types.h> /* u32, u16.... used by pci.h */ | |
19 | ||
014e4678 GG |
20 | struct ftpci100_data { |
21 | unsigned int reg_base; | |
22 | unsigned int io_base; | |
23 | unsigned int mem_base; | |
24 | unsigned int mmio_base; | |
25 | unsigned int ndevs; | |
26 | }; | |
27 | ||
28 | static struct pci_config devs[FTPCI100_MAX_FUNCTIONS]; | |
29 | static struct pci_controller local_hose; | |
30 | ||
31 | static void setup_pci_bar(unsigned int bus, unsigned int dev, unsigned func, | |
32 | unsigned char header, struct ftpci100_data *priv) | |
33 | { | |
34 | struct pci_controller *hose = (struct pci_controller *)&local_hose; | |
35 | unsigned int i, tmp32, bar_no, iovsmem = 1; | |
36 | pci_dev_t dev_nu; | |
37 | ||
38 | /* A device is present, add an entry to the array */ | |
39 | devs[priv->ndevs].bus = bus; | |
40 | devs[priv->ndevs].dev = dev; | |
41 | devs[priv->ndevs].func = func; | |
42 | ||
43 | dev_nu = PCI_BDF(bus, dev, func); | |
44 | ||
45 | if ((header & 0x7f) == 0x01) | |
46 | /* PCI-PCI Bridge */ | |
47 | bar_no = 2; | |
48 | else | |
49 | bar_no = 6; | |
50 | ||
51 | /* Allocate address spaces by configuring BARs */ | |
52 | for (i = 0; i < bar_no; i++) { | |
53 | pci_hose_write_config_dword(hose, dev_nu, | |
54 | PCI_BASE_ADDRESS_0 + i * 4, 0xffffffff); | |
55 | pci_hose_read_config_dword(hose, dev_nu, | |
56 | PCI_BASE_ADDRESS_0 + i * 4, &tmp32); | |
57 | ||
58 | if (tmp32 == 0x0) | |
59 | continue; | |
60 | ||
61 | /* IO space */ | |
62 | if (tmp32 & 0x1) { | |
63 | iovsmem = 0; | |
64 | unsigned int size_mask = ~(tmp32 & 0xfffffffc); | |
65 | ||
66 | if (priv->io_base & size_mask) | |
67 | priv->io_base = (priv->io_base & ~size_mask) + \ | |
68 | size_mask + 1; | |
69 | ||
70 | devs[priv->ndevs].bar[i].addr = priv->io_base; | |
71 | devs[priv->ndevs].bar[i].size = size_mask + 1; | |
72 | ||
73 | pci_hose_write_config_dword(hose, dev_nu, | |
74 | PCI_BASE_ADDRESS_0 + i * 4, | |
75 | priv->io_base); | |
76 | ||
77 | debug("Allocated IO address 0x%X-" \ | |
78 | "0x%X for Bus %d, Device %d, Function %d\n", | |
79 | priv->io_base, | |
80 | priv->io_base + size_mask, bus, dev, func); | |
81 | ||
82 | priv->io_base += size_mask + 1; | |
83 | } else { | |
84 | /* Memory space */ | |
85 | unsigned int is_64bit = ((tmp32 & 0x6) == 0x4); | |
86 | unsigned int is_pref = tmp32 & 0x8; | |
87 | unsigned int size_mask = ~(tmp32 & 0xfffffff0); | |
88 | unsigned int alloc_base; | |
89 | unsigned int *addr_mem_base; | |
90 | ||
91 | if (is_pref) | |
92 | addr_mem_base = &priv->mem_base; | |
93 | else | |
94 | addr_mem_base = &priv->mmio_base; | |
95 | ||
96 | alloc_base = *addr_mem_base; | |
97 | ||
98 | if (alloc_base & size_mask) | |
99 | alloc_base = (alloc_base & ~size_mask) \ | |
100 | + size_mask + 1; | |
101 | ||
102 | pci_hose_write_config_dword(hose, dev_nu, | |
103 | PCI_BASE_ADDRESS_0 + i * 4, alloc_base); | |
104 | ||
105 | debug("Allocated %s address 0x%X-" \ | |
106 | "0x%X for Bus %d, Device %d, Function %d\n", | |
107 | is_pref ? "MEM" : "MMIO", alloc_base, | |
108 | alloc_base + size_mask, bus, dev, func); | |
109 | ||
110 | devs[priv->ndevs].bar[i].addr = alloc_base; | |
111 | devs[priv->ndevs].bar[i].size = size_mask + 1; | |
112 | ||
113 | debug("BAR address BAR size\n"); | |
114 | debug("%010x %08d\n", | |
115 | devs[priv->ndevs].bar[0].addr, | |
116 | devs[priv->ndevs].bar[0].size); | |
117 | ||
118 | alloc_base += size_mask + 1; | |
119 | *addr_mem_base = alloc_base; | |
120 | ||
121 | if (is_64bit) { | |
122 | i++; | |
123 | pci_hose_write_config_dword(hose, dev_nu, | |
124 | PCI_BASE_ADDRESS_0 + i * 4, 0x0); | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | /* Enable Bus Master, Memory Space, and IO Space */ | |
130 | pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32); | |
131 | pci_hose_write_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, 0x08); | |
132 | pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32); | |
133 | ||
134 | pci_hose_read_config_dword(hose, dev_nu, PCI_COMMAND, &tmp32); | |
135 | ||
136 | tmp32 &= 0xffff; | |
137 | ||
138 | if (iovsmem == 0) | |
139 | tmp32 |= 0x5; | |
140 | else | |
141 | tmp32 |= 0x6; | |
142 | ||
143 | pci_hose_write_config_dword(hose, dev_nu, PCI_COMMAND, tmp32); | |
144 | } | |
145 | ||
146 | static void pci_bus_scan(struct ftpci100_data *priv) | |
147 | { | |
148 | struct pci_controller *hose = (struct pci_controller *)&local_hose; | |
149 | unsigned int bus, dev, func; | |
150 | pci_dev_t dev_nu; | |
151 | unsigned int data32; | |
152 | unsigned int tmp; | |
153 | unsigned char header; | |
154 | unsigned char int_pin; | |
155 | unsigned int niobars; | |
156 | unsigned int nmbars; | |
157 | ||
158 | priv->ndevs = 1; | |
159 | ||
160 | nmbars = 0; | |
161 | niobars = 0; | |
162 | ||
163 | for (bus = 0; bus < MAX_BUS_NUM; bus++) | |
164 | for (dev = 0; dev < MAX_DEV_NUM; dev++) | |
165 | for (func = 0; func < MAX_FUN_NUM; func++) { | |
166 | dev_nu = PCI_BDF(bus, dev, func); | |
167 | pci_hose_read_config_dword(hose, dev_nu, | |
168 | PCI_VENDOR_ID, &data32); | |
169 | ||
170 | /* | |
171 | * some broken boards return 0 or ~0, | |
172 | * if a slot is empty. | |
173 | */ | |
174 | if (data32 == 0xffffffff || | |
175 | data32 == 0x00000000 || | |
176 | data32 == 0x0000ffff || | |
177 | data32 == 0xffff0000) | |
178 | continue; | |
179 | ||
180 | pci_hose_read_config_dword(hose, dev_nu, | |
181 | PCI_HEADER_TYPE, &tmp); | |
182 | header = (unsigned char)tmp; | |
183 | setup_pci_bar(bus, dev, func, header, priv); | |
184 | ||
185 | devs[priv->ndevs].v_id = (u16)(data32 & \ | |
186 | 0x0000ffff); | |
187 | ||
188 | devs[priv->ndevs].d_id = (u16)((data32 & \ | |
189 | 0xffff0000) >> 16); | |
190 | ||
191 | /* Figure out what INTX# line the card uses */ | |
192 | pci_hose_read_config_byte(hose, dev_nu, | |
193 | PCI_INTERRUPT_PIN, &int_pin); | |
194 | ||
195 | /* assign the appropriate irq line */ | |
196 | if (int_pin > PCI_IRQ_LINES) { | |
197 | printf("more irq lines than expect\n"); | |
198 | } else if (int_pin != 0) { | |
199 | /* This device uses an interrupt line */ | |
200 | devs[priv->ndevs].pin = int_pin; | |
201 | } | |
202 | ||
203 | pci_hose_read_config_dword(hose, dev_nu, | |
204 | PCI_CLASS_DEVICE, &data32); | |
205 | ||
206 | debug("%06d %03d %03d " \ | |
207 | "%04d %08x %08x " \ | |
208 | "%03d %08x %06d %08x\n", | |
209 | priv->ndevs, devs[priv->ndevs].bus, | |
210 | devs[priv->ndevs].dev, | |
211 | devs[priv->ndevs].func, | |
212 | devs[priv->ndevs].d_id, | |
213 | devs[priv->ndevs].v_id, | |
214 | devs[priv->ndevs].pin, | |
215 | devs[priv->ndevs].bar[0].addr, | |
216 | devs[priv->ndevs].bar[0].size, | |
217 | data32 >> 8); | |
218 | ||
219 | priv->ndevs++; | |
220 | } | |
221 | } | |
222 | ||
223 | static void ftpci_preinit(struct ftpci100_data *priv) | |
224 | { | |
225 | struct ftpci100_ahbc *ftpci100; | |
226 | struct pci_controller *hose = (struct pci_controller *)&local_hose; | |
227 | u32 pci_config_addr; | |
228 | u32 pci_config_data; | |
229 | ||
230 | priv->reg_base = CONFIG_FTPCI100_BASE; | |
231 | priv->io_base = CONFIG_FTPCI100_BASE + CONFIG_FTPCI100_IO_SIZE; | |
232 | priv->mmio_base = CONFIG_FTPCI100_MEM_BASE; | |
233 | priv->mem_base = CONFIG_FTPCI100_MEM_BASE + CONFIG_FTPCI100_MEM_SIZE; | |
234 | ||
235 | ftpci100 = (struct ftpci100_ahbc *)priv->reg_base; | |
236 | ||
237 | pci_config_addr = (u32) &ftpci100->conf; | |
238 | pci_config_data = (u32) &ftpci100->data; | |
239 | ||
240 | /* print device name */ | |
241 | printf("FTPCI100\n"); | |
242 | ||
243 | /* dump basic configuration */ | |
244 | debug("%s: Config addr is %08X, data port is %08X\n", | |
245 | __func__, pci_config_addr, pci_config_data); | |
246 | ||
247 | /* PCI memory space */ | |
248 | pci_set_region(hose->regions + 0, | |
249 | CONFIG_PCI_MEM_BUS, | |
250 | CONFIG_PCI_MEM_PHYS, | |
251 | CONFIG_PCI_MEM_SIZE, | |
252 | PCI_REGION_MEM); | |
253 | hose->region_count++; | |
254 | ||
255 | /* PCI IO space */ | |
256 | pci_set_region(hose->regions + 1, | |
257 | CONFIG_PCI_IO_BUS, | |
258 | CONFIG_PCI_IO_PHYS, | |
259 | CONFIG_PCI_IO_SIZE, | |
260 | PCI_REGION_IO); | |
261 | hose->region_count++; | |
262 | ||
263 | #if defined(CONFIG_PCI_SYS_BUS) | |
264 | /* PCI System Memory space */ | |
265 | pci_set_region(hose->regions + 2, | |
266 | CONFIG_PCI_SYS_BUS, | |
267 | CONFIG_PCI_SYS_PHYS, | |
268 | CONFIG_PCI_SYS_SIZE, | |
269 | PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); | |
270 | hose->region_count++; | |
271 | #endif | |
272 | ||
273 | /* setup indirect read/write function */ | |
274 | pci_setup_indirect(hose, pci_config_addr, pci_config_data); | |
275 | ||
276 | /* register hose */ | |
277 | pci_register_hose(hose); | |
278 | } | |
279 | ||
280 | void pci_ftpci_init(void) | |
281 | { | |
282 | struct ftpci100_data *priv = NULL; | |
283 | struct pci_controller *hose = (struct pci_controller *)&local_hose; | |
284 | pci_dev_t bridge_num; | |
285 | ||
286 | struct pci_device_id bridge_ids[] = { | |
287 | {FTPCI100_BRIDGE_VENDORID, FTPCI100_BRIDGE_DEVICEID}, | |
288 | {0, 0} | |
289 | }; | |
290 | ||
291 | priv = malloc(sizeof(struct ftpci100_data)); | |
292 | ||
293 | if (!priv) { | |
294 | printf("%s(): failed to malloc priv\n", __func__); | |
295 | return; | |
296 | } | |
297 | ||
298 | memset(priv, 0, sizeof(struct ftpci100_data)); | |
299 | ||
300 | ftpci_preinit(priv); | |
301 | ||
302 | debug("Device bus dev func deviceID vendorID pin address" \ | |
303 | " size class\n"); | |
304 | ||
305 | pci_bus_scan(priv); | |
306 | ||
307 | /* | |
308 | * Setup the PCI Bridge Window to 1GB, | |
309 | * it will cause USB OHCI Host controller Unrecoverable Error | |
310 | * if it is not set. | |
311 | */ | |
312 | bridge_num = pci_find_devices(bridge_ids, 0); | |
313 | if (bridge_num == -1) { | |
314 | printf("PCI Bridge not found\n"); | |
315 | return; | |
316 | } | |
317 | pci_hose_write_config_dword(hose, bridge_num, PCI_MEM_BASE_SIZE1, | |
318 | FTPCI100_BASE_ADR_SIZE(1024)); | |
319 | } |