| /* |
| * PCI HotPlug Utility functions |
| * |
| * Copyright (C) 1995,2001 Compaq Computer Corporation |
| * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) |
| * Copyright (C) 2001 IBM Corp. |
| * |
| * 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. |
| * |
| * Send feedback to <greg@kroah.com> |
| * |
| */ |
| |
| #include <linux/config.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/slab.h> |
| #include <linux/pci.h> |
| #include "pci_hotplug.h" |
| |
| |
| #if !defined(CONFIG_HOTPLUG_PCI_MODULE) |
| #define MY_NAME "pci_hotplug" |
| #else |
| #define MY_NAME THIS_MODULE->name |
| #endif |
| |
| #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0) |
| #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) |
| #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) |
| #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) |
| |
| |
| /* local variables */ |
| static int debug; |
| |
| static int build_dev (struct pci_bus *bus, unsigned int devfn, struct pci_dev **pci_dev) |
| { |
| struct pci_dev *my_dev; |
| |
| my_dev = kmalloc (sizeof (struct pci_dev), GFP_KERNEL); |
| if (!my_dev) |
| return -ENOMEM; |
| |
| memset(my_dev, 0, sizeof(struct pci_dev)); |
| |
| my_dev->devfn = devfn; |
| my_dev->bus = bus; |
| my_dev->sysdata = bus->sysdata; |
| *pci_dev = my_dev; |
| return 0; |
| } |
| |
| /** |
| * pci_bus_read_config_byte - read a byte from a pci device |
| * @bus: pointer to the parent bus of the pci device to read from |
| * @devfn: the device / function of the pci device to read from |
| * @where: the location in the pci address space to read from |
| * @value: pointer to where to place the data read |
| * |
| * Like pci_read_config_byte() but works for pci devices that do not have a |
| * pci_dev structure set up yet. |
| * Returns 0 on success. |
| */ |
| int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *value) |
| { |
| struct pci_dev *dev = NULL; |
| int result; |
| |
| dbg("%p, %d, %d, %p\n", bus, devfn, where, value); |
| dev = pci_find_slot(bus->number, devfn); |
| if (dev) { |
| dbg("using native pci_dev\n"); |
| return pci_read_config_byte (dev, where, value); |
| } |
| |
| result = build_dev(bus, devfn, &dev); |
| if (result) |
| return result; |
| result = pci_read_config_byte(dev, where, value); |
| kfree (dev); |
| return result; |
| } |
| |
| /** |
| * pci_bus_read_config_word - read a word from a pci device |
| * @bus: pointer to the parent bus of the pci device to read from |
| * @devfn: the device / function of the pci device to read from |
| * @where: the location on the pci address space to read from |
| * @value: pointer to where to place the data read |
| * |
| * Like pci_read_config_word() but works for pci devices that do not have a |
| * pci_dev structure set up yet. |
| * Returns 0 on success. |
| */ |
| int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *value) |
| { |
| struct pci_dev *dev = NULL; |
| int result; |
| |
| dbg("%p, %d, %d, %p\n", bus, devfn, where, value); |
| dev = pci_find_slot(bus->number, devfn); |
| if (dev) { |
| dbg("using native pci_dev\n"); |
| return pci_read_config_word (dev, where, value); |
| } |
| |
| result = build_dev(bus, devfn, &dev); |
| if (result) |
| return result; |
| result = pci_read_config_word(dev, where, value); |
| kfree (dev); |
| return result; |
| } |
| |
| /** |
| * pci_bus_read_config_dword - read a dword from a pci device |
| * @bus: pointer to the parent bus of the pci device to read from |
| * @devfn: the device / function of the pci device to read from |
| * @where: the location on the pci address space to read from |
| * @value: pointer to where to place the data read |
| * |
| * Like pci_read_config_dword() but works for pci devices that do not have a |
| * pci_dev structure set up yet. |
| * Returns 0 on success. |
| */ |
| int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *value) |
| { |
| struct pci_dev *dev = NULL; |
| int result; |
| |
| dbg("%p, %d, %d, %p\n", bus, devfn, where, value); |
| dev = pci_find_slot(bus->number, devfn); |
| if (dev) { |
| dbg("using native pci_dev\n"); |
| return pci_read_config_dword (dev, where, value); |
| } |
| |
| result = build_dev(bus, devfn, &dev); |
| if (result) |
| return result; |
| result = pci_read_config_dword(dev, where, value); |
| kfree (dev); |
| return result; |
| } |
| |
| /** |
| * pci_bus_write_config_byte - write a byte to a pci device |
| * @bus: pointer to the parent bus of the pci device to read from |
| * @devfn: the device / function of the pci device to read from |
| * @where: the location on the pci address space to write to |
| * @value: the value to write to the pci device |
| * |
| * Like pci_write_config_byte() but works for pci devices that do not have a |
| * pci_dev structure set up yet. |
| * Returns 0 on success. |
| */ |
| int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 value) |
| { |
| struct pci_dev *dev = NULL; |
| int result; |
| |
| dbg("%p, %d, %d, %d\n", bus, devfn, where, value); |
| dev = pci_find_slot(bus->number, devfn); |
| if (dev) { |
| dbg("using native pci_dev\n"); |
| return pci_write_config_byte (dev, where, value); |
| } |
| |
| result = build_dev(bus, devfn, &dev); |
| if (result) |
| return result; |
| result = pci_write_config_byte(dev, where, value); |
| kfree (dev); |
| return result; |
| } |
| |
| /** |
| * pci_bus_write_config_word - write a word to a pci device |
| * @bus: pointer to the parent bus of the pci device to read from |
| * @devfn: the device / function of the pci device to read from |
| * @where: the location on the pci address space to write to |
| * @value: the value to write to the pci device |
| * |
| * Like pci_write_config_word() but works for pci devices that do not have a |
| * pci_dev structure set up yet. |
| * Returns 0 on success. |
| */ |
| int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 value) |
| { |
| struct pci_dev *dev = NULL; |
| int result; |
| |
| dbg("%p, %d, %d, %d\n", bus, devfn, where, value); |
| dev = pci_find_slot(bus->number, devfn); |
| if (dev) { |
| dbg("using native pci_dev\n"); |
| return pci_write_config_word (dev, where, value); |
| } |
| |
| result = build_dev(bus, devfn, &dev); |
| if (result) |
| return result; |
| result = pci_write_config_word(dev, where, value); |
| kfree (dev); |
| return result; |
| } |
| |
| /** |
| * pci_bus_write_config_dword - write a dword to a pci device |
| * @bus: pointer to the parent bus of the pci device to read from |
| * @devfn: the device / function of the pci device to read from |
| * @where: the location on the pci address space to write to |
| * @value: the value to write to the pci device |
| * |
| * Like pci_write_config_dword() but works for pci devices that do not have a |
| * pci_dev structure set up yet. |
| * Returns 0 on success. |
| */ |
| int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 value) |
| { |
| struct pci_dev *dev = NULL; |
| int result; |
| |
| dbg("%p, %d, %d, %d\n", bus, devfn, where, value); |
| dev = pci_find_slot(bus->number, devfn); |
| if (dev) { |
| dbg("using native pci_dev\n"); |
| return pci_write_config_dword (dev, where, value); |
| } |
| |
| result = build_dev(bus, devfn, &dev); |
| if (result) |
| return result; |
| result = pci_write_config_dword(dev, where, value); |
| kfree (dev); |
| return result; |
| } |
| |
| /* |
| * This is code that scans the pci buses. |
| * Every bus and every function is presented to a custom |
| * function that can act upon it. |
| */ |
| |
| static int pci_visit_bus (struct pci_visit * fn, struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_parent) |
| { |
| struct list_head *ln; |
| struct pci_dev *dev; |
| struct pci_dev_wrapped wrapped_dev; |
| int result = 0; |
| |
| dbg("scanning bus %02x\n", wrapped_bus->bus->number); |
| |
| if (fn->pre_visit_pci_bus) { |
| result = fn->pre_visit_pci_bus(wrapped_bus, wrapped_parent); |
| if (result) |
| return result; |
| } |
| |
| ln = wrapped_bus->bus->devices.next; |
| while (ln != &wrapped_bus->bus->devices) { |
| dev = pci_dev_b(ln); |
| ln = ln->next; |
| |
| memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); |
| wrapped_dev.dev = dev; |
| |
| result = pci_visit_dev(fn, &wrapped_dev, wrapped_bus); |
| if (result) |
| return result; |
| } |
| |
| if (fn->post_visit_pci_bus) |
| result = fn->post_visit_pci_bus(wrapped_bus, wrapped_parent); |
| |
| return result; |
| } |
| |
| |
| static int pci_visit_bridge (struct pci_visit * fn, struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_parent) |
| { |
| struct pci_bus *bus = wrapped_dev->dev->subordinate; |
| struct pci_bus_wrapped wrapped_bus; |
| int result; |
| |
| memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); |
| wrapped_bus.bus = bus; |
| |
| dbg("scanning bridge %02x, %02x\n", wrapped_dev->dev->devfn >> 3, |
| wrapped_dev->dev->devfn & 0x7); |
| |
| if (fn->visit_pci_dev) { |
| result = fn->visit_pci_dev(wrapped_dev, wrapped_parent); |
| if (result) |
| return result; |
| } |
| |
| result = pci_visit_bus(fn, &wrapped_bus, wrapped_dev); |
| return result; |
| } |
| |
| |
| int pci_visit_dev (struct pci_visit *fn, struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_parent) |
| { |
| struct pci_dev* dev = wrapped_dev ? wrapped_dev->dev : NULL; |
| int result = 0; |
| |
| if (!dev) |
| return 0; |
| |
| if (fn->pre_visit_pci_dev) { |
| result = fn->pre_visit_pci_dev(wrapped_dev, wrapped_parent); |
| if (result) |
| return result; |
| } |
| |
| switch (dev->class >> 8) { |
| case PCI_CLASS_BRIDGE_PCI: |
| result = pci_visit_bridge(fn, wrapped_dev, |
| wrapped_parent); |
| if (result) |
| return result; |
| break; |
| default: |
| dbg("scanning device %02x, %02x\n", |
| PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
| if (fn->visit_pci_dev) { |
| result = fn->visit_pci_dev (wrapped_dev, |
| wrapped_parent); |
| if (result) |
| return result; |
| } |
| } |
| |
| if (fn->post_visit_pci_dev) |
| result = fn->post_visit_pci_dev(wrapped_dev, wrapped_parent); |
| |
| return result; |
| } |
| |
| /* Compatibility wrapper functions */ |
| |
| static struct pci_bus *alloc_bus(struct pci_ops *ops, u8 bus_nr) |
| { |
| struct pci_bus *bus = kmalloc(sizeof(struct pci_bus), GFP_KERNEL); |
| if (!bus) |
| return NULL; |
| memset(bus, 0, sizeof(struct pci_bus)); |
| bus->number = bus_nr; |
| bus->ops = ops; |
| return bus; |
| } |
| |
| int pci_read_config_byte_nodev(struct pci_ops *ops, u8 bus_nr, u8 slot, u8 function, int where, u8 *value) |
| { |
| int result; |
| struct pci_bus *bus = alloc_bus(ops, bus_nr); |
| if (!bus) |
| return -ENOMEM; |
| result = pci_bus_read_config_byte(bus, PCI_DEVFN(slot, function), |
| where, value); |
| kfree(bus); |
| return result; |
| } |
| |
| int pci_read_config_word_nodev(struct pci_ops *ops, u8 bus_nr, u8 slot, u8 function, int where, u16 *value) |
| { |
| int result; |
| struct pci_bus *bus = alloc_bus(ops, bus_nr); |
| if (!bus) |
| return -ENOMEM; |
| result = pci_bus_read_config_word(bus, PCI_DEVFN(slot, function), |
| where, value); |
| kfree(bus); |
| return result; |
| } |
| |
| int pci_read_config_dword_nodev(struct pci_ops *ops, u8 bus_nr, u8 slot, u8 function, int where, u32 *value) |
| { |
| int result; |
| struct pci_bus *bus = alloc_bus(ops, bus_nr); |
| if (!bus) |
| return -ENOMEM; |
| result = pci_bus_read_config_dword(bus, PCI_DEVFN(slot, function), |
| where, value); |
| kfree(bus); |
| return result; |
| } |
| |
| int pci_write_config_byte_nodev(struct pci_ops *ops, u8 bus_nr, u8 slot, u8 function, int where, u8 value) |
| { |
| int result; |
| struct pci_bus *bus = alloc_bus(ops, bus_nr); |
| if (!bus) |
| return -ENOMEM; |
| result = pci_bus_write_config_byte(bus, PCI_DEVFN(slot, function), |
| where, value); |
| kfree(bus); |
| return result; |
| } |
| |
| int pci_write_config_word_nodev(struct pci_ops *ops, u8 bus_nr, u8 slot, u8 function, int where, u16 value) |
| { |
| int result; |
| struct pci_bus *bus = alloc_bus(ops, bus_nr); |
| if (!bus) |
| return -ENOMEM; |
| result = pci_bus_write_config_word(bus, PCI_DEVFN(slot, function), |
| where, value); |
| kfree(bus); |
| return result; |
| } |
| |
| int pci_write_config_dword_nodev(struct pci_ops *ops, u8 bus_nr, u8 slot, u8 function, int where, u32 value) |
| { |
| int result; |
| struct pci_bus *bus = alloc_bus(ops, bus_nr); |
| if (!bus) |
| return -ENOMEM; |
| result = pci_bus_write_config_dword(bus, PCI_DEVFN(slot, function), |
| where, value); |
| kfree(bus); |
| return result; |
| } |
| |
| EXPORT_SYMBOL(pci_visit_dev); |
| EXPORT_SYMBOL(pci_bus_read_config_byte); |
| EXPORT_SYMBOL(pci_bus_read_config_word); |
| EXPORT_SYMBOL(pci_bus_read_config_dword); |
| EXPORT_SYMBOL(pci_bus_write_config_byte); |
| EXPORT_SYMBOL(pci_bus_write_config_word); |
| EXPORT_SYMBOL(pci_bus_write_config_dword); |
| EXPORT_SYMBOL(pci_read_config_byte_nodev); |
| EXPORT_SYMBOL(pci_read_config_word_nodev); |
| EXPORT_SYMBOL(pci_read_config_dword_nodev); |
| EXPORT_SYMBOL(pci_write_config_byte_nodev); |
| EXPORT_SYMBOL(pci_write_config_word_nodev); |
| EXPORT_SYMBOL(pci_write_config_dword_nodev); |
| |