18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Setup routines for AGP 3.5 compliant bridges. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/list.h> 78c2ecf20Sopenharmony_ci#include <linux/pci.h> 88c2ecf20Sopenharmony_ci#include <linux/agp_backend.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "agp.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* Generic AGP 3.5 enabling routines */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct agp_3_5_dev { 178c2ecf20Sopenharmony_ci struct list_head list; 188c2ecf20Sopenharmony_ci u8 capndx; 198c2ecf20Sopenharmony_ci u32 maxbw; 208c2ecf20Sopenharmony_ci struct pci_dev *dev; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void agp_3_5_dev_list_insert(struct list_head *head, struct list_head *new) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct agp_3_5_dev *cur, *n = list_entry(new, struct agp_3_5_dev, list); 268c2ecf20Sopenharmony_ci struct list_head *pos; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci list_for_each(pos, head) { 298c2ecf20Sopenharmony_ci cur = list_entry(pos, struct agp_3_5_dev, list); 308c2ecf20Sopenharmony_ci if (cur->maxbw > n->maxbw) 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci list_add_tail(new, pos); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void agp_3_5_dev_list_sort(struct agp_3_5_dev *list, unsigned int ndevs) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct agp_3_5_dev *cur; 398c2ecf20Sopenharmony_ci struct pci_dev *dev; 408c2ecf20Sopenharmony_ci struct list_head *pos, *tmp, *head = &list->list, *start = head->next; 418c2ecf20Sopenharmony_ci u32 nistat; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci INIT_LIST_HEAD(head); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci for (pos=start; pos!=head; ) { 468c2ecf20Sopenharmony_ci cur = list_entry(pos, struct agp_3_5_dev, list); 478c2ecf20Sopenharmony_ci dev = cur->dev; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &nistat); 508c2ecf20Sopenharmony_ci cur->maxbw = (nistat >> 16) & 0xff; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci tmp = pos; 538c2ecf20Sopenharmony_ci pos = pos->next; 548c2ecf20Sopenharmony_ci agp_3_5_dev_list_insert(head, tmp); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Initialize all isochronous transfer parameters for an AGP 3.0 608c2ecf20Sopenharmony_ci * node (i.e. a host bridge in combination with the adapters 618c2ecf20Sopenharmony_ci * lying behind it...) 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int agp_3_5_isochronous_node_enable(struct agp_bridge_data *bridge, 658c2ecf20Sopenharmony_ci struct agp_3_5_dev *dev_list, unsigned int ndevs) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Convenience structure to make the calculations clearer 698c2ecf20Sopenharmony_ci * here. The field names come straight from the AGP 3.0 spec. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci struct isoch_data { 728c2ecf20Sopenharmony_ci u32 maxbw; 738c2ecf20Sopenharmony_ci u32 n; 748c2ecf20Sopenharmony_ci u32 y; 758c2ecf20Sopenharmony_ci u32 l; 768c2ecf20Sopenharmony_ci u32 rq; 778c2ecf20Sopenharmony_ci struct agp_3_5_dev *dev; 788c2ecf20Sopenharmony_ci }; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci struct pci_dev *td = bridge->dev, *dev; 818c2ecf20Sopenharmony_ci struct list_head *head = &dev_list->list, *pos; 828c2ecf20Sopenharmony_ci struct agp_3_5_dev *cur; 838c2ecf20Sopenharmony_ci struct isoch_data *master, target; 848c2ecf20Sopenharmony_ci unsigned int cdev = 0; 858c2ecf20Sopenharmony_ci u32 mnistat, tnistat, tstatus, mcmd; 868c2ecf20Sopenharmony_ci u16 tnicmd, mnicmd; 878c2ecf20Sopenharmony_ci u32 tot_bw = 0, tot_n = 0, tot_rq = 0, y_max, rq_isoch, rq_async; 888c2ecf20Sopenharmony_ci u32 step, rem, rem_isoch, rem_async; 898c2ecf20Sopenharmony_ci int ret = 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * We'll work with an array of isoch_data's (one for each 938c2ecf20Sopenharmony_ci * device in dev_list) throughout this function. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci master = kmalloc_array(ndevs, sizeof(*master), GFP_KERNEL); 968c2ecf20Sopenharmony_ci if (master == NULL) { 978c2ecf20Sopenharmony_ci ret = -ENOMEM; 988c2ecf20Sopenharmony_ci goto get_out; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * Sort the device list by maxbw. We need to do this because the 1038c2ecf20Sopenharmony_ci * spec suggests that the devices with the smallest requirements 1048c2ecf20Sopenharmony_ci * have their resources allocated first, with all remaining resources 1058c2ecf20Sopenharmony_ci * falling to the device with the largest requirement. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * We don't exactly do this, we divide target resources by ndevs 1088c2ecf20Sopenharmony_ci * and split them amongst the AGP 3.0 devices. The remainder of such 1098c2ecf20Sopenharmony_ci * division operations are dropped on the last device, sort of like 1108c2ecf20Sopenharmony_ci * the spec mentions it should be done. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * We can't do this sort when we initially construct the dev_list 1138c2ecf20Sopenharmony_ci * because we don't know until this function whether isochronous 1148c2ecf20Sopenharmony_ci * transfers are enabled and consequently whether maxbw will mean 1158c2ecf20Sopenharmony_ci * anything. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci agp_3_5_dev_list_sort(dev_list, ndevs); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat); 1208c2ecf20Sopenharmony_ci pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Extract power-on defaults from the target */ 1238c2ecf20Sopenharmony_ci target.maxbw = (tnistat >> 16) & 0xff; 1248c2ecf20Sopenharmony_ci target.n = (tnistat >> 8) & 0xff; 1258c2ecf20Sopenharmony_ci target.y = (tnistat >> 6) & 0x3; 1268c2ecf20Sopenharmony_ci target.l = (tnistat >> 3) & 0x7; 1278c2ecf20Sopenharmony_ci target.rq = (tstatus >> 24) & 0xff; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci y_max = target.y; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * Extract power-on defaults for each device in dev_list. Along 1338c2ecf20Sopenharmony_ci * the way, calculate the total isochronous bandwidth required 1348c2ecf20Sopenharmony_ci * by these devices and the largest requested payload size. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci list_for_each(pos, head) { 1378c2ecf20Sopenharmony_ci cur = list_entry(pos, struct agp_3_5_dev, list); 1388c2ecf20Sopenharmony_ci dev = cur->dev; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pci_read_config_dword(dev, cur->capndx+AGPNISTAT, &mnistat); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci master[cdev].maxbw = (mnistat >> 16) & 0xff; 1438c2ecf20Sopenharmony_ci master[cdev].n = (mnistat >> 8) & 0xff; 1448c2ecf20Sopenharmony_ci master[cdev].y = (mnistat >> 6) & 0x3; 1458c2ecf20Sopenharmony_ci master[cdev].dev = cur; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci tot_bw += master[cdev].maxbw; 1488c2ecf20Sopenharmony_ci y_max = max(y_max, master[cdev].y); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci cdev++; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Check if this configuration has any chance of working */ 1548c2ecf20Sopenharmony_ci if (tot_bw > target.maxbw) { 1558c2ecf20Sopenharmony_ci dev_err(&td->dev, "isochronous bandwidth required " 1568c2ecf20Sopenharmony_ci "by AGP 3.0 devices exceeds that which is supported by " 1578c2ecf20Sopenharmony_ci "the AGP 3.0 bridge!\n"); 1588c2ecf20Sopenharmony_ci ret = -ENODEV; 1598c2ecf20Sopenharmony_ci goto free_and_exit; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci target.y = y_max; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * Write the calculated payload size into the target's NICMD 1668c2ecf20Sopenharmony_ci * register. Doing this directly effects the ISOCH_N value 1678c2ecf20Sopenharmony_ci * in the target's NISTAT register, so we need to do this now 1688c2ecf20Sopenharmony_ci * to get an accurate value for ISOCH_N later. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci pci_read_config_word(td, bridge->capndx+AGPNICMD, &tnicmd); 1718c2ecf20Sopenharmony_ci tnicmd &= ~(0x3 << 6); 1728c2ecf20Sopenharmony_ci tnicmd |= target.y << 6; 1738c2ecf20Sopenharmony_ci pci_write_config_word(td, bridge->capndx+AGPNICMD, tnicmd); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Reread the target's ISOCH_N */ 1768c2ecf20Sopenharmony_ci pci_read_config_dword(td, bridge->capndx+AGPNISTAT, &tnistat); 1778c2ecf20Sopenharmony_ci target.n = (tnistat >> 8) & 0xff; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Calculate the minimum ISOCH_N needed by each master */ 1808c2ecf20Sopenharmony_ci for (cdev=0; cdev<ndevs; cdev++) { 1818c2ecf20Sopenharmony_ci master[cdev].y = target.y; 1828c2ecf20Sopenharmony_ci master[cdev].n = master[cdev].maxbw / (master[cdev].y + 1); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci tot_n += master[cdev].n; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Exit if the minimal ISOCH_N allocation among the masters is more 1888c2ecf20Sopenharmony_ci * than the target can handle. */ 1898c2ecf20Sopenharmony_ci if (tot_n > target.n) { 1908c2ecf20Sopenharmony_ci dev_err(&td->dev, "number of isochronous " 1918c2ecf20Sopenharmony_ci "transactions per period required by AGP 3.0 devices " 1928c2ecf20Sopenharmony_ci "exceeds that which is supported by the AGP 3.0 " 1938c2ecf20Sopenharmony_ci "bridge!\n"); 1948c2ecf20Sopenharmony_ci ret = -ENODEV; 1958c2ecf20Sopenharmony_ci goto free_and_exit; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Calculate left over ISOCH_N capability in the target. We'll give 1998c2ecf20Sopenharmony_ci * this to the hungriest device (as per the spec) */ 2008c2ecf20Sopenharmony_ci rem = target.n - tot_n; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Calculate the minimum isochronous RQ depth needed by each master. 2048c2ecf20Sopenharmony_ci * Along the way, distribute the extra ISOCH_N capability calculated 2058c2ecf20Sopenharmony_ci * above. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci for (cdev=0; cdev<ndevs; cdev++) { 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * This is a little subtle. If ISOCH_Y > 64B, then ISOCH_Y 2108c2ecf20Sopenharmony_ci * byte isochronous writes will be broken into 64B pieces. 2118c2ecf20Sopenharmony_ci * This means we need to budget more RQ depth to account for 2128c2ecf20Sopenharmony_ci * these kind of writes (each isochronous write is actually 2138c2ecf20Sopenharmony_ci * many writes on the AGP bus). 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci master[cdev].rq = master[cdev].n; 2168c2ecf20Sopenharmony_ci if (master[cdev].y > 0x1) 2178c2ecf20Sopenharmony_ci master[cdev].rq *= (1 << (master[cdev].y - 1)); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci tot_rq += master[cdev].rq; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci master[ndevs-1].n += rem; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Figure the number of isochronous and asynchronous RQ slots the 2248c2ecf20Sopenharmony_ci * target is providing. */ 2258c2ecf20Sopenharmony_ci rq_isoch = (target.y > 0x1) ? target.n * (1 << (target.y - 1)) : target.n; 2268c2ecf20Sopenharmony_ci rq_async = target.rq - rq_isoch; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Exit if the minimal RQ needs of the masters exceeds what the target 2298c2ecf20Sopenharmony_ci * can provide. */ 2308c2ecf20Sopenharmony_ci if (tot_rq > rq_isoch) { 2318c2ecf20Sopenharmony_ci dev_err(&td->dev, "number of request queue slots " 2328c2ecf20Sopenharmony_ci "required by the isochronous bandwidth requested by " 2338c2ecf20Sopenharmony_ci "AGP 3.0 devices exceeds the number provided by the " 2348c2ecf20Sopenharmony_ci "AGP 3.0 bridge!\n"); 2358c2ecf20Sopenharmony_ci ret = -ENODEV; 2368c2ecf20Sopenharmony_ci goto free_and_exit; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Calculate asynchronous RQ capability in the target (per master) as 2408c2ecf20Sopenharmony_ci * well as the total number of leftover isochronous RQ slots. */ 2418c2ecf20Sopenharmony_ci step = rq_async / ndevs; 2428c2ecf20Sopenharmony_ci rem_async = step + (rq_async % ndevs); 2438c2ecf20Sopenharmony_ci rem_isoch = rq_isoch - tot_rq; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Distribute the extra RQ slots calculated above and write our 2468c2ecf20Sopenharmony_ci * isochronous settings out to the actual devices. */ 2478c2ecf20Sopenharmony_ci for (cdev=0; cdev<ndevs; cdev++) { 2488c2ecf20Sopenharmony_ci cur = master[cdev].dev; 2498c2ecf20Sopenharmony_ci dev = cur->dev; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci master[cdev].rq += (cdev == ndevs - 1) 2528c2ecf20Sopenharmony_ci ? (rem_async + rem_isoch) : step; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci pci_read_config_word(dev, cur->capndx+AGPNICMD, &mnicmd); 2558c2ecf20Sopenharmony_ci pci_read_config_dword(dev, cur->capndx+AGPCMD, &mcmd); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci mnicmd &= ~(0xff << 8); 2588c2ecf20Sopenharmony_ci mnicmd &= ~(0x3 << 6); 2598c2ecf20Sopenharmony_ci mcmd &= ~(0xff << 24); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mnicmd |= master[cdev].n << 8; 2628c2ecf20Sopenharmony_ci mnicmd |= master[cdev].y << 6; 2638c2ecf20Sopenharmony_ci mcmd |= master[cdev].rq << 24; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci pci_write_config_dword(dev, cur->capndx+AGPCMD, mcmd); 2668c2ecf20Sopenharmony_ci pci_write_config_word(dev, cur->capndx+AGPNICMD, mnicmd); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cifree_and_exit: 2708c2ecf20Sopenharmony_ci kfree(master); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ciget_out: 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* 2778c2ecf20Sopenharmony_ci * This function basically allocates request queue slots among the 2788c2ecf20Sopenharmony_ci * AGP 3.0 systems in nonisochronous nodes. The algorithm is 2798c2ecf20Sopenharmony_ci * pretty stupid, divide the total number of RQ slots provided by the 2808c2ecf20Sopenharmony_ci * target by ndevs. Distribute this many slots to each AGP 3.0 device, 2818c2ecf20Sopenharmony_ci * giving any left over slots to the last device in dev_list. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_cistatic void agp_3_5_nonisochronous_node_enable(struct agp_bridge_data *bridge, 2848c2ecf20Sopenharmony_ci struct agp_3_5_dev *dev_list, unsigned int ndevs) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct agp_3_5_dev *cur; 2878c2ecf20Sopenharmony_ci struct list_head *head = &dev_list->list, *pos; 2888c2ecf20Sopenharmony_ci u32 tstatus, mcmd; 2898c2ecf20Sopenharmony_ci u32 trq, mrq, rem; 2908c2ecf20Sopenharmony_ci unsigned int cdev = 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci pci_read_config_dword(bridge->dev, bridge->capndx+AGPSTAT, &tstatus); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci trq = (tstatus >> 24) & 0xff; 2958c2ecf20Sopenharmony_ci mrq = trq / ndevs; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci rem = mrq + (trq % ndevs); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci for (pos=head->next; cdev<ndevs; cdev++, pos=pos->next) { 3008c2ecf20Sopenharmony_ci cur = list_entry(pos, struct agp_3_5_dev, list); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci pci_read_config_dword(cur->dev, cur->capndx+AGPCMD, &mcmd); 3038c2ecf20Sopenharmony_ci mcmd &= ~(0xff << 24); 3048c2ecf20Sopenharmony_ci mcmd |= ((cdev == ndevs - 1) ? rem : mrq) << 24; 3058c2ecf20Sopenharmony_ci pci_write_config_dword(cur->dev, cur->capndx+AGPCMD, mcmd); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/* 3108c2ecf20Sopenharmony_ci * Fully configure and enable an AGP 3.0 host bridge and all the devices 3118c2ecf20Sopenharmony_ci * lying behind it. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ciint agp_3_5_enable(struct agp_bridge_data *bridge) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct pci_dev *td = bridge->dev, *dev = NULL; 3168c2ecf20Sopenharmony_ci u8 mcapndx; 3178c2ecf20Sopenharmony_ci u32 isoch; 3188c2ecf20Sopenharmony_ci u32 tstatus, mstatus, ncapid; 3198c2ecf20Sopenharmony_ci u32 mmajor; 3208c2ecf20Sopenharmony_ci u16 mpstat; 3218c2ecf20Sopenharmony_ci struct agp_3_5_dev *dev_list, *cur; 3228c2ecf20Sopenharmony_ci struct list_head *head, *pos; 3238c2ecf20Sopenharmony_ci unsigned int ndevs = 0; 3248c2ecf20Sopenharmony_ci int ret = 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Extract some power-on defaults from the target */ 3278c2ecf20Sopenharmony_ci pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus); 3288c2ecf20Sopenharmony_ci isoch = (tstatus >> 17) & 0x1; 3298c2ecf20Sopenharmony_ci if (isoch == 0) /* isoch xfers not available, bail out. */ 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* 3338c2ecf20Sopenharmony_ci * Allocate a head for our AGP 3.5 device list 3348c2ecf20Sopenharmony_ci * (multiple AGP v3 devices are allowed behind a single bridge). 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci if ((dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL)) == NULL) { 3378c2ecf20Sopenharmony_ci ret = -ENOMEM; 3388c2ecf20Sopenharmony_ci goto get_out; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci head = &dev_list->list; 3418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(head); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Find all AGP devices, and add them to dev_list. */ 3448c2ecf20Sopenharmony_ci for_each_pci_dev(dev) { 3458c2ecf20Sopenharmony_ci mcapndx = pci_find_capability(dev, PCI_CAP_ID_AGP); 3468c2ecf20Sopenharmony_ci if (mcapndx == 0) 3478c2ecf20Sopenharmony_ci continue; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci switch ((dev->class >>8) & 0xff00) { 3508c2ecf20Sopenharmony_ci case 0x0600: /* Bridge */ 3518c2ecf20Sopenharmony_ci /* Skip bridges. We should call this function for each one. */ 3528c2ecf20Sopenharmony_ci continue; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci case 0x0001: /* Unclassified device */ 3558c2ecf20Sopenharmony_ci /* Don't know what this is, but log it for investigation. */ 3568c2ecf20Sopenharmony_ci if (mcapndx != 0) { 3578c2ecf20Sopenharmony_ci dev_info(&td->dev, "wacky, found unclassified AGP device %s [%04x/%04x]\n", 3588c2ecf20Sopenharmony_ci pci_name(dev), 3598c2ecf20Sopenharmony_ci dev->vendor, dev->device); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci continue; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci case 0x0300: /* Display controller */ 3648c2ecf20Sopenharmony_ci case 0x0400: /* Multimedia controller */ 3658c2ecf20Sopenharmony_ci if ((cur = kmalloc(sizeof(*cur), GFP_KERNEL)) == NULL) { 3668c2ecf20Sopenharmony_ci ret = -ENOMEM; 3678c2ecf20Sopenharmony_ci goto free_and_exit; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci cur->dev = dev; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci pos = &cur->list; 3728c2ecf20Sopenharmony_ci list_add(pos, head); 3738c2ecf20Sopenharmony_ci ndevs++; 3748c2ecf20Sopenharmony_ci continue; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci default: 3778c2ecf20Sopenharmony_ci continue; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * Take an initial pass through the devices lying behind our host 3838c2ecf20Sopenharmony_ci * bridge. Make sure each one is actually an AGP 3.0 device, otherwise 3848c2ecf20Sopenharmony_ci * exit with an error message. Along the way store the AGP 3.0 3858c2ecf20Sopenharmony_ci * cap_ptr for each device 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci list_for_each(pos, head) { 3888c2ecf20Sopenharmony_ci cur = list_entry(pos, struct agp_3_5_dev, list); 3898c2ecf20Sopenharmony_ci dev = cur->dev; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_STATUS, &mpstat); 3928c2ecf20Sopenharmony_ci if ((mpstat & PCI_STATUS_CAP_LIST) == 0) 3938c2ecf20Sopenharmony_ci continue; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &mcapndx); 3968c2ecf20Sopenharmony_ci if (mcapndx != 0) { 3978c2ecf20Sopenharmony_ci do { 3988c2ecf20Sopenharmony_ci pci_read_config_dword(dev, mcapndx, &ncapid); 3998c2ecf20Sopenharmony_ci if ((ncapid & 0xff) != 2) 4008c2ecf20Sopenharmony_ci mcapndx = (ncapid >> 8) & 0xff; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci while (((ncapid & 0xff) != 2) && (mcapndx != 0)); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (mcapndx == 0) { 4068c2ecf20Sopenharmony_ci dev_err(&td->dev, "woah! Non-AGP device %s on " 4078c2ecf20Sopenharmony_ci "secondary bus of AGP 3.5 bridge!\n", 4088c2ecf20Sopenharmony_ci pci_name(dev)); 4098c2ecf20Sopenharmony_ci ret = -ENODEV; 4108c2ecf20Sopenharmony_ci goto free_and_exit; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci mmajor = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; 4148c2ecf20Sopenharmony_ci if (mmajor < 3) { 4158c2ecf20Sopenharmony_ci dev_err(&td->dev, "woah! AGP 2.0 device %s on " 4168c2ecf20Sopenharmony_ci "secondary bus of AGP 3.5 bridge operating " 4178c2ecf20Sopenharmony_ci "with AGP 3.0 electricals!\n", pci_name(dev)); 4188c2ecf20Sopenharmony_ci ret = -ENODEV; 4198c2ecf20Sopenharmony_ci goto free_and_exit; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci cur->capndx = mcapndx; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci pci_read_config_dword(dev, cur->capndx+AGPSTAT, &mstatus); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (((mstatus >> 3) & 0x1) == 0) { 4278c2ecf20Sopenharmony_ci dev_err(&td->dev, "woah! AGP 3.x device %s not " 4288c2ecf20Sopenharmony_ci "operating in AGP 3.x mode on secondary bus " 4298c2ecf20Sopenharmony_ci "of AGP 3.5 bridge operating with AGP 3.0 " 4308c2ecf20Sopenharmony_ci "electricals!\n", pci_name(dev)); 4318c2ecf20Sopenharmony_ci ret = -ENODEV; 4328c2ecf20Sopenharmony_ci goto free_and_exit; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * Call functions to divide target resources amongst the AGP 3.0 4388c2ecf20Sopenharmony_ci * masters. This process is dramatically different depending on 4398c2ecf20Sopenharmony_ci * whether isochronous transfers are supported. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (isoch) { 4428c2ecf20Sopenharmony_ci ret = agp_3_5_isochronous_node_enable(bridge, dev_list, ndevs); 4438c2ecf20Sopenharmony_ci if (ret) { 4448c2ecf20Sopenharmony_ci dev_info(&td->dev, "something bad happened setting " 4458c2ecf20Sopenharmony_ci "up isochronous xfers; falling back to " 4468c2ecf20Sopenharmony_ci "non-isochronous xfer mode\n"); 4478c2ecf20Sopenharmony_ci } else { 4488c2ecf20Sopenharmony_ci goto free_and_exit; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci agp_3_5_nonisochronous_node_enable(bridge, dev_list, ndevs); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cifree_and_exit: 4548c2ecf20Sopenharmony_ci /* Be sure to free the dev_list */ 4558c2ecf20Sopenharmony_ci for (pos=head->next; pos!=head; ) { 4568c2ecf20Sopenharmony_ci cur = list_entry(pos, struct agp_3_5_dev, list); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci pos = pos->next; 4598c2ecf20Sopenharmony_ci kfree(cur); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci kfree(dev_list); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciget_out: 4648c2ecf20Sopenharmony_ci return ret; 4658c2ecf20Sopenharmony_ci} 466