📄 ibmphp_pci.c
字号:
/* * IBM Hot Plug Controller Driver * * Written By: Irene Zubarev, IBM Corporation * * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2001,2002 IBM Corp. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Send feedback to <gregkh@us.ibm.com> * */#include <linux/module.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/list.h>#include "ibmphp.h"static int configure_device(struct pci_func *);static int configure_bridge(struct pci_func **, u8);static struct res_needed *scan_behind_bridge(struct pci_func *, u8);static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8);static u8 find_sec_number (u8 primary_busno, u8 slotno);/* * NOTE..... If BIOS doesn't provide default routing, we assign: * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. * If adapter is bridged, then we assign 11 to it and devices behind it. * We also assign the same irq numbers for multi function devices. * These are PIC mode, so shouldn't matter n.e.ways (hopefully) */static void assign_alt_irq (struct pci_func * cur_func, u8 class_code){ int j; for (j = 0; j < 4; j++) { if (cur_func->irq[j] == 0xff) { switch (class_code) { case PCI_BASE_CLASS_STORAGE: cur_func->irq[j] = SCSI_IRQ; break; case PCI_BASE_CLASS_NETWORK: cur_func->irq[j] = LAN_IRQ; break; default: cur_func->irq[j] = OTHER_IRQ; break; } } }}/* * Configures the device to be added (will allocate needed resources if it * can), the device can be a bridge or a regular pci device, can also be * multi-functional * * Input: function to be added * * TO DO: The error case with Multifunction device or multi function bridge, * if there is an error, will need to go through all previous functions and * unconfigure....or can add some code into unconfigure_card.... */int ibmphp_configure_card (struct pci_func *func, u8 slotno){ u16 vendor_id; u32 class; u8 class_code; u8 hdr_type, device, sec_number; u8 function; struct pci_func *newfunc; /* for multi devices */ struct pci_func *cur_func, *prev_func; int rc, i, j; int cleanup_count; u8 flag; u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */ debug ("inside configure_card, func->busno = %x\n", func->busno); device = func->device; cur_func = func; /* We only get bus and device from IRQ routing table. So at this point, * func->busno is correct, and func->device contains only device (at the 5 * highest bits) */ /* For every function on the card */ for (function = 0x00; function < 0x08; function++) { unsigned int devfn = PCI_DEVFN(device, function); ibmphp_pci_bus->number = cur_func->busno; cur_func->function = function; debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", cur_func->busno, cur_func->device, cur_func->function); pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); debug ("vendor_id is %x\n", vendor_id); if (vendor_id != PCI_VENDOR_ID_NOTVALID) { /* found correct device!!! */ debug ("found valid device, vendor_id = %x\n", vendor_id); ++valid_device; /* header: x x x x x x x x * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge * |_=> 0 = single function device, 1 = multi-function device */ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); class_code = class >> 24; debug ("hrd_type = %x, class = %x, class_code %x\n", hdr_type, class, class_code); class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ if (class == PCI_CLASS_NOT_DEFINED_VGA) { err ("The device %x is VGA compatible and as is not supported for hot plugging. " "Please choose another device.\n", cur_func->device); return -ENODEV; } else if (class == PCI_CLASS_DISPLAY_VGA) { err ("The device %x is not supported for hot plugging. " "Please choose another device.\n", cur_func->device); return -ENODEV; } switch (hdr_type) { case PCI_HEADER_TYPE_NORMAL: debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class); assign_alt_irq (cur_func, class_code); if ((rc = configure_device (cur_func)) < 0) { /* We need to do this in case some other BARs were properly inserted */ err ("was not able to configure devfunc %x on bus %x.\n", cur_func->device, cur_func->busno); cleanup_count = 6; goto error; } cur_func->next = NULL; function = 0x8; break; case PCI_HEADER_TYPE_MULTIDEVICE: assign_alt_irq (cur_func, class_code); if ((rc = configure_device (cur_func)) < 0) { /* We need to do this in case some other BARs were properly inserted */ err ("was not able to configure devfunc %x on bus %x...bailing out\n", cur_func->device, cur_func->busno); cleanup_count = 6; goto error; } newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); if (!newfunc) { err ("out of system memory\n"); return -ENOMEM; } memset (newfunc, 0, sizeof (struct pci_func)); newfunc->busno = cur_func->busno; newfunc->device = device; cur_func->next = newfunc; cur_func = newfunc; for (j = 0; j < 4; j++) newfunc->irq[j] = cur_func->irq[j]; break; case PCI_HEADER_TYPE_MULTIBRIDGE: class >>= 8; if (class != PCI_CLASS_BRIDGE_PCI) { err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " "Please insert another card.\n", cur_func->device); return -ENODEV; } assign_alt_irq (cur_func, class_code); rc = configure_bridge (&cur_func, slotno); if (rc == -ENODEV) { err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); err ("Bus %x, devfunc %x\n", cur_func->busno, cur_func->device); return rc; } if (rc) { /* We need to do this in case some other BARs were properly inserted */ err ("was not able to hot-add PPB properly.\n"); func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ cleanup_count = 2; goto error; } pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); flag = FALSE; for (i = 0; i < 32; i++) { if (func->devices[i]) { newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); if (!newfunc) { err ("out of system memory\n"); return -ENOMEM; } memset (newfunc, 0, sizeof (struct pci_func)); newfunc->busno = sec_number; newfunc->device = (u8) i; for (j = 0; j < 4; j++) newfunc->irq[j] = cur_func->irq[j]; if (flag) { for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; prev_func->next = newfunc; } else cur_func->next = newfunc; rc = ibmphp_configure_card (newfunc, slotno); /* This could only happen if kmalloc failed */ if (rc) { /* We need to do this in case bridge itself got configured properly, but devices behind it failed */ func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ cleanup_count = 2; goto error; } flag = TRUE; } } newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); if (!newfunc) { err ("out of system memory\n"); return -ENOMEM; } memset (newfunc, 0, sizeof (struct pci_func)); newfunc->busno = cur_func->busno; newfunc->device = device; for (j = 0; j < 4; j++) newfunc->irq[j] = cur_func->irq[j]; for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; prev_func->next = newfunc; cur_func = newfunc; break; case PCI_HEADER_TYPE_BRIDGE: class >>= 8; debug ("class now is %x\n", class); if (class != PCI_CLASS_BRIDGE_PCI) { err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " "Please insert another card.\n", cur_func->device); return -ENODEV; } assign_alt_irq (cur_func, class_code); debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno); rc = configure_bridge (&cur_func, slotno); if (rc == -ENODEV) { err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); err ("Bus %x, devfunc %x\n", cur_func->busno, cur_func->device); return rc; } if (rc) { /* We need to do this in case some other BARs were properly inserted */ func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ err ("was not able to hot-add PPB properly.\n"); cleanup_count = 2; goto error; } debug ("cur_func->busno = %x, device = %x, function = %x\n", cur_func->busno, device, function); pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); debug ("after configuring bridge..., sec_number = %x\n", sec_number); flag = FALSE; for (i = 0; i < 32; i++) { if (func->devices[i]) { debug ("inside for loop, device is %x\n", i); newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); if (!newfunc) { err (" out of system memory\n"); return -ENOMEM; } memset (newfunc, 0, sizeof (struct pci_func)); newfunc->busno = sec_number; newfunc->device = (u8) i; for (j = 0; j < 4; j++) newfunc->irq[j] = cur_func->irq[j]; if (flag) { for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; prev_func->next = newfunc; } else cur_func->next = newfunc; rc = ibmphp_configure_card (newfunc, slotno); /* Again, this case should not happen... For complete paranoia, will need to call remove_bus */ if (rc) { /* We need to do this in case some other BARs were properly inserted */ func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ cleanup_count = 2; goto error; } flag = TRUE; } } function = 0x8; break; default: err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type); return -ENXIO; break; } /* end of switch */ } /* end of valid device */ } /* end of for */ if (!valid_device) { err ("Cannot find any valid devices on the card. Or unable to read from card.\n"); return -ENODEV; } return 0;error: for (i = 0; i < cleanup_count; i++) { if (cur_func->io[i]) { ibmphp_remove_resource (cur_func->io[i]); cur_func->io[i] = NULL; } else if (cur_func->pfmem[i]) { ibmphp_remove_resource (cur_func->pfmem[i]); cur_func->pfmem[i] = NULL; } else if (cur_func->mem[i]) { ibmphp_remove_resource (cur_func->mem[i]); cur_func->mem[i] = NULL; } } return rc;}/* * This function configures the pci BARs of a single device. * Input: pointer to the pci_func * Output: configured PCI, 0, or error */static int configure_device (struct pci_func *func){ u32 bar[6]; u32 address[] = { PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, 0 }; u8 irq; int count; int len[6]; struct resource_node *io[6]; struct resource_node *mem[6]; struct resource_node *mem_tmp; struct resource_node *pfmem[6]; unsigned int devfn; debug ("%s - inside\n", __FUNCTION__); devfn = PCI_DEVFN(func->device, func->function); ibmphp_pci_bus->number = func->busno; for (count = 0; address[count]; count++) { /* for 6 BARs */ /* not sure if i need this. per scott, said maybe need smth like this if devices don't adhere 100% to the spec, so don't want to write to the reserved bits pcibios_read_config_byte(cur_func->busno, cur_func->device, PCI_BASE_ADDRESS_0 + 4 * count, &tmp); if (tmp & 0x01) // IO pcibios_write_config_dword(cur_func->busno, cur_func->device, PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD); else // Memory pcibios_write_config_dword(cur_func->busno, cur_func->device, PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF); */ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); if (!bar[count]) /* This BAR is not implemented */ continue; debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]); if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { /* This is IO */ debug ("inside IO SPACE\n"); len[count] = bar[count] & 0xFFFFFFFC; len[count] = ~len[count] + 1; debug ("len[count] in IO %x, count %d\n", len[count], count); io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); if (!io[count]) { err ("out of system memory\n"); return -ENOMEM; } memset (io[count], 0, sizeof (struct resource_node)); io[count]->type = IO; io[count]->busno = func->busno; io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); io[count]->len = len[count]; if (ibmphp_check_resource(io[count], 0) == 0) { ibmphp_add_resource (io[count]); func->io[count] = io[count]; } else { err ("cannot allocate requested io for bus %x device %x function %x len %x\n", func->busno, func->device, func->function, len[count]); kfree (io[count]); return -EIO; } pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); /* _______________This is for debugging purposes only_____________________ */ debug ("b4 writing, the IO address is %x\n", func->io[count]->start); pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); debug ("after writing.... the start address is %x\n", bar[count]); /* _________________________________________________________________________*/ } else { /* This is Memory */ if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { /* pfmem */ debug ("PFMEM SPACE\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -