0001
0002
0003
0004
0005
0006 #include <linux/list.h>
0007 #include <linux/pci.h>
0008 #include <linux/agp_backend.h>
0009 #include <linux/module.h>
0010 #include <linux/slab.h>
0011
0012 #include "agp.h"
0013
0014
0015
0016 struct agp_3_5_dev {
0017 struct list_head list;
0018 u8 capndx;
0019 u32 maxbw;
0020 struct pci_dev *dev;
0021 };
0022
0023 static void agp_3_5_dev_list_insert(struct list_head *head, struct list_head *new)
0024 {
0025 struct agp_3_5_dev *cur, *n = list_entry(new, struct agp_3_5_dev, list);
0026 struct list_head *pos;
0027
0028 list_for_each(pos, head) {
0029 cur = list_entry(pos, struct agp_3_5_dev, list);
0030 if (cur->maxbw > n->maxbw)
0031 break;
0032 }
0033 list_add_tail(new, pos);
0034 }
0035
0036 static void agp_3_5_dev_list_sort(struct agp_3_5_dev *list, unsigned int ndevs)
0037 {
0038 struct agp_3_5_dev *cur;
0039 struct pci_dev *dev;
0040 struct list_head *pos, *tmp, *head = &list->list, *start = head->next;
0041 u32 nistat;
0042
0043 INIT_LIST_HEAD(head);
0044
0045 for (pos=start; pos!=head; ) {
0046 cur = list_entry(pos, struct agp_3_5_dev, list);
0047 dev = cur->dev;
0048
0049 pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &nistat);
0050 cur->maxbw = (nistat >> 16) & 0xff;
0051
0052 tmp = pos;
0053 pos = pos->next;
0054 agp_3_5_dev_list_insert(head, tmp);
0055 }
0056 }
0057
0058
0059
0060
0061
0062
0063
0064 static int agp_3_5_isochronous_node_enable(struct agp_bridge_data *bridge,
0065 struct agp_3_5_dev *dev_list, unsigned int ndevs)
0066 {
0067
0068
0069
0070
0071 struct isoch_data {
0072 u32 maxbw;
0073 u32 n;
0074 u32 y;
0075 u32 l;
0076 u32 rq;
0077 struct agp_3_5_dev *dev;
0078 };
0079
0080 struct pci_dev *td = bridge->dev, *dev;
0081 struct list_head *head = &dev_list->list, *pos;
0082 struct agp_3_5_dev *cur;
0083 struct isoch_data *master, target;
0084 unsigned int cdev = 0;
0085 u32 mnistat, tnistat, tstatus, mcmd;
0086 u16 tnicmd, mnicmd;
0087 u32 tot_bw = 0, tot_n = 0, tot_rq = 0, y_max, rq_isoch, rq_async;
0088 u32 step, rem, rem_isoch, rem_async;
0089 int ret = 0;
0090
0091
0092
0093
0094
0095 master = kmalloc_array(ndevs, sizeof(*master), GFP_KERNEL);
0096 if (master == NULL) {
0097 ret = -ENOMEM;
0098 goto get_out;
0099 }
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117 agp_3_5_dev_list_sort(dev_list, ndevs);
0118
0119 pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat);
0120 pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus);
0121
0122
0123 target.maxbw = (tnistat >> 16) & 0xff;
0124 target.n = (tnistat >> 8) & 0xff;
0125 target.y = (tnistat >> 6) & 0x3;
0126 target.l = (tnistat >> 3) & 0x7;
0127 target.rq = (tstatus >> 24) & 0xff;
0128
0129 y_max = target.y;
0130
0131
0132
0133
0134
0135
0136 list_for_each(pos, head) {
0137 cur = list_entry(pos, struct agp_3_5_dev, list);
0138 dev = cur->dev;
0139
0140 pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &mnistat);
0141
0142 master[cdev].maxbw = (mnistat >> 16) & 0xff;
0143 master[cdev].n = (mnistat >> 8) & 0xff;
0144 master[cdev].y = (mnistat >> 6) & 0x3;
0145 master[cdev].dev = cur;
0146
0147 tot_bw += master[cdev].maxbw;
0148 y_max = max(y_max, master[cdev].y);
0149
0150 cdev++;
0151 }
0152
0153
0154 if (tot_bw > target.maxbw) {
0155 dev_err(&td->dev, "isochronous bandwidth required "
0156 "by AGP 3.0 devices exceeds that which is supported by "
0157 "the AGP 3.0 bridge!\n");
0158 ret = -ENODEV;
0159 goto free_and_exit;
0160 }
0161
0162 target.y = y_max;
0163
0164
0165
0166
0167
0168
0169
0170 pci_read_config_word(td, bridge->capndx+AGPNICMD, &tnicmd);
0171 tnicmd &= ~(0x3 << 6);
0172 tnicmd |= target.y << 6;
0173 pci_write_config_word(td, bridge->capndx+AGPNICMD, tnicmd);
0174
0175
0176 pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat);
0177 target.n = (tnistat >> 8) & 0xff;
0178
0179
0180 for (cdev=0; cdev<ndevs; cdev++) {
0181 master[cdev].y = target.y;
0182 master[cdev].n = master[cdev].maxbw / (master[cdev].y + 1);
0183
0184 tot_n += master[cdev].n;
0185 }
0186
0187
0188
0189 if (tot_n > target.n) {
0190 dev_err(&td->dev, "number of isochronous "
0191 "transactions per period required by AGP 3.0 devices "
0192 "exceeds that which is supported by the AGP 3.0 "
0193 "bridge!\n");
0194 ret = -ENODEV;
0195 goto free_and_exit;
0196 }
0197
0198
0199
0200 rem = target.n - tot_n;
0201
0202
0203
0204
0205
0206
0207 for (cdev=0; cdev<ndevs; cdev++) {
0208
0209
0210
0211
0212
0213
0214
0215 master[cdev].rq = master[cdev].n;
0216 if (master[cdev].y > 0x1)
0217 master[cdev].rq *= (1 << (master[cdev].y - 1));
0218
0219 tot_rq += master[cdev].rq;
0220 }
0221 master[ndevs-1].n += rem;
0222
0223
0224
0225 rq_isoch = (target.y > 0x1) ? target.n * (1 << (target.y - 1)) : target.n;
0226 rq_async = target.rq - rq_isoch;
0227
0228
0229
0230 if (tot_rq > rq_isoch) {
0231 dev_err(&td->dev, "number of request queue slots "
0232 "required by the isochronous bandwidth requested by "
0233 "AGP 3.0 devices exceeds the number provided by the "
0234 "AGP 3.0 bridge!\n");
0235 ret = -ENODEV;
0236 goto free_and_exit;
0237 }
0238
0239
0240
0241 step = rq_async / ndevs;
0242 rem_async = step + (rq_async % ndevs);
0243 rem_isoch = rq_isoch - tot_rq;
0244
0245
0246
0247 for (cdev=0; cdev<ndevs; cdev++) {
0248 cur = master[cdev].dev;
0249 dev = cur->dev;
0250
0251 master[cdev].rq += (cdev == ndevs - 1)
0252 ? (rem_async + rem_isoch) : step;
0253
0254 pci_read_config_word(dev, cur->capndx+AGPNICMD, &mnicmd);
0255 pci_read_config_dword(dev, cur->capndx+AGPCMD, &mcmd);
0256
0257 mnicmd &= ~(0xff << 8);
0258 mnicmd &= ~(0x3 << 6);
0259 mcmd &= ~(0xff << 24);
0260
0261 mnicmd |= master[cdev].n << 8;
0262 mnicmd |= master[cdev].y << 6;
0263 mcmd |= master[cdev].rq << 24;
0264
0265 pci_write_config_dword(dev, cur->capndx+AGPCMD, mcmd);
0266 pci_write_config_word(dev, cur->capndx+AGPNICMD, mnicmd);
0267 }
0268
0269 free_and_exit:
0270 kfree(master);
0271
0272 get_out:
0273 return ret;
0274 }
0275
0276
0277
0278
0279
0280
0281
0282
0283 static void agp_3_5_nonisochronous_node_enable(struct agp_bridge_data *bridge,
0284 struct agp_3_5_dev *dev_list, unsigned int ndevs)
0285 {
0286 struct agp_3_5_dev *cur;
0287 struct list_head *head = &dev_list->list, *pos;
0288 u32 tstatus, mcmd;
0289 u32 trq, mrq, rem;
0290 unsigned int cdev = 0;
0291
0292 pci_read_config_dword(bridge->dev, bridge->capndx+AGPSTAT, &tstatus);
0293
0294 trq = (tstatus >> 24) & 0xff;
0295 mrq = trq / ndevs;
0296
0297 rem = mrq + (trq % ndevs);
0298
0299 for (pos=head->next; cdev<ndevs; cdev++, pos=pos->next) {
0300 cur = list_entry(pos, struct agp_3_5_dev, list);
0301
0302 pci_read_config_dword(cur->dev, cur->capndx+AGPCMD, &mcmd);
0303 mcmd &= ~(0xff << 24);
0304 mcmd |= ((cdev == ndevs - 1) ? rem : mrq) << 24;
0305 pci_write_config_dword(cur->dev, cur->capndx+AGPCMD, mcmd);
0306 }
0307 }
0308
0309
0310
0311
0312
0313 int agp_3_5_enable(struct agp_bridge_data *bridge)
0314 {
0315 struct pci_dev *td = bridge->dev, *dev = NULL;
0316 u8 mcapndx;
0317 u32 isoch;
0318 u32 tstatus, mstatus, ncapid;
0319 u32 mmajor;
0320 u16 mpstat;
0321 struct agp_3_5_dev *dev_list, *cur;
0322 struct list_head *head, *pos;
0323 unsigned int ndevs = 0;
0324 int ret = 0;
0325
0326
0327 pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus);
0328 isoch = (tstatus >> 17) & 0x1;
0329 if (isoch == 0)
0330 return -ENODEV;
0331
0332
0333
0334
0335
0336 if ((dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL)) == NULL) {
0337 ret = -ENOMEM;
0338 goto get_out;
0339 }
0340 head = &dev_list->list;
0341 INIT_LIST_HEAD(head);
0342
0343
0344 for_each_pci_dev(dev) {
0345 mcapndx = pci_find_capability(dev, PCI_CAP_ID_AGP);
0346 if (mcapndx == 0)
0347 continue;
0348
0349 switch ((dev->class >>8) & 0xff00) {
0350 case 0x0600:
0351
0352 continue;
0353
0354 case 0x0001:
0355
0356 if (mcapndx != 0) {
0357 dev_info(&td->dev, "wacky, found unclassified AGP device %s [%04x/%04x]\n",
0358 pci_name(dev),
0359 dev->vendor, dev->device);
0360 }
0361 continue;
0362
0363 case 0x0300:
0364 case 0x0400:
0365 if ((cur = kmalloc(sizeof(*cur), GFP_KERNEL)) == NULL) {
0366 ret = -ENOMEM;
0367 goto free_and_exit;
0368 }
0369 cur->dev = dev;
0370
0371 pos = &cur->list;
0372 list_add(pos, head);
0373 ndevs++;
0374 continue;
0375
0376 default:
0377 continue;
0378 }
0379 }
0380
0381
0382
0383
0384
0385
0386
0387 list_for_each(pos, head) {
0388 cur = list_entry(pos, struct agp_3_5_dev, list);
0389 dev = cur->dev;
0390
0391 pci_read_config_word(dev, PCI_STATUS, &mpstat);
0392 if ((mpstat & PCI_STATUS_CAP_LIST) == 0)
0393 continue;
0394
0395 pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &mcapndx);
0396 if (mcapndx != 0) {
0397 do {
0398 pci_read_config_dword(dev, mcapndx, &ncapid);
0399 if ((ncapid & 0xff) != 2)
0400 mcapndx = (ncapid >> 8) & 0xff;
0401 }
0402 while (((ncapid & 0xff) != 2) && (mcapndx != 0));
0403 }
0404
0405 if (mcapndx == 0) {
0406 dev_err(&td->dev, "woah! Non-AGP device %s on "
0407 "secondary bus of AGP 3.5 bridge!\n",
0408 pci_name(dev));
0409 ret = -ENODEV;
0410 goto free_and_exit;
0411 }
0412
0413 mmajor = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf;
0414 if (mmajor < 3) {
0415 dev_err(&td->dev, "woah! AGP 2.0 device %s on "
0416 "secondary bus of AGP 3.5 bridge operating "
0417 "with AGP 3.0 electricals!\n", pci_name(dev));
0418 ret = -ENODEV;
0419 goto free_and_exit;
0420 }
0421
0422 cur->capndx = mcapndx;
0423
0424 pci_read_config_dword(dev, cur->capndx+AGPSTAT, &mstatus);
0425
0426 if (((mstatus >> 3) & 0x1) == 0) {
0427 dev_err(&td->dev, "woah! AGP 3.x device %s not "
0428 "operating in AGP 3.x mode on secondary bus "
0429 "of AGP 3.5 bridge operating with AGP 3.0 "
0430 "electricals!\n", pci_name(dev));
0431 ret = -ENODEV;
0432 goto free_and_exit;
0433 }
0434 }
0435
0436
0437
0438
0439
0440
0441 if (isoch) {
0442 ret = agp_3_5_isochronous_node_enable(bridge, dev_list, ndevs);
0443 if (ret) {
0444 dev_info(&td->dev, "something bad happened setting "
0445 "up isochronous xfers; falling back to "
0446 "non-isochronous xfer mode\n");
0447 } else {
0448 goto free_and_exit;
0449 }
0450 }
0451 agp_3_5_nonisochronous_node_enable(bridge, dev_list, ndevs);
0452
0453 free_and_exit:
0454
0455 for (pos=head->next; pos!=head; ) {
0456 cur = list_entry(pos, struct agp_3_5_dev, list);
0457
0458 pos = pos->next;
0459 kfree(cur);
0460 }
0461 kfree(dev_list);
0462
0463 get_out:
0464 return ret;
0465 }