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