in agp/isoch.c [313:465]
int agp_3_5_enable(struct agp_bridge_data *bridge)
{
struct pci_dev *td = bridge->dev, *dev = NULL;
u8 mcapndx;
u32 isoch;
u32 tstatus, mstatus, ncapid;
u32 mmajor;
u16 mpstat;
struct agp_3_5_dev *dev_list, *cur;
struct list_head *head, *pos;
unsigned int ndevs = 0;
int ret = 0;
/* Extract some power-on defaults from the target */
pci_read_config_dword(td, bridge->capndx+AGPSTAT, &tstatus);
isoch = (tstatus >> 17) & 0x1;
if (isoch == 0) /* isoch xfers not available, bail out. */
return -ENODEV;
/*
* Allocate a head for our AGP 3.5 device list
* (multiple AGP v3 devices are allowed behind a single bridge).
*/
if ((dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL)) == NULL) {
ret = -ENOMEM;
goto get_out;
}
head = &dev_list->list;
INIT_LIST_HEAD(head);
/* Find all AGP devices, and add them to dev_list. */
for_each_pci_dev(dev) {
mcapndx = pci_find_capability(dev, PCI_CAP_ID_AGP);
if (mcapndx == 0)
continue;
switch ((dev->class >>8) & 0xff00) {
case 0x0600: /* Bridge */
/* Skip bridges. We should call this function for each one. */
continue;
case 0x0001: /* Unclassified device */
/* Don't know what this is, but log it for investigation. */
if (mcapndx != 0) {
dev_info(&td->dev, "wacky, found unclassified AGP device %s [%04x/%04x]\n",
pci_name(dev),
dev->vendor, dev->device);
}
continue;
case 0x0300: /* Display controller */
case 0x0400: /* Multimedia controller */
if ((cur = kmalloc(sizeof(*cur), GFP_KERNEL)) == NULL) {
ret = -ENOMEM;
goto free_and_exit;
}
cur->dev = dev;
pos = &cur->list;
list_add(pos, head);
ndevs++;
continue;
default:
continue;
}
}
/*
* Take an initial pass through the devices lying behind our host
* bridge. Make sure each one is actually an AGP 3.0 device, otherwise
* exit with an error message. Along the way store the AGP 3.0
* cap_ptr for each device
*/
list_for_each(pos, head) {
cur = list_entry(pos, struct agp_3_5_dev, list);
dev = cur->dev;
pci_read_config_word(dev, PCI_STATUS, &mpstat);
if ((mpstat & PCI_STATUS_CAP_LIST) == 0)
continue;
pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &mcapndx);
if (mcapndx != 0) {
do {
pci_read_config_dword(dev, mcapndx, &ncapid);
if ((ncapid & 0xff) != 2)
mcapndx = (ncapid >> 8) & 0xff;
}
while (((ncapid & 0xff) != 2) && (mcapndx != 0));
}
if (mcapndx == 0) {
dev_err(&td->dev, "woah! Non-AGP device %s on "
"secondary bus of AGP 3.5 bridge!\n",
pci_name(dev));
ret = -ENODEV;
goto free_and_exit;
}
mmajor = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf;
if (mmajor < 3) {
dev_err(&td->dev, "woah! AGP 2.0 device %s on "
"secondary bus of AGP 3.5 bridge operating "
"with AGP 3.0 electricals!\n", pci_name(dev));
ret = -ENODEV;
goto free_and_exit;
}
cur->capndx = mcapndx;
pci_read_config_dword(dev, cur->capndx+AGPSTAT, &mstatus);
if (((mstatus >> 3) & 0x1) == 0) {
dev_err(&td->dev, "woah! AGP 3.x device %s not "
"operating in AGP 3.x mode on secondary bus "
"of AGP 3.5 bridge operating with AGP 3.0 "
"electricals!\n", pci_name(dev));
ret = -ENODEV;
goto free_and_exit;
}
}
/*
* Call functions to divide target resources amongst the AGP 3.0
* masters. This process is dramatically different depending on
* whether isochronous transfers are supported.
*/
if (isoch) {
ret = agp_3_5_isochronous_node_enable(bridge, dev_list, ndevs);
if (ret) {
dev_info(&td->dev, "something bad happened setting "
"up isochronous xfers; falling back to "
"non-isochronous xfer mode\n");
} else {
goto free_and_exit;
}
}
agp_3_5_nonisochronous_node_enable(bridge, dev_list, ndevs);
free_and_exit:
/* Be sure to free the dev_list */
for (pos=head->next; pos!=head; ) {
cur = list_entry(pos, struct agp_3_5_dev, list);
pos = pos->next;
kfree(cur);
}
kfree(dev_list);
get_out:
return ret;
}