mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
4e57b68178
I recently picked up my older work to remove unnecessary #includes of sched.h, starting from a patch by Dave Jones to not include sched.h from module.h. This reduces the number of indirect includes of sched.h by ~300. Another ~400 pointless direct includes can be removed after this disentangling (patch to follow later). However, quite a few indirect includes need to be fixed up for this. In order to feed the patches through -mm with as little disturbance as possible, I've split out the fixes I accumulated up to now (complete for i386 and x86_64, more archs to follow later) and post them before the real patch. This way this large part of the patch is kept simple with only adding #includes, and all hunks are independent of each other. So if any hunk rejects or gets in the way of other patches, just drop it. My scripts will pick it up again in the next round. Signed-off-by: Tim Schmielau <tim@physik3.uni-rostock.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
526 lines
11 KiB
C
526 lines
11 KiB
C
/*
|
|
* Interface for Dynamic Logical Partitioning of I/O Slots on
|
|
* RPA-compliant PPC64 platform.
|
|
*
|
|
* John Rose <johnrose@austin.ibm.com>
|
|
* Linda Xie <lxie@us.ibm.com>
|
|
*
|
|
* October 2003
|
|
*
|
|
* Copyright (C) 2003 IBM.
|
|
*
|
|
* 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.
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/string.h>
|
|
|
|
#include <asm/pci-bridge.h>
|
|
#include <asm/semaphore.h>
|
|
#include <asm/rtas.h>
|
|
#include <asm/vio.h>
|
|
|
|
#include "../pci.h"
|
|
#include "rpaphp.h"
|
|
#include "rpadlpar.h"
|
|
|
|
static DECLARE_MUTEX(rpadlpar_sem);
|
|
|
|
#define DLPAR_MODULE_NAME "rpadlpar_io"
|
|
|
|
#define NODE_TYPE_VIO 1
|
|
#define NODE_TYPE_SLOT 2
|
|
#define NODE_TYPE_PHB 3
|
|
|
|
static struct device_node *find_vio_slot_node(char *drc_name)
|
|
{
|
|
struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
|
|
struct device_node *dn = NULL;
|
|
char *name;
|
|
int rc;
|
|
|
|
if (!parent)
|
|
return NULL;
|
|
|
|
while ((dn = of_get_next_child(parent, dn))) {
|
|
rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
|
|
if ((rc == 0) && (!strcmp(drc_name, name)))
|
|
break;
|
|
}
|
|
|
|
return dn;
|
|
}
|
|
|
|
/* Find dlpar-capable pci node that contains the specified name and type */
|
|
static struct device_node *find_php_slot_pci_node(char *drc_name,
|
|
char *drc_type)
|
|
{
|
|
struct device_node *np = NULL;
|
|
char *name;
|
|
char *type;
|
|
int rc;
|
|
|
|
while ((np = of_find_node_by_type(np, "pci"))) {
|
|
rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
|
|
if (rc == 0)
|
|
if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
|
|
break;
|
|
}
|
|
|
|
return np;
|
|
}
|
|
|
|
static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
|
|
{
|
|
struct device_node *dn;
|
|
|
|
dn = find_php_slot_pci_node(drc_name, "SLOT");
|
|
if (dn) {
|
|
*node_type = NODE_TYPE_SLOT;
|
|
return dn;
|
|
}
|
|
|
|
dn = find_php_slot_pci_node(drc_name, "PHB");
|
|
if (dn) {
|
|
*node_type = NODE_TYPE_PHB;
|
|
return dn;
|
|
}
|
|
|
|
dn = find_vio_slot_node(drc_name);
|
|
if (dn) {
|
|
*node_type = NODE_TYPE_VIO;
|
|
return dn;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct slot *find_slot(struct device_node *dn)
|
|
{
|
|
struct list_head *tmp, *n;
|
|
struct slot *slot;
|
|
|
|
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
|
|
slot = list_entry(tmp, struct slot, rpaphp_slot_list);
|
|
if (slot->dn == dn)
|
|
return slot;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void rpadlpar_claim_one_bus(struct pci_bus *b)
|
|
{
|
|
struct list_head *ld;
|
|
struct pci_bus *child_bus;
|
|
|
|
for (ld = b->devices.next; ld != &b->devices; ld = ld->next) {
|
|
struct pci_dev *dev = pci_dev_b(ld);
|
|
int i;
|
|
|
|
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
|
struct resource *r = &dev->resource[i];
|
|
|
|
if (r->parent || !r->start || !r->flags)
|
|
continue;
|
|
rpaphp_claim_resource(dev, i);
|
|
}
|
|
}
|
|
|
|
list_for_each_entry(child_bus, &b->children, node)
|
|
rpadlpar_claim_one_bus(child_bus);
|
|
}
|
|
|
|
static int pci_add_secondary_bus(struct device_node *dn,
|
|
struct pci_dev *bridge_dev)
|
|
{
|
|
struct pci_dn *pdn = dn->data;
|
|
struct pci_controller *hose = pdn->phb;
|
|
struct pci_bus *child;
|
|
u8 sec_busno;
|
|
|
|
/* Get busno of downstream bus */
|
|
pci_read_config_byte(bridge_dev, PCI_SECONDARY_BUS, &sec_busno);
|
|
|
|
/* Allocate and add to children of bridge_dev->bus */
|
|
child = pci_add_new_bus(bridge_dev->bus, bridge_dev, sec_busno);
|
|
if (!child) {
|
|
printk(KERN_ERR "%s: could not add secondary bus\n", __FUNCTION__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
sprintf(child->name, "PCI Bus #%02x", child->number);
|
|
|
|
/* Fixup subordinate bridge bases and resources */
|
|
pcibios_fixup_bus(child);
|
|
|
|
/* Claim new bus resources */
|
|
rpadlpar_claim_one_bus(bridge_dev->bus);
|
|
|
|
if (hose->last_busno < child->number)
|
|
hose->last_busno = child->number;
|
|
|
|
pdn->bussubno = child->number;
|
|
|
|
/* ioremap() for child bus, which may or may not succeed */
|
|
remap_bus_range(child);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
|
|
struct device_node *dev_dn)
|
|
{
|
|
struct pci_dev *tmp = NULL;
|
|
struct device_node *child_dn;
|
|
|
|
list_for_each_entry(tmp, &parent->devices, bus_list) {
|
|
child_dn = pci_device_to_OF_node(tmp);
|
|
if (child_dn == dev_dn)
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
|
|
{
|
|
struct pci_dn *pdn = dn->data;
|
|
struct pci_controller *hose = pdn->phb;
|
|
struct pci_dev *dev = NULL;
|
|
|
|
/* Scan phb bus for EADS device, adding new one to bus->devices */
|
|
if (!pci_scan_single_device(hose->bus, pdn->devfn)) {
|
|
printk(KERN_ERR "%s: found no device on bus\n", __FUNCTION__);
|
|
return NULL;
|
|
}
|
|
|
|
/* Add new devices to global lists. Register in proc, sysfs. */
|
|
pci_bus_add_devices(hose->bus);
|
|
|
|
/* Confirm new bridge dev was created */
|
|
dev = dlpar_find_new_dev(hose->bus, dn);
|
|
if (dev) {
|
|
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
|
|
printk(KERN_ERR "%s: unexpected header type %d\n",
|
|
__FUNCTION__, dev->hdr_type);
|
|
return NULL;
|
|
}
|
|
|
|
if (pci_add_secondary_bus(dn, dev))
|
|
return NULL;
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
|
|
{
|
|
struct pci_dev *dev;
|
|
int rc;
|
|
|
|
if (rpaphp_find_pci_bus(dn))
|
|
return -EINVAL;
|
|
|
|
/* Add pci bus */
|
|
dev = dlpar_pci_add_bus(dn);
|
|
if (!dev) {
|
|
printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
|
|
drc_name);
|
|
return -EIO;
|
|
}
|
|
|
|
if (dn->child) {
|
|
rc = rpaphp_config_pci_adapter(dev->subordinate);
|
|
if (rc < 0) {
|
|
printk(KERN_ERR "%s: unable to enable slot %s\n",
|
|
__FUNCTION__, drc_name);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
/* Add hotplug slot */
|
|
if (rpaphp_add_slot(dn)) {
|
|
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
|
|
__FUNCTION__, drc_name);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dlpar_remove_root_bus(struct pci_controller *phb)
|
|
{
|
|
struct pci_bus *phb_bus;
|
|
int rc;
|
|
|
|
phb_bus = phb->bus;
|
|
if (!(list_empty(&phb_bus->children) &&
|
|
list_empty(&phb_bus->devices))) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
rc = pcibios_remove_root_bus(phb);
|
|
if (rc)
|
|
return -EIO;
|
|
|
|
device_unregister(phb_bus->bridge);
|
|
pci_remove_bus(phb_bus);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
|
|
{
|
|
struct slot *slot;
|
|
struct pci_dn *pdn;
|
|
int rc = 0;
|
|
|
|
if (!rpaphp_find_pci_bus(dn))
|
|
return -EINVAL;
|
|
|
|
slot = find_slot(dn);
|
|
if (slot) {
|
|
/* Remove hotplug slot */
|
|
if (rpaphp_remove_slot(slot)) {
|
|
printk(KERN_ERR
|
|
"%s: unable to remove hotplug slot %s\n",
|
|
__FUNCTION__, drc_name);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
pdn = dn->data;
|
|
BUG_ON(!pdn || !pdn->phb);
|
|
rc = dlpar_remove_root_bus(pdn->phb);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
pdn->phb = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dlpar_add_phb(char *drc_name, struct device_node *dn)
|
|
{
|
|
struct pci_controller *phb;
|
|
|
|
if (PCI_DN(dn)->phb) {
|
|
/* PHB already exists */
|
|
return -EINVAL;
|
|
}
|
|
|
|
phb = init_phb_dynamic(dn);
|
|
if (!phb)
|
|
return -EIO;
|
|
|
|
if (rpaphp_add_slot(dn)) {
|
|
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
|
|
__FUNCTION__, drc_name);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
|
|
{
|
|
if (vio_find_node(dn))
|
|
return -EINVAL;
|
|
|
|
if (!vio_register_device_node(dn)) {
|
|
printk(KERN_ERR
|
|
"%s: failed to register vio node %s\n",
|
|
__FUNCTION__, drc_name);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* dlpar_add_slot - DLPAR add an I/O Slot
|
|
* @drc_name: drc-name of newly added slot
|
|
*
|
|
* Make the hotplug module and the kernel aware
|
|
* of a newly added I/O Slot.
|
|
* Return Codes -
|
|
* 0 Success
|
|
* -ENODEV Not a valid drc_name
|
|
* -EINVAL Slot already added
|
|
* -ERESTARTSYS Signalled before obtaining lock
|
|
* -EIO Internal PCI Error
|
|
*/
|
|
int dlpar_add_slot(char *drc_name)
|
|
{
|
|
struct device_node *dn = NULL;
|
|
int node_type;
|
|
int rc = -EIO;
|
|
|
|
if (down_interruptible(&rpadlpar_sem))
|
|
return -ERESTARTSYS;
|
|
|
|
/* Find newly added node */
|
|
dn = find_dlpar_node(drc_name, &node_type);
|
|
if (!dn) {
|
|
rc = -ENODEV;
|
|
goto exit;
|
|
}
|
|
|
|
switch (node_type) {
|
|
case NODE_TYPE_VIO:
|
|
rc = dlpar_add_vio_slot(drc_name, dn);
|
|
break;
|
|
case NODE_TYPE_SLOT:
|
|
rc = dlpar_add_pci_slot(drc_name, dn);
|
|
break;
|
|
case NODE_TYPE_PHB:
|
|
rc = dlpar_add_phb(drc_name, dn);
|
|
break;
|
|
}
|
|
|
|
printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
|
|
exit:
|
|
up(&rpadlpar_sem);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
|
|
* @drc_name: drc-name of newly added slot
|
|
*
|
|
* Remove the kernel and hotplug representations
|
|
* of an I/O Slot.
|
|
* Return Codes:
|
|
* 0 Success
|
|
* -EINVAL Vio dev doesn't exist
|
|
*/
|
|
static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
|
|
{
|
|
struct vio_dev *vio_dev;
|
|
|
|
vio_dev = vio_find_node(dn);
|
|
if (!vio_dev)
|
|
return -EINVAL;
|
|
|
|
vio_unregister_device(vio_dev);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* dlpar_remove_slot - DLPAR remove a PCI I/O Slot
|
|
* @drc_name: drc-name of newly added slot
|
|
*
|
|
* Remove the kernel and hotplug representations
|
|
* of a PCI I/O Slot.
|
|
* Return Codes:
|
|
* 0 Success
|
|
* -ENODEV Not a valid drc_name
|
|
* -EIO Internal PCI Error
|
|
*/
|
|
int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
|
|
{
|
|
struct pci_bus *bus;
|
|
struct slot *slot;
|
|
|
|
bus = rpaphp_find_pci_bus(dn);
|
|
if (!bus)
|
|
return -EINVAL;
|
|
|
|
slot = find_slot(dn);
|
|
if (slot) {
|
|
/* Remove hotplug slot */
|
|
if (rpaphp_remove_slot(slot)) {
|
|
printk(KERN_ERR
|
|
"%s: unable to remove hotplug slot %s\n",
|
|
__FUNCTION__, drc_name);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if (unmap_bus_range(bus)) {
|
|
printk(KERN_ERR "%s: failed to unmap bus range\n",
|
|
__FUNCTION__);
|
|
return -ERANGE;
|
|
}
|
|
|
|
BUG_ON(!bus->self);
|
|
pci_remove_bus_device(bus->self);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* dlpar_remove_slot - DLPAR remove an I/O Slot
|
|
* @drc_name: drc-name of newly added slot
|
|
*
|
|
* Remove the kernel and hotplug representations
|
|
* of an I/O Slot.
|
|
* Return Codes:
|
|
* 0 Success
|
|
* -ENODEV Not a valid drc_name
|
|
* -EINVAL Slot already removed
|
|
* -ERESTARTSYS Signalled before obtaining lock
|
|
* -EIO Internal Error
|
|
*/
|
|
int dlpar_remove_slot(char *drc_name)
|
|
{
|
|
struct device_node *dn;
|
|
int node_type;
|
|
int rc = 0;
|
|
|
|
if (down_interruptible(&rpadlpar_sem))
|
|
return -ERESTARTSYS;
|
|
|
|
dn = find_dlpar_node(drc_name, &node_type);
|
|
if (!dn) {
|
|
rc = -ENODEV;
|
|
goto exit;
|
|
}
|
|
|
|
switch (node_type) {
|
|
case NODE_TYPE_VIO:
|
|
rc = dlpar_remove_vio_slot(drc_name, dn);
|
|
break;
|
|
case NODE_TYPE_PHB:
|
|
rc = dlpar_remove_phb(drc_name, dn);
|
|
break;
|
|
case NODE_TYPE_SLOT:
|
|
rc = dlpar_remove_pci_slot(drc_name, dn);
|
|
break;
|
|
}
|
|
printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
|
|
exit:
|
|
up(&rpadlpar_sem);
|
|
return rc;
|
|
}
|
|
|
|
static inline int is_dlpar_capable(void)
|
|
{
|
|
int rc = rtas_token("ibm,configure-connector");
|
|
|
|
return (int) (rc != RTAS_UNKNOWN_SERVICE);
|
|
}
|
|
|
|
int __init rpadlpar_io_init(void)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!is_dlpar_capable()) {
|
|
printk(KERN_WARNING "%s: partition not DLPAR capable\n",
|
|
__FUNCTION__);
|
|
return -EPERM;
|
|
}
|
|
|
|
rc = dlpar_sysfs_init();
|
|
return rc;
|
|
}
|
|
|
|
void rpadlpar_io_exit(void)
|
|
{
|
|
dlpar_sysfs_exit();
|
|
return;
|
|
}
|
|
|
|
module_init(rpadlpar_io_init);
|
|
module_exit(rpadlpar_io_exit);
|
|
MODULE_LICENSE("GPL");
|