/drivers/pci/slot.c
C | 419 lines | 274 code | 57 blank | 88 comment | 33 complexity | 02356973c4494901ac7bf6b7ea182ceb MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * drivers/pci/slot.c
- * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
- * Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P.
- * Alex Chiang <achiang@hp.com>
- */
- #include <linux/kobject.h>
- #include <linux/slab.h>
- #include <linux/pci.h>
- #include <linux/err.h>
- #include "pci.h"
- struct kset *pci_slots_kset;
- EXPORT_SYMBOL_GPL(pci_slots_kset);
- static ssize_t pci_slot_attr_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
- {
- struct pci_slot *slot = to_pci_slot(kobj);
- struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
- return attribute->show ? attribute->show(slot, buf) : -EIO;
- }
- static ssize_t pci_slot_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t len)
- {
- struct pci_slot *slot = to_pci_slot(kobj);
- struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
- return attribute->store ? attribute->store(slot, buf, len) : -EIO;
- }
- static const struct sysfs_ops pci_slot_sysfs_ops = {
- .show = pci_slot_attr_show,
- .store = pci_slot_attr_store,
- };
- static ssize_t address_read_file(struct pci_slot *slot, char *buf)
- {
- if (slot->number == 0xff)
- return sprintf(buf, "%04x:%02x\n",
- pci_domain_nr(slot->bus),
- slot->bus->number);
- else
- return sprintf(buf, "%04x:%02x:%02x\n",
- pci_domain_nr(slot->bus),
- slot->bus->number,
- slot->number);
- }
- /* these strings match up with the values in pci_bus_speed */
- static const char *pci_bus_speed_strings[] = {
- "33 MHz PCI", /* 0x00 */
- "66 MHz PCI", /* 0x01 */
- "66 MHz PCI-X", /* 0x02 */
- "100 MHz PCI-X", /* 0x03 */
- "133 MHz PCI-X", /* 0x04 */
- NULL, /* 0x05 */
- NULL, /* 0x06 */
- NULL, /* 0x07 */
- NULL, /* 0x08 */
- "66 MHz PCI-X 266", /* 0x09 */
- "100 MHz PCI-X 266", /* 0x0a */
- "133 MHz PCI-X 266", /* 0x0b */
- "Unknown AGP", /* 0x0c */
- "1x AGP", /* 0x0d */
- "2x AGP", /* 0x0e */
- "4x AGP", /* 0x0f */
- "8x AGP", /* 0x10 */
- "66 MHz PCI-X 533", /* 0x11 */
- "100 MHz PCI-X 533", /* 0x12 */
- "133 MHz PCI-X 533", /* 0x13 */
- "2.5 GT/s PCIe", /* 0x14 */
- "5.0 GT/s PCIe", /* 0x15 */
- "8.0 GT/s PCIe", /* 0x16 */
- };
- static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
- {
- const char *speed_string;
- if (speed < ARRAY_SIZE(pci_bus_speed_strings))
- speed_string = pci_bus_speed_strings[speed];
- else
- speed_string = "Unknown";
- return sprintf(buf, "%s\n", speed_string);
- }
- static ssize_t max_speed_read_file(struct pci_slot *slot, char *buf)
- {
- return bus_speed_read(slot->bus->max_bus_speed, buf);
- }
- static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf)
- {
- return bus_speed_read(slot->bus->cur_bus_speed, buf);
- }
- static void pci_slot_release(struct kobject *kobj)
- {
- struct pci_dev *dev;
- struct pci_slot *slot = to_pci_slot(kobj);
- dev_dbg(&slot->bus->dev, "dev %02x, released physical slot %s\n",
- slot->number, pci_slot_name(slot));
- list_for_each_entry(dev, &slot->bus->devices, bus_list)
- if (PCI_SLOT(dev->devfn) == slot->number)
- dev->slot = NULL;
- list_del(&slot->list);
- kfree(slot);
- }
- static struct pci_slot_attribute pci_slot_attr_address =
- __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
- static struct pci_slot_attribute pci_slot_attr_max_speed =
- __ATTR(max_bus_speed, (S_IFREG | S_IRUGO), max_speed_read_file, NULL);
- static struct pci_slot_attribute pci_slot_attr_cur_speed =
- __ATTR(cur_bus_speed, (S_IFREG | S_IRUGO), cur_speed_read_file, NULL);
- static struct attribute *pci_slot_default_attrs[] = {
- &pci_slot_attr_address.attr,
- &pci_slot_attr_max_speed.attr,
- &pci_slot_attr_cur_speed.attr,
- NULL,
- };
- static struct kobj_type pci_slot_ktype = {
- .sysfs_ops = &pci_slot_sysfs_ops,
- .release = &pci_slot_release,
- .default_attrs = pci_slot_default_attrs,
- };
- static char *make_slot_name(const char *name)
- {
- char *new_name;
- int len, max, dup;
- new_name = kstrdup(name, GFP_KERNEL);
- if (!new_name)
- return NULL;
- /*
- * Make sure we hit the realloc case the first time through the
- * loop. 'len' will be strlen(name) + 3 at that point which is
- * enough space for "name-X" and the trailing NUL.
- */
- len = strlen(name) + 2;
- max = 1;
- dup = 1;
- for (;;) {
- struct kobject *dup_slot;
- dup_slot = kset_find_obj(pci_slots_kset, new_name);
- if (!dup_slot)
- break;
- kobject_put(dup_slot);
- if (dup == max) {
- len++;
- max *= 10;
- kfree(new_name);
- new_name = kmalloc(len, GFP_KERNEL);
- if (!new_name)
- break;
- }
- sprintf(new_name, "%s-%d", name, dup++);
- }
- return new_name;
- }
- static int rename_slot(struct pci_slot *slot, const char *name)
- {
- int result = 0;
- char *slot_name;
- if (strcmp(pci_slot_name(slot), name) == 0)
- return result;
- slot_name = make_slot_name(name);
- if (!slot_name)
- return -ENOMEM;
- result = kobject_rename(&slot->kobj, slot_name);
- kfree(slot_name);
- return result;
- }
- static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr)
- {
- struct pci_slot *slot;
- /*
- * We already hold pci_bus_sem so don't worry
- */
- list_for_each_entry(slot, &parent->slots, list)
- if (slot->number == slot_nr) {
- kobject_get(&slot->kobj);
- return slot;
- }
- return NULL;
- }
- /**
- * pci_create_slot - create or increment refcount for physical PCI slot
- * @parent: struct pci_bus of parent bridge
- * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder
- * @name: user visible string presented in /sys/bus/pci/slots/<name>
- * @hotplug: set if caller is hotplug driver, NULL otherwise
- *
- * PCI slots have first class attributes such as address, speed, width,
- * and a &struct pci_slot is used to manage them. This interface will
- * either return a new &struct pci_slot to the caller, or if the pci_slot
- * already exists, its refcount will be incremented.
- *
- * Slots are uniquely identified by a @pci_bus, @slot_nr tuple.
- *
- * There are known platforms with broken firmware that assign the same
- * name to multiple slots. Workaround these broken platforms by renaming
- * the slots on behalf of the caller. If firmware assigns name N to
- * multiple slots:
- *
- * The first slot is assigned N
- * The second slot is assigned N-1
- * The third slot is assigned N-2
- * etc.
- *
- * Placeholder slots:
- * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
- * a slot. There is one notable exception - pSeries (rpaphp), where the
- * @slot_nr cannot be determined until a device is actually inserted into
- * the slot. In this scenario, the caller may pass -1 for @slot_nr.
- *
- * The following semantics are imposed when the caller passes @slot_nr ==
- * -1. First, we no longer check for an existing %struct pci_slot, as there
- * may be many slots with @slot_nr of -1. The other change in semantics is
- * user-visible, which is the 'address' parameter presented in sysfs will
- * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
- * %struct pci_bus and bb is the bus number. In other words, the devfn of
- * the 'placeholder' slot will not be displayed.
- */
- struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
- const char *name,
-