11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PCI Express Hot Plug Controller Driver 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1995,2001 Compaq Computer Corporation 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 IBM Corp. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003-2004 Intel Corporation 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * All rights reserved. 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or (at 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * your option) any later version. 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * NON INFRINGEMENT. See the GNU General Public License for more 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * details. 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 268cf4c19523b7694c88bba716d88fb659fa702411Kristen Accardi * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "../pci.h" 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "pciehp.h" 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 370ab2b57f8db8a1bcdf24089074f5d2856a3ffb42Sam Ravnborgstatic int __ref pciehp_add_bridge(struct pci_dev *dev) 380eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah{ 390eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah struct pci_bus *parent = dev->bus; 400eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah int pass, busnr, start = parent->secondary; 410eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah int end = parent->subordinate; 420eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah 430eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah for (busnr = start; busnr <= end; busnr++) { 440eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah if (!pci_find_bus(pci_domain_nr(parent), busnr)) 450eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah break; 460eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah } 470eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah if (busnr-- > end) { 480eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah err("No bus number available for hot-added bridge %s\n", 490eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah pci_name(dev)); 500eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah return -1; 510eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah } 520eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah for (pass = 0; pass < 2; pass++) 530eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah busnr = pci_scan_bridge(parent, dev, busnr, pass); 540eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah if (!dev->subordinate) 550eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah return -1; 569789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu 570eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah return 0; 580eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah} 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6071b720c0f96145f5868c87591c286b290bc1a6afRajesh Shahint pciehp_configure_device(struct slot *p_slot) 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6271b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah struct pci_dev *dev; 639789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu struct pci_dev *bridge = p_slot->ctrl->pcie->port; 649789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu struct pci_bus *parent = bridge->subordinate; 6571b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah int num, fn; 667f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi struct controller *ctrl = p_slot->ctrl; 6771b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah 68d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); 6971b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah if (dev) { 7018b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_err(ctrl, "Device %s already exists " 71d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige "at %04x:%02x:00, cannot hot-add\n", pci_name(dev), 72d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige pci_domain_nr(parent), parent->number); 7356bfada3e1a25c0da6f4590a4b04c67ec10910c2Kenji Kaneshige pci_dev_put(dev); 7471b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah return -EINVAL; 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 77d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); 7871b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah if (num == 0) { 797f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi ctrl_err(ctrl, "No new device found\n"); 8071b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah return -ENODEV; 8171b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah } 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8371b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah for (fn = 0; fn < 8; fn++) { 84d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); 850eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah if (!dev) 8671b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah continue; 8771b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || 8871b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { 890eb3bcfd088e3234f7af29e189a7900ccfdd278aRajesh Shah pciehp_add_bridge(dev); 9071b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah } 919789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu pci_dev_put(dev); 929789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu } 939789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu 949789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu pci_assign_unassigned_bridge_resources(bridge); 959789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu 969789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu for (fn = 0; fn < 8; fn++) { 979789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); 989789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu if (!dev) 999789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu continue; 1009789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { 1019789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu pci_dev_put(dev); 1029789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu continue; 1039789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu } 104d569c74d78ffcde2f163256e4da934ec3bacff0eBjorn Helgaas pci_configure_slot(dev); 1056e33706b191ff8687f103a86de842b690fd8fb9dKenji Kaneshige pci_dev_put(dev); 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10871b720c0f96145f5868c87591c286b290bc1a6afRajesh Shah pci_bus_add_devices(parent); 1099789ac979b6b6ae6cc09f7b29c88e95ecb14ec39Yinghai Lu 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 113ca22a5e4d70620b7f3d809e424daa5214b0aa00dRajesh Shahint pciehp_unconfigure_device(struct slot *p_slot) 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1151cf53d5ddb93b77ce1e277da85fe695e4c2a667dKristen Carlson Accardi int ret, rc = 0; 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int j; 117ca22a5e4d70620b7f3d809e424daa5214b0aa00dRajesh Shah u8 bctl = 0; 1181cf53d5ddb93b77ce1e277da85fe695e4c2a667dKristen Carlson Accardi u8 presence = 0; 119385e24917ed8eeba25dddd8e63bf3fe3d53eafc5Kenji Kaneshige struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; 1202326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige u16 command; 1217f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi struct controller *ctrl = p_slot->ctrl; 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 123d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", 124d689f7eb364a51ccd857605dede0d6c22a1aad91Kenji Kaneshige __func__, pci_domain_nr(parent), parent->number); 12582a9e79ef132cbf77de58aae35c1a14237f2fcdeKenji Kaneshige ret = pciehp_get_adapter_status(p_slot, &presence); 1262326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige if (ret) 1272326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige presence = 0; 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 129f07234b66af1d1a204b9ddabdbdb312e8f1fb35eRolf Eike Beer for (j = 0; j < 8; j++) { 130ac81860ea073daed50246af54db706c6e491f240Praveen Kalamegham struct pci_dev *temp = pci_get_slot(parent, PCI_DEVFN(0, j)); 131ca22a5e4d70620b7f3d809e424daa5214b0aa00dRajesh Shah if (!temp) 132ca22a5e4d70620b7f3d809e424daa5214b0aa00dRajesh Shah continue; 1332326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { 1342326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); 1352326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige if (bctl & PCI_BRIDGE_CTL_VGA) { 1367f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi ctrl_err(ctrl, 1377f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi "Cannot remove display device %s\n", 1387f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi pci_name(temp)); 1392326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige pci_dev_put(temp); 14001b666df487b80c956cef3ce3253776ddeebd41ePraveen Kalamegham rc = -EINVAL; 141ac81860ea073daed50246af54db706c6e491f240Praveen Kalamegham break; 142ca22a5e4d70620b7f3d809e424daa5214b0aa00dRajesh Shah } 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 144ca22a5e4d70620b7f3d809e424daa5214b0aa00dRajesh Shah pci_remove_bus_device(temp); 1452326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige /* 1462326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige * Ensure that no new Requests will be generated from 1472326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige * the device. 1482326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige */ 1492326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige if (presence) { 1502326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige pci_read_config_word(temp, PCI_COMMAND, &command); 1512326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); 1522326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige command |= PCI_COMMAND_INTX_DISABLE; 1532326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige pci_write_config_word(temp, PCI_COMMAND, command); 1542326e2b99969e69fedc92de80d80b2d2f92fd942Kenji Kaneshige } 15556bfada3e1a25c0da6f4590a4b04c67ec10910c2Kenji Kaneshige pci_dev_put(temp); 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1579fe8164536c4fa6e630c706c667a2c6e8456d143Kenji Kaneshige 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 160