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