diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/agp/isoch.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/agp/isoch.c')
-rw-r--r-- | drivers/char/agp/isoch.c | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/drivers/char/agp/isoch.c b/drivers/char/agp/isoch.c new file mode 100644 index 000000000000..c9ac731504f2 --- /dev/null +++ b/drivers/char/agp/isoch.c | |||
@@ -0,0 +1,470 @@ | |||
1 | /* | ||
2 | * Setup routines for AGP 3.5 compliant bridges. | ||
3 | */ | ||
4 | |||
5 | #include <linux/list.h> | ||
6 | #include <linux/pci.h> | ||
7 | #include <linux/agp_backend.h> | ||
8 | #include <linux/module.h> | ||
9 | |||
10 | #include "agp.h" | ||
11 | |||
12 | /* Generic AGP 3.5 enabling routines */ | ||
13 | |||
14 | struct agp_3_5_dev { | ||
15 | struct list_head list; | ||
16 | u8 capndx; | ||
17 | u32 maxbw; | ||
18 | struct pci_dev *dev; | ||
19 | }; | ||
20 | |||
21 | static void agp_3_5_dev_list_insert(struct list_head *head, struct list_head *new) | ||
22 | { | ||
23 | struct agp_3_5_dev *cur, *n = list_entry(new, struct agp_3_5_dev, list); | ||
24 | struct list_head *pos; | ||
25 | |||
26 | list_for_each(pos, head) { | ||
27 | cur = list_entry(pos, struct agp_3_5_dev, list); | ||
28 | if(cur->maxbw > n->maxbw) | ||
29 | break; | ||
30 | } | ||
31 | list_add_tail(new, pos); | ||
32 | } | ||
33 | |||
34 | static void agp_3_5_dev_list_sort(struct agp_3_5_dev *list, unsigned int ndevs) | ||
35 | { | ||
36 | struct agp_3_5_dev *cur; | ||
37 | struct pci_dev *dev; | ||
38 | struct list_head *pos, *tmp, *head = &list->list, *start = head->next; | ||
39 | u32 nistat; | ||
40 | |||
41 | INIT_LIST_HEAD(head); | ||
42 | |||
43 | for (pos=start; pos!=head; ) { | ||
44 | cur = list_entry(pos, struct agp_3_5_dev, list); | ||
45 | dev = cur->dev; | ||
46 | |||
47 | pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &nistat); | ||
48 | cur->maxbw = (nistat >> 16) & 0xff; | ||
49 | |||
50 | tmp = pos; | ||
51 | pos = pos->next; | ||
52 | agp_3_5_dev_list_insert(head, tmp); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Initialize all isochronous transfer parameters for an AGP 3.0 | ||
58 | * node (i.e. a host bridge in combination with the adapters | ||
59 | * lying behind it...) | ||
60 | */ | ||
61 | |||
62 | static int agp_3_5_isochronous_node_enable(struct agp_bridge_data *bridge, | ||
63 | struct agp_3_5_dev *dev_list, unsigned int ndevs) | ||
64 | { | ||
65 | /* | ||
66 | * Convenience structure to make the calculations clearer | ||
67 | * here. The field names come straight from the AGP 3.0 spec. | ||
68 | */ | ||
69 | struct isoch_data { | ||
70 | u32 maxbw; | ||
71 | u32 n; | ||
72 | u32 y; | ||
73 | u32 l; | ||
74 | u32 rq; | ||
75 | struct agp_3_5_dev *dev; | ||
76 | }; | ||
77 | |||
78 | struct pci_dev *td = bridge->dev, *dev; | ||
79 | struct list_head *head = &dev_list->list, *pos; | ||
80 | struct agp_3_5_dev *cur; | ||
81 | struct isoch_data *master, target; | ||
82 | unsigned int cdev = 0; | ||
83 | u32 mnistat, tnistat, tstatus, mcmd; | ||
84 | u16 tnicmd, mnicmd; | ||
85 | u8 mcapndx; | ||
86 | u32 tot_bw = 0, tot_n = 0, tot_rq = 0, y_max, rq_isoch, rq_async; | ||
87 | u32 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 = kmalloc(ndevs * sizeof(*master), GFP_KERNEL)) == NULL) { | ||
95 | ret = -ENOMEM; | ||
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 | pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat); | ||
118 | pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus); | ||
119 | |||
120 | /* Extract power-on defaults from the target */ | ||
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 | y_max = target.y; | ||
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 | list_for_each(pos, head) { | ||
135 | cur = list_entry(pos, struct agp_3_5_dev, list); | ||
136 | dev = cur->dev; | ||
137 | |||
138 | mcapndx = cur->capndx; | ||
139 | |||
140 | pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &mnistat); | ||
141 | |||
142 | master[cdev].maxbw = (mnistat >> 16) & 0xff; | ||
143 | master[cdev].n = (mnistat >> 8) & 0xff; | ||
144 | master[cdev].y = (mnistat >> 6) & 0x3; | ||
145 | master[cdev].dev = cur; | ||
146 | |||
147 | tot_bw += master[cdev].maxbw; | ||
148 | y_max = max(y_max, master[cdev].y); | ||
149 | |||
150 | cdev++; | ||
151 | } | ||
152 | |||
153 | /* Check if this configuration has any chance of working */ | ||
154 | if (tot_bw > target.maxbw) { | ||
155 | printk(KERN_ERR PFX "isochronous bandwidth required " | ||
156 | "by AGP 3.0 devices exceeds that which is supported by " | ||
157 | "the AGP 3.0 bridge!\n"); | ||
158 | ret = -ENODEV; | ||
159 | goto free_and_exit; | ||
160 | } | ||
161 | |||
162 | target.y = y_max; | ||
163 | |||
164 | /* | ||
165 | * Write the calculated payload size into the target's NICMD | ||
166 | * register. Doing this directly effects the ISOCH_N value | ||
167 | * in the target's NISTAT register, so we need to do this now | ||
168 | * to get an accurate value for ISOCH_N later. | ||
169 | */ | ||
170 | pci_read_config_word(td, bridge->capndx+AGPNICMD, &tnicmd); | ||
171 | tnicmd &= ~(0x3 << 6); | ||
172 | tnicmd |= target.y << 6; | ||
173 | pci_write_config_word(td, bridge->capndx+AGPNICMD, tnicmd); | ||
174 | |||
175 | /* Reread the target's ISOCH_N */ | ||
176 | pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat); | ||
177 | target.n = (tnistat >> 8) & 0xff; | ||
178 | |||
179 | /* Calculate the minimum ISOCH_N needed by each master */ | ||
180 | for (cdev=0; cdev<ndevs; cdev++) { | ||
181 | master[cdev].y = target.y; | ||
182 | master[cdev].n = master[cdev].maxbw / (master[cdev].y + 1); | ||
183 | |||
184 | tot_n += master[cdev].n; | ||
185 | } | ||
186 | |||
187 | /* Exit if the minimal ISOCH_N allocation among the masters is more | ||
188 | * than the target can handle. */ | ||
189 | if (tot_n > target.n) { | ||
190 | printk(KERN_ERR PFX "number of isochronous " | ||
191 | "transactions per period required by AGP 3.0 devices " | ||
192 | "exceeds that which is supported by the AGP 3.0 " | ||
193 | "bridge!\n"); | ||
194 | ret = -ENODEV; | ||
195 | goto free_and_exit; | ||
196 | } | ||
197 | |||
198 | /* Calculate left over ISOCH_N capability in the target. We'll give | ||
199 | * this to the hungriest device (as per the spec) */ | ||
200 | rem = target.n - tot_n; | ||
201 | |||
202 | /* | ||
203 | * Calculate the minimum isochronous RQ depth needed by each master. | ||
204 | * Along the way, distribute the extra ISOCH_N capability calculated | ||
205 | * above. | ||
206 | */ | ||
207 | for (cdev=0; cdev<ndevs; cdev++) { | ||
208 | /* | ||
209 | * This is a little subtle. If ISOCH_Y > 64B, then ISOCH_Y | ||
210 | * byte isochronous writes will be broken into 64B pieces. | ||
211 | * This means we need to budget more RQ depth to account for | ||
212 | * these kind of writes (each isochronous write is actually | ||
213 | * many writes on the AGP bus). | ||
214 | */ | ||
215 | master[cdev].rq = master[cdev].n; | ||
216 | if(master[cdev].y > 0x1) | ||
217 | master[cdev].rq *= (1 << (master[cdev].y - 1)); | ||
218 | |||
219 | tot_rq += master[cdev].rq; | ||
220 | |||
221 | if (cdev == ndevs-1) | ||
222 | master[cdev].n += rem; | ||
223 | } | ||
224 | |||
225 | /* Figure the number of isochronous and asynchronous RQ slots the | ||
226 | * target is providing. */ | ||
227 | rq_isoch = (target.y > 0x1) ? target.n * (1 << (target.y - 1)) : target.n; | ||
228 | rq_async = target.rq - rq_isoch; | ||
229 | |||
230 | /* Exit if the minimal RQ needs of the masters exceeds what the target | ||
231 | * can provide. */ | ||
232 | if (tot_rq > rq_isoch) { | ||
233 | printk(KERN_ERR PFX "number of request queue slots " | ||
234 | "required by the isochronous bandwidth requested by " | ||
235 | "AGP 3.0 devices exceeds the number provided by the " | ||
236 | "AGP 3.0 bridge!\n"); | ||
237 | ret = -ENODEV; | ||
238 | goto free_and_exit; | ||
239 | } | ||
240 | |||
241 | /* Calculate asynchronous RQ capability in the target (per master) as | ||
242 | * well as the total number of leftover isochronous RQ slots. */ | ||
243 | step = rq_async / ndevs; | ||
244 | rem_async = step + (rq_async % ndevs); | ||
245 | rem_isoch = rq_isoch - tot_rq; | ||
246 | |||
247 | /* Distribute the extra RQ slots calculated above and write our | ||
248 | * isochronous settings out to the actual devices. */ | ||
249 | for (cdev=0; cdev<ndevs; cdev++) { | ||
250 | cur = master[cdev].dev; | ||
251 | dev = cur->dev; | ||
252 | |||
253 | mcapndx = cur->capndx; | ||
254 | |||
255 | master[cdev].rq += (cdev == ndevs - 1) | ||
256 | ? (rem_async + rem_isoch) : step; | ||
257 | |||
258 | pci_read_config_word(dev, cur->capndx+AGPNICMD, &mnicmd); | ||
259 | pci_read_config_dword(dev, cur->capndx+AGPCMD, &mcmd); | ||
260 | |||
261 | mnicmd &= ~(0xff << 8); | ||
262 | mnicmd &= ~(0x3 << 6); | ||
263 | mcmd &= ~(0xff << 24); | ||
264 | |||
265 | mnicmd |= master[cdev].n << 8; | ||
266 | mnicmd |= master[cdev].y << 6; | ||
267 | mcmd |= master[cdev].rq << 24; | ||
268 | |||
269 | pci_write_config_dword(dev, cur->capndx+AGPCMD, mcmd); | ||
270 | pci_write_config_word(dev, cur->capndx+AGPNICMD, mnicmd); | ||
271 | } | ||
272 | |||
273 | free_and_exit: | ||
274 | kfree(master); | ||
275 | |||
276 | get_out: | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * This function basically allocates request queue slots among the | ||
282 | * AGP 3.0 systems in nonisochronous nodes. The algorithm is | ||
283 | * pretty stupid, divide the total number of RQ slots provided by the | ||
284 | * target by ndevs. Distribute this many slots to each AGP 3.0 device, | ||
285 | * giving any left over slots to the last device in dev_list. | ||
286 | */ | ||
287 | static void agp_3_5_nonisochronous_node_enable(struct agp_bridge_data *bridge, | ||
288 | struct agp_3_5_dev *dev_list, unsigned int ndevs) | ||
289 | { | ||
290 | struct agp_3_5_dev *cur; | ||
291 | struct list_head *head = &dev_list->list, *pos; | ||
292 | u32 tstatus, mcmd; | ||
293 | u32 trq, mrq, rem; | ||
294 | unsigned int cdev = 0; | ||
295 | |||
296 | pci_read_config_dword(bridge->dev, bridge->capndx+AGPSTAT, &tstatus); | ||
297 | |||
298 | trq = (tstatus >> 24) & 0xff; | ||
299 | mrq = trq / ndevs; | ||
300 | |||
301 | rem = mrq + (trq % ndevs); | ||
302 | |||
303 | for (pos=head->next; cdev<ndevs; cdev++, pos=pos->next) { | ||
304 | cur = list_entry(pos, struct agp_3_5_dev, list); | ||
305 | |||
306 | pci_read_config_dword(cur->dev, cur->capndx+AGPCMD, &mcmd); | ||
307 | mcmd &= ~(0xff << 24); | ||
308 | mcmd |= ((cdev == ndevs - 1) ? rem : mrq) << 24; | ||
309 | pci_write_config_dword(cur->dev, cur->capndx+AGPCMD, mcmd); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * Fully configure and enable an AGP 3.0 host bridge and all the devices | ||
315 | * lying behind it. | ||
316 | */ | ||
317 | int agp_3_5_enable(struct agp_bridge_data *bridge) | ||
318 | { | ||
319 | struct pci_dev *td = bridge->dev, *dev = NULL; | ||
320 | u8 mcapndx; | ||
321 | u32 isoch, arqsz; | ||
322 | u32 tstatus, mstatus, ncapid; | ||
323 | u32 mmajor; | ||
324 | u16 mpstat; | ||
325 | struct agp_3_5_dev *dev_list, *cur; | ||
326 | struct list_head *head, *pos; | ||
327 | unsigned int ndevs = 0; | ||
328 | int ret = 0; | ||
329 | |||
330 | /* Extract some power-on defaults from the target */ | ||
331 | pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus); | ||
332 | isoch = (tstatus >> 17) & 0x1; | ||
333 | if (isoch == 0) /* isoch xfers not available, bail out. */ | ||
334 | return -ENODEV; | ||
335 | |||
336 | arqsz = (tstatus >> 13) & 0x7; | ||
337 | |||
338 | /* | ||
339 | * Allocate a head for our AGP 3.5 device list | ||
340 | * (multiple AGP v3 devices are allowed behind a single bridge). | ||
341 | */ | ||
342 | if ((dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL)) == NULL) { | ||
343 | ret = -ENOMEM; | ||
344 | goto get_out; | ||
345 | } | ||
346 | head = &dev_list->list; | ||
347 | INIT_LIST_HEAD(head); | ||
348 | |||
349 | /* Find all AGP devices, and add them to dev_list. */ | ||
350 | for_each_pci_dev(dev) { | ||
351 | mcapndx = pci_find_capability(dev, PCI_CAP_ID_AGP); | ||
352 | if (mcapndx == 0) | ||
353 | continue; | ||
354 | |||
355 | switch ((dev->class >>8) & 0xff00) { | ||
356 | case 0x0600: /* Bridge */ | ||
357 | /* Skip bridges. We should call this function for each one. */ | ||
358 | continue; | ||
359 | |||
360 | case 0x0001: /* Unclassified device */ | ||
361 | /* Don't know what this is, but log it for investigation. */ | ||
362 | if (mcapndx != 0) { | ||
363 | printk (KERN_INFO PFX "Wacky, found unclassified AGP device. %x:%x\n", | ||
364 | dev->vendor, dev->device); | ||
365 | } | ||
366 | continue; | ||
367 | |||
368 | case 0x0300: /* Display controller */ | ||
369 | case 0x0400: /* Multimedia controller */ | ||
370 | if((cur = kmalloc(sizeof(*cur), GFP_KERNEL)) == NULL) { | ||
371 | ret = -ENOMEM; | ||
372 | goto free_and_exit; | ||
373 | } | ||
374 | cur->dev = dev; | ||
375 | |||
376 | pos = &cur->list; | ||
377 | list_add(pos, head); | ||
378 | ndevs++; | ||
379 | continue; | ||
380 | |||
381 | default: | ||
382 | continue; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Take an initial pass through the devices lying behind our host | ||
388 | * bridge. Make sure each one is actually an AGP 3.0 device, otherwise | ||
389 | * exit with an error message. Along the way store the AGP 3.0 | ||
390 | * cap_ptr for each device | ||
391 | */ | ||
392 | list_for_each(pos, head) { | ||
393 | cur = list_entry(pos, struct agp_3_5_dev, list); | ||
394 | dev = cur->dev; | ||
395 | |||
396 | pci_read_config_word(dev, PCI_STATUS, &mpstat); | ||
397 | if ((mpstat & PCI_STATUS_CAP_LIST) == 0) | ||
398 | continue; | ||
399 | |||
400 | pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &mcapndx); | ||
401 | if (mcapndx != 0) { | ||
402 | do { | ||
403 | pci_read_config_dword(dev, mcapndx, &ncapid); | ||
404 | if ((ncapid & 0xff) != 2) | ||
405 | mcapndx = (ncapid >> 8) & 0xff; | ||
406 | } | ||
407 | while (((ncapid & 0xff) != 2) && (mcapndx != 0)); | ||
408 | } | ||
409 | |||
410 | if (mcapndx == 0) { | ||
411 | printk(KERN_ERR PFX "woah! Non-AGP device " | ||
412 | "found on the secondary bus of an AGP 3.5 bridge!\n"); | ||
413 | ret = -ENODEV; | ||
414 | goto free_and_exit; | ||
415 | } | ||
416 | |||
417 | mmajor = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; | ||
418 | if (mmajor < 3) { | ||
419 | printk(KERN_ERR PFX "woah! AGP 2.0 device " | ||
420 | "found on the secondary bus of an AGP 3.5 " | ||
421 | "bridge operating with AGP 3.0 electricals!\n"); | ||
422 | ret = -ENODEV; | ||
423 | goto free_and_exit; | ||
424 | } | ||
425 | |||
426 | cur->capndx = mcapndx; | ||
427 | |||
428 | pci_read_config_dword(dev, cur->capndx+AGPSTAT, &mstatus); | ||
429 | |||
430 | if (((mstatus >> 3) & 0x1) == 0) { | ||
431 | printk(KERN_ERR PFX "woah! AGP 3.x device " | ||
432 | "not operating in AGP 3.x mode found on the " | ||
433 | "secondary bus of an AGP 3.5 bridge operating " | ||
434 | "with AGP 3.0 electricals!\n"); | ||
435 | ret = -ENODEV; | ||
436 | goto free_and_exit; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * Call functions to divide target resources amongst the AGP 3.0 | ||
442 | * masters. This process is dramatically different depending on | ||
443 | * whether isochronous transfers are supported. | ||
444 | */ | ||
445 | if (isoch) { | ||
446 | ret = agp_3_5_isochronous_node_enable(bridge, dev_list, ndevs); | ||
447 | if (ret) { | ||
448 | printk(KERN_INFO PFX "Something bad happened setting " | ||
449 | "up isochronous xfers. Falling back to " | ||
450 | "non-isochronous xfer mode.\n"); | ||
451 | } else { | ||
452 | goto free_and_exit; | ||
453 | } | ||
454 | } | ||
455 | agp_3_5_nonisochronous_node_enable(bridge, dev_list, ndevs); | ||
456 | |||
457 | free_and_exit: | ||
458 | /* Be sure to free the dev_list */ | ||
459 | for (pos=head->next; pos!=head; ) { | ||
460 | cur = list_entry(pos, struct agp_3_5_dev, list); | ||
461 | |||
462 | pos = pos->next; | ||
463 | kfree(cur); | ||
464 | } | ||
465 | kfree(dev_list); | ||
466 | |||
467 | get_out: | ||
468 | return ret; | ||
469 | } | ||
470 | |||