| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * PCI Express Hot Plug Controller Driver | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 1995,2001 Compaq Computer Corporation | 
					
						
							|  |  |  |  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | 
					
						
							|  |  |  |  * Copyright (C) 2001 IBM Corp. | 
					
						
							|  |  |  |  * Copyright (C) 2003-2004 Intel Corporation | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-08-16 15:16:10 -07:00
										 |  |  |  * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include "../pci.h"
 | 
					
						
							|  |  |  | #include "pciehp.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | int pciehp_configure_device(struct slot *p_slot) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | 	struct pci_dev *dev; | 
					
						
							| 
									
										
										
										
											2010-01-22 01:02:26 -08:00
										 |  |  | 	struct pci_dev *bridge = p_slot->ctrl->pcie->port; | 
					
						
							|  |  |  | 	struct pci_bus *parent = bridge->subordinate; | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 	int num, ret = 0; | 
					
						
							| 
									
										
										
										
											2008-09-05 12:11:26 +09:00
										 |  |  | 	struct controller *ctrl = p_slot->ctrl; | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 	pci_lock_rescan_remove(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-15 17:26:56 +09:00
										 |  |  | 	dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | 	if (dev) { | 
					
						
							| 
									
										
										
										
											2014-04-18 20:13:50 -04:00
										 |  |  | 		ctrl_err(ctrl, "Device %s already exists at %04x:%02x:00, cannot hot-add\n", | 
					
						
							|  |  |  | 			 pci_name(dev), pci_domain_nr(parent), parent->number); | 
					
						
							| 
									
										
										
										
											2006-05-12 11:22:24 +09:00
										 |  |  | 		pci_dev_put(dev); | 
					
						
							| 
									
										
										
										
											2014-02-11 17:36:51 -07:00
										 |  |  | 		ret = -EEXIST; | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-15 17:26:56 +09:00
										 |  |  | 	num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | 	if (num == 0) { | 
					
						
							| 
									
										
										
										
											2008-09-05 12:11:26 +09:00
										 |  |  | 		ctrl_err(ctrl, "No new device found\n"); | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 		ret = -ENODEV; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 	list_for_each_entry(dev, &parent->devices, bus_list) | 
					
						
							| 
									
										
										
										
											2014-05-04 12:23:48 +08:00
										 |  |  | 		if (pci_is_bridge(dev)) | 
					
						
							| 
									
										
										
										
											2012-05-17 18:58:41 -07:00
										 |  |  | 			pci_hp_add_bridge(dev); | 
					
						
							| 
									
										
										
										
											2010-01-22 01:02:26 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pci_assign_unassigned_bridge_resources(bridge); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 	list_for_each_entry(dev, &parent->devices, bus_list) { | 
					
						
							|  |  |  | 		if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) | 
					
						
							| 
									
										
										
										
											2010-01-22 01:02:26 -08:00
										 |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-14 16:35:25 -06:00
										 |  |  | 		pci_configure_slot(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:06 -08:00
										 |  |  | 	pci_bus_add_devices(parent); | 
					
						
							| 
									
										
										
										
											2010-01-22 01:02:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  |  out: | 
					
						
							|  |  |  | 	pci_unlock_rescan_remove(); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:08 -08:00
										 |  |  | int pciehp_unconfigure_device(struct slot *p_slot) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-12-14 13:06:16 -07:00
										 |  |  | 	int rc = 0; | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:08 -08:00
										 |  |  | 	u8 bctl = 0; | 
					
						
							| 
									
										
										
										
											2007-08-09 16:09:31 -07:00
										 |  |  | 	u8 presence = 0; | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 	struct pci_dev *dev, *temp; | 
					
						
							| 
									
										
										
										
											2009-09-15 17:30:14 +09:00
										 |  |  | 	struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; | 
					
						
							| 
									
										
										
										
											2007-12-20 19:46:33 +09:00
										 |  |  | 	u16 command; | 
					
						
							| 
									
										
										
										
											2008-09-05 12:11:26 +09:00
										 |  |  | 	struct controller *ctrl = p_slot->ctrl; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-15 17:26:56 +09:00
										 |  |  | 	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", | 
					
						
							|  |  |  | 		 __func__, pci_domain_nr(parent), parent->number); | 
					
						
							| 
									
										
										
										
											2013-12-14 13:06:16 -07:00
										 |  |  | 	pciehp_get_adapter_status(p_slot, &presence); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 	pci_lock_rescan_remove(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:16 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Stopping an SR-IOV PF device removes all the associated VFs, | 
					
						
							|  |  |  | 	 * which will update the bus->devices list and confuse the | 
					
						
							|  |  |  | 	 * iterator.  Therefore, iterate in reverse so we remove the VFs | 
					
						
							|  |  |  | 	 * first, then the PF.  We do the same in pci_stop_bus_device(). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	list_for_each_entry_safe_reverse(dev, temp, &parent->devices, | 
					
						
							|  |  |  | 					 bus_list) { | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 		pci_dev_get(dev); | 
					
						
							|  |  |  | 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { | 
					
						
							|  |  |  | 			pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl); | 
					
						
							| 
									
										
										
										
											2007-12-20 19:46:33 +09:00
										 |  |  | 			if (bctl & PCI_BRIDGE_CTL_VGA) { | 
					
						
							| 
									
										
										
										
											2008-09-05 12:11:26 +09:00
										 |  |  | 				ctrl_err(ctrl, | 
					
						
							|  |  |  | 					 "Cannot remove display device %s\n", | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 					 pci_name(dev)); | 
					
						
							|  |  |  | 				pci_dev_put(dev); | 
					
						
							| 
									
										
										
										
											2010-05-20 15:32:22 -05:00
										 |  |  | 				rc = -EINVAL; | 
					
						
							| 
									
										
										
										
											2010-05-19 17:03:12 -05:00
										 |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2005-10-31 16:20:08 -08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 		pci_stop_and_remove_bus_device(dev); | 
					
						
							| 
									
										
										
										
											2007-12-20 19:46:33 +09:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Ensure that no new Requests will be generated from | 
					
						
							|  |  |  | 		 * the device. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (presence) { | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 			pci_read_config_word(dev, PCI_COMMAND, &command); | 
					
						
							| 
									
										
										
										
											2007-12-20 19:46:33 +09:00
										 |  |  | 			command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); | 
					
						
							|  |  |  | 			command |= PCI_COMMAND_INTX_DISABLE; | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 			pci_write_config_word(dev, PCI_COMMAND, command); | 
					
						
							| 
									
										
										
										
											2007-12-20 19:46:33 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-15 11:12:19 +08:00
										 |  |  | 		pci_dev_put(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-08-09 16:09:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 	pci_unlock_rescan_remove(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return rc; | 
					
						
							|  |  |  | } |