Rev 1029 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1029 | serge | 1 | |
2 | { |
||
3 | link_t link; |
||
4 | int capndx; |
||
5 | u32_t maxbw; |
||
6 | PCITAG tag; |
||
7 | }; |
||
8 | |||
9 | |||
10 | { |
||
11 | new->prev = old; |
||
12 | new->next = old->next; |
||
13 | new->next->prev = new; |
||
14 | old->next = new; |
||
15 | } |
||
16 | |||
17 | |||
18 | { |
||
19 | struct agp_3_5_dev *cur, *n = (struct agp_3_5_dev*)new; |
||
20 | link_t *pos = head->next; |
||
21 | |||
22 | |||
23 | cur = (struct agp_3_5_dev*)pos; |
||
24 | if(cur->maxbw > n->maxbw) |
||
25 | break; |
||
26 | } |
||
27 | list_insert_tail(new, pos); |
||
28 | } |
||
29 | |||
30 | |||
31 | { |
||
32 | struct agp_3_5_dev *cur; |
||
33 | link_t *pos, *tmp, *start = list->next; |
||
34 | u32_t nistat; |
||
35 | |||
36 | |||
37 | |||
38 | |||
39 | { |
||
40 | PCITAG tag; |
||
41 | |||
42 | |||
43 | tag = cur->tag; |
||
44 | |||
45 | |||
46 | cur->maxbw = (nistat >> 16) & 0xff; |
||
47 | |||
48 | |||
49 | pos = pos->next; |
||
50 | agp_3_5_dev_list_insert(list, tmp); |
||
51 | } |
||
52 | } |
||
53 | |||
54 | |||
55 | |||
56 | * Initialize all isochronous transfer parameters for an AGP 3.0 |
||
57 | * node (i.e. a host bridge in combination with the adapters |
||
58 | * lying behind it...) |
||
59 | */ |
||
60 | |||
61 | |||
62 | link_t *dev_list, unsigned int ndevs) |
||
63 | { |
||
64 | /* |
||
65 | * Convenience structure to make the calculations clearer |
||
66 | * here. The field names come straight from the AGP 3.0 spec. |
||
67 | */ |
||
68 | struct isoch_data { |
||
69 | u32_t maxbw; |
||
70 | u32_t n; |
||
71 | u32_t y; |
||
72 | u32_t l; |
||
73 | u32_t rq; |
||
74 | struct agp_3_5_dev *dev; |
||
75 | }; |
||
76 | |||
77 | |||
78 | |||
79 | |||
80 | struct agp_3_5_dev *cur; |
||
81 | struct isoch_data *master, target; |
||
82 | unsigned int cdev = 0; |
||
83 | u32_t mnistat, tnistat, tstatus, mcmd; |
||
84 | u16_t tnicmd, mnicmd; |
||
85 | u8_t mcapndx; |
||
86 | u32_t tot_bw = 0, tot_n = 0, tot_rq = 0, y_max, rq_isoch, rq_async; |
||
87 | u32_t step, rem, rem_isoch, rem_async; |
||
88 | int ret = 0; |
||
89 | |||
90 | |||
91 | * We'll work with an array of isoch_data's (one for each |
||
92 | * device in dev_list) throughout this function. |
||
93 | */ |
||
94 | if ((master = malloc(ndevs * sizeof(*master))) == NULL) { |
||
95 | ret = -1; |
||
96 | goto get_out; |
||
97 | } |
||
98 | |||
99 | |||
100 | * Sort the device list by maxbw. We need to do this because the |
||
101 | * spec suggests that the devices with the smallest requirements |
||
102 | * have their resources allocated first, with all remaining resources |
||
103 | * falling to the device with the largest requirement. |
||
104 | * |
||
105 | * We don't exactly do this, we divide target resources by ndevs |
||
106 | * and split them amongst the AGP 3.0 devices. The remainder of such |
||
107 | * division operations are dropped on the last device, sort of like |
||
108 | * the spec mentions it should be done. |
||
109 | * |
||
110 | * We can't do this sort when we initially construct the dev_list |
||
111 | * because we don't know until this function whether isochronous |
||
112 | * transfers are enabled and consequently whether maxbw will mean |
||
113 | * anything. |
||
114 | */ |
||
115 | agp_3_5_dev_list_sort(dev_list, ndevs); |
||
116 | |||
117 | |||
118 | tstatus = pciReadLong(td, bridge->capndx+AGPSTAT); |
||
119 | |||
120 | |||
121 | target.maxbw = (tnistat >> 16) & 0xff; |
||
122 | target.n = (tnistat >> 8) & 0xff; |
||
123 | target.y = (tnistat >> 6) & 0x3; |
||
124 | target.l = (tnistat >> 3) & 0x7; |
||
125 | target.rq = (tstatus >> 24) & 0xff; |
||
126 | |||
127 | |||
128 | |||
129 | |||
130 | * Extract power-on defaults for each device in dev_list. Along |
||
131 | * the way, calculate the total isochronous bandwidth required |
||
132 | * by these devices and the largest requested payload size. |
||
133 | */ |
||
134 | |||
135 | |||
136 | |||
137 | |||
138 | { |
||
139 | PCITAG dev; |
||
140 | |||
141 | |||
142 | dev = cur->tag; |
||
143 | |||
144 | |||
145 | |||
146 | |||
147 | |||
148 | |||
149 | master[cdev].n = (mnistat >> 8) & 0xff; |
||
150 | master[cdev].y = (mnistat >> 6) & 0x3; |
||
151 | master[cdev].dev = cur; |
||
152 | |||
153 | |||
154 | y_max = max(y_max, master[cdev].y); |
||
155 | |||
156 | |||
157 | } |
||
158 | |||
159 | |||
160 | if (tot_bw > target.maxbw) { |
||
161 | dbgprintf("isochronous bandwidth required " |
||
162 | "by AGP 3.0 devices exceeds that which is supported by " |
||
163 | "the AGP 3.0 bridge!\n"); |
||
164 | ret = -1; |
||
165 | goto free_and_exit; |
||
166 | } |
||
167 | |||
168 | |||
169 | |||
170 | |||
171 | * Write the calculated payload size into the target's NICMD |
||
172 | * register. Doing this directly effects the ISOCH_N value |
||
173 | * in the target's NISTAT register, so we need to do this now |
||
174 | * to get an accurate value for ISOCH_N later. |
||
175 | */ |
||
176 | tnicmd = pciReadWord(td, bridge->capndx+AGPNICMD); |
||
177 | tnicmd &= ~(0x3 << 6); |
||
178 | tnicmd |= target.y << 6; |
||
179 | pciWriteWord(td, bridge->capndx+AGPNICMD, tnicmd); |
||
180 | |||
181 | |||
182 | tnistat = pciReadLong(td, bridge->capndx+AGPNISTAT); |
||
183 | target.n = (tnistat >> 8) & 0xff; |
||
184 | |||
185 | |||
186 | for (cdev=0; cdev |
||
187 | master[cdev].y = target.y; |
||
188 | master[cdev].n = master[cdev].maxbw / (master[cdev].y + 1); |
||
189 | |||
190 | |||
191 | } |
||
192 | |||
193 | |||
194 | * than the target can handle. */ |
||
195 | if (tot_n > target.n) { |
||
196 | dbgprintf("number of isochronous " |
||
197 | "transactions per period required by AGP 3.0 devices " |
||
198 | "exceeds that which is supported by the AGP 3.0 " |
||
199 | "bridge!\n"); |
||
200 | ret = -1; |
||
201 | goto free_and_exit; |
||
202 | } |
||
203 | |||
204 | |||
205 | * this to the hungriest device (as per the spec) */ |
||
206 | rem = target.n - tot_n; |
||
207 | |||
208 | |||
209 | * Calculate the minimum isochronous RQ depth needed by each master. |
||
210 | * Along the way, distribute the extra ISOCH_N capability calculated |
||
211 | * above. |
||
212 | */ |
||
213 | for (cdev=0; cdev |
||
214 | /* |
||
215 | * This is a little subtle. If ISOCH_Y > 64B, then ISOCH_Y |
||
216 | * byte isochronous writes will be broken into 64B pieces. |
||
217 | * This means we need to budget more RQ depth to account for |
||
218 | * these kind of writes (each isochronous write is actually |
||
219 | * many writes on the AGP bus). |
||
220 | */ |
||
221 | master[cdev].rq = master[cdev].n; |
||
222 | if(master[cdev].y > 0x1) |
||
223 | master[cdev].rq *= (1 << (master[cdev].y - 1)); |
||
224 | |||
225 | |||
226 | } |
||
227 | master[ndevs-1].n += rem; |
||
228 | |||
229 | |||
230 | * target is providing. */ |
||
231 | rq_isoch = (target.y > 0x1) ? target.n * (1 << (target.y - 1)) : target.n; |
||
232 | rq_async = target.rq - rq_isoch; |
||
233 | |||
234 | |||
235 | * can provide. */ |
||
236 | if (tot_rq > rq_isoch) { |
||
237 | dbgprintf("number of request queue slots " |
||
238 | "required by the isochronous bandwidth requested by " |
||
239 | "AGP 3.0 devices exceeds the number provided by the " |
||
240 | "AGP 3.0 bridge!\n"); |
||
241 | ret = -1; |
||
242 | goto free_and_exit; |
||
243 | } |
||
244 | |||
245 | |||
246 | * well as the total number of leftover isochronous RQ slots. */ |
||
247 | step = rq_async / ndevs; |
||
248 | rem_async = step + (rq_async % ndevs); |
||
249 | rem_isoch = rq_isoch - tot_rq; |
||
250 | |||
251 | |||
252 | * isochronous settings out to the actual devices. */ |
||
253 | for (cdev=0; cdev |
||
254 | { |
||
255 | PCITAG dev; |
||
256 | |||
257 | |||
258 | dev = cur->tag; |
||
259 | |||
260 | |||
261 | |||
262 | |||
263 | ? (rem_async + rem_isoch) : step; |
||
264 | |||
265 | |||
266 | mcmd = pciReadLong(dev, cur->capndx+AGPCMD); |
||
267 | |||
268 | |||
269 | mnicmd &= ~(0x3 << 6); |
||
270 | mcmd &= ~(0xff << 24); |
||
271 | |||
272 | |||
273 | mnicmd |= master[cdev].y << 6; |
||
274 | mcmd |= master[cdev].rq << 24; |
||
275 | |||
276 | |||
277 | pciWriteWord(dev, cur->capndx+AGPNICMD, mnicmd); |
||
278 | } |
||
279 | |||
280 | |||
281 | free(master); |
||
282 | |||
283 | |||
284 | return ret; |
||
285 | } |
||
286 | |||
287 | |||
288 | |||
289 | * This function basically allocates request queue slots among the |
||
290 | * AGP 3.0 systems in nonisochronous nodes. The algorithm is |
||
291 | * pretty stupid, divide the total number of RQ slots provided by the |
||
292 | * target by ndevs. Distribute this many slots to each AGP 3.0 device, |
||
293 | * giving any left over slots to the last device in dev_list. |
||
294 | */ |
||
295 | static void agp_3_5_nonisochronous_node_enable(agp_t *bridge, |
||
296 | link_t *dev_list, unsigned int ndevs) |
||
297 | { |
||
298 | struct agp_3_5_dev *cur; |
||
299 | u32_t tstatus, mcmd; |
||
300 | u32_t trq, mrq, rem; |
||
301 | unsigned int cdev = 0; |
||
302 | |||
303 | |||
304 | |||
305 | |||
306 | mrq = trq / ndevs; |
||
307 | |||
308 | |||
309 | |||
310 | |||
311 | |||
312 | |||
313 | cur = (struct agp_3_5_dev*)pos; |
||
314 | |||
315 | |||
316 | mcmd &= ~(0xff << 24); |
||
317 | mcmd |= ((cdev == ndevs - 1) ? rem : mrq) << 24; |
||
318 | pciWriteLong(cur->tag, cur->capndx+AGPCMD, mcmd); |
||
319 | } |
||
320 | } |
||
321 | |||
322 | |||
323 | |||
324 | |||
325 | * Fully configure and enable an AGP 3.0 host bridge and all the devices |
||
326 | * lying behind it. |
||
327 | */ |
||
328 | int agp_3_5_enable(agp_t *bridge) |
||
329 | { |
||
330 | u8_t mcapndx; |
||
331 | u32_t isoch, arqsz; |
||
332 | u32_t tstatus, mstatus, ncapid; |
||
333 | u32_t mmajor; |
||
334 | u16_t mpstat; |
||
335 | |||
336 | |||
337 | |||
338 | |||
339 | |||
340 | |||
341 | PCITAG dev = 0; |
||
342 | int ret = 0; |
||
343 | |||
344 | |||
345 | tstatus = pciReadLong(bridge->PciTag, bridge->capndx+AGPSTAT); |
||
346 | isoch = (tstatus >> 17) & 0x1; |
||
347 | if (isoch == 0) /* isoch xfers not available, bail out. */ |
||
348 | return -1; |
||
349 | |||
350 | |||
351 | |||
352 | |||
353 | |||
354 | |||
355 | for_each_pci_dev(dev) |
||
356 | { |
||
357 | u16_t devclass; |
||
358 | |||
359 | |||
360 | if (mcapndx == 0) |
||
361 | continue; |
||
362 | |||
363 | |||
364 | |||
365 | |||
366 | { |
||
367 | case 0x0600: /* Bridge */ |
||
368 | /* Skip bridges. We should call this function for each one. */ |
||
369 | continue; |
||
370 | |||
371 | |||
372 | /* Don't know what this is, but log it for investigation. */ |
||
373 | if (mcapndx != 0) { |
||
374 | dbgprintf("Wacky, found unclassified AGP device.\n"); |
||
375 | } |
||
376 | continue; |
||
377 | |||
378 | |||
379 | case 0x0400: /* Multimedia controller */ |
||
380 | if((cur = malloc(sizeof(*cur))) == NULL) |
||
381 | { |
||
382 | ret = -1; |
||
383 | goto free_and_exit; |
||
384 | } |
||
385 | cur->tag = dev; |
||
386 | list_prepend(&cur->link, &dev_list); |
||
387 | ndevs++; |
||
388 | continue; |
||
389 | |||
390 | |||
391 | continue; |
||
392 | } |
||
393 | } |
||
394 | |||
395 | |||
396 | * Take an initial pass through the devices lying behind our host |
||
397 | * bridge. Make sure each one is actually an AGP 3.0 device, otherwise |
||
398 | * exit with an error message. Along the way store the AGP 3.0 |
||
399 | * cap_ptr for each device |
||
400 | */ |
||
401 | |||
402 | |||
403 | |||
404 | |||
405 | { |
||
406 | dev = cur->tag; |
||
407 | |||
408 | |||
409 | if ((mpstat & PCI_STATUS_CAP_LIST) == 0) |
||
410 | continue; |
||
411 | |||
412 | |||
413 | if (mcapndx != 0) { |
||
414 | do { |
||
415 | ncapid = pciReadLong(dev, mcapndx); |
||
416 | if ((ncapid & 0xff) != 2) |
||
417 | mcapndx = (ncapid >> 8) & 0xff; |
||
418 | } |
||
419 | while (((ncapid & 0xff) != 2) && (mcapndx != 0)); |
||
420 | } |
||
421 | |||
422 | |||
423 | dbgprintf("woah! Non-AGP device " |
||
424 | "found on the secondary bus of an AGP 3.5 bridge!\n"); |
||
425 | ret = -1; |
||
426 | goto free_and_exit; |
||
427 | } |
||
428 | |||
429 | |||
430 | if (mmajor < 3) { |
||
431 | dbgprintf("woah! AGP 2.0 device " |
||
432 | "found on the secondary bus of an AGP 3.5 " |
||
433 | "bridge operating with AGP 3.0 electricals!\n"); |
||
434 | ret = -1; |
||
435 | goto free_and_exit; |
||
436 | } |
||
437 | |||
438 | |||
439 | |||
440 | |||
441 | |||
442 | |||
443 | dbgprintf("woah! AGP 3.x device " |
||
444 | "not operating in AGP 3.x mode found on the " |
||
445 | "secondary bus of an AGP 3.5 bridge operating " |
||
446 | "with AGP 3.0 electricals!\n"); |
||
447 | ret = -1; |
||
448 | goto free_and_exit; |
||
449 | } |
||
450 | cur = (struct agp_3_5_dev*)cur->link.next; |
||
451 | } |
||
452 | |||
453 | |||
454 | * Call functions to divide target resources amongst the AGP 3.0 |
||
455 | * masters. This process is dramatically different depending on |
||
456 | * whether isochronous transfers are supported. |
||
457 | */ |
||
458 | if (isoch) { |
||
459 | ret = agp_3_5_isochronous_node_enable(bridge, &dev_list, ndevs); |
||
460 | if (ret) { |
||
461 | dbgprintf("Something bad happened setting " |
||
462 | "up isochronous xfers. Falling back to " |
||
463 | "non-isochronous xfer mode.\n"); |
||
464 | } else { |
||
465 | goto free_and_exit; |
||
466 | } |
||
467 | } |
||
468 | agp_3_5_nonisochronous_node_enable(bridge, &dev_list, ndevs); |
||
469 | |||
470 | |||
471 | /* Be sure to free the dev_list */ |
||
472 | for (pos = (struct agp_3_5_dev*)dev_list.next; &pos->link != &dev_list; ) |
||
473 | { |
||
474 | cur = pos; |
||
475 | |||
476 | |||
477 | free(cur); |
||
478 | } |
||
479 | |||
480 | |||
481 | return ret; |
||
482 | }>><>><>><>><>><>><>><>><>><>><>><>><> |
||
483 |