1689 lines · 54.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * 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. * * 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->function = %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);					rc = configure_device(cur_func);					if (rc < 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);					rc = configure_device(cur_func);					if (rc < 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 = kzalloc_obj(*newfunc);					if (!newfunc)						return -ENOMEM; 					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 = 0;					for (i = 0; i < 32; i++) {						if (func->devices[i]) {							newfunc = kzalloc_obj(*newfunc);							if (!newfunc)								return -ENOMEM; 							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 = 1;						}					} 					newfunc = kzalloc_obj(*newfunc);					if (!newfunc)						return -ENOMEM; 					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 = 0;					for (i = 0; i < 32; i++) {						if (func->devices[i]) {							debug("inside for loop, device is %x\n", i);							newfunc = kzalloc_obj(*newfunc);							if (!newfunc)								return -ENOMEM; 							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 = 1;						}					} 					function = 0x8;					break;				default:					err("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type);					return -ENXIO;			}	/* 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];	static const 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", __func__); 	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 * something 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] = kzalloc_obj(struct resource_node); 			if (!io[count])				return -ENOMEM; 			io[count]->type = IO;			io[count]->busno = func->busno;			io[count]->devfunc = PCI_DEVFN(func->device, func->function);			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"); 				len[count] = bar[count] & 0xFFFFFFF0;				len[count] = ~len[count] + 1; 				debug("len[count] in PFMEM %x, count %d\n", len[count], count); 				pfmem[count] = kzalloc_obj(struct resource_node);				if (!pfmem[count])					return -ENOMEM; 				pfmem[count]->type = PFMEM;				pfmem[count]->busno = func->busno;				pfmem[count]->devfunc = PCI_DEVFN(func->device,							func->function);				pfmem[count]->len = len[count];				pfmem[count]->fromMem = 0;				if (ibmphp_check_resource(pfmem[count], 0) == 0) {					ibmphp_add_resource(pfmem[count]);					func->pfmem[count] = pfmem[count];				} else {					mem_tmp = kzalloc_obj(*mem_tmp);					if (!mem_tmp) {						kfree(pfmem[count]);						return -ENOMEM;					}					mem_tmp->type = MEM;					mem_tmp->busno = pfmem[count]->busno;					mem_tmp->devfunc = pfmem[count]->devfunc;					mem_tmp->len = pfmem[count]->len;					debug("there's no pfmem... going into mem.\n");					if (ibmphp_check_resource(mem_tmp, 0) == 0) {						ibmphp_add_resource(mem_tmp);						pfmem[count]->fromMem = 1;						pfmem[count]->rangeno = mem_tmp->rangeno;						pfmem[count]->start = mem_tmp->start;						pfmem[count]->end = mem_tmp->end;						ibmphp_add_pfmem_from_mem(pfmem[count]);						func->pfmem[count] = pfmem[count];					} else {						err("cannot allocate requested pfmem for bus %x, device %x, len %x\n",						     func->busno, func->device, len[count]);						kfree(mem_tmp);						kfree(pfmem[count]);						return -EIO;					}				} 				pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); 				/*_______________This is for debugging purposes only______________________________*/				debug("b4 writing, start address is %x\n", func->pfmem[count]->start);				pci_bus_read_config_dword(ibmphp_pci_bus, devfn, address[count], &bar[count]);				debug("after writing, start address is %x\n", bar[count]);				/*_________________________________________________________________________________*/ 				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {	/* takes up another dword */					debug("inside the mem 64 case, count %d\n", count);					count += 1;					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */					pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], 0x00000000);				}			} else {				/* regular memory */				debug("REGULAR MEM SPACE\n"); 				len[count] = bar[count] & 0xFFFFFFF0;				len[count] = ~len[count] + 1; 				debug("len[count] in Mem %x, count %d\n", len[count], count); 				mem[count] = kzalloc_obj(struct resource_node);				if (!mem[count])					return -ENOMEM; 				mem[count]->type = MEM;				mem[count]->busno = func->busno;				mem[count]->devfunc = PCI_DEVFN(func->device,							func->function);				mem[count]->len = len[count];				if (ibmphp_check_resource(mem[count], 0) == 0) {					ibmphp_add_resource(mem[count]);					func->mem[count] = mem[count];				} else {

Lines 1–500 of 1689.