Commit c0638a45 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/hotplug'

  - Simplify SHPC existence/permission checks (Bjorn Helgaas)

  - Remove hotplug sample skeleton driver (Lukas Wunner)

  - Convert pciehp to threaded IRQ handling (Lukas Wunner)

  - Improve pciehp tolerance of missed events and initially unstable links
    (Lukas Wunner)

  - Clear spurious pciehp events on resume (Lukas Wunner)

  - Add pciehp runtime PM support, including for Thunderbolt controllers
    (Lukas Wunner)

  - Support interrupts from pciehp bridges in D3hot (Lukas Wunner)

* pci/hotplug:
  PCI: pciehp: Deduplicate presence check on probe & resume
  PCI: pciehp: Avoid implicit fallthroughs in switch statements
  PCI: Whitelist Thunderbolt ports for runtime D3
  PCI: Whitelist native hotplug ports for runtime D3
  PCI: sysfs: Resume to D0 on function reset
  PCI: pciehp: Resume parent to D0 on config space access
  PCI: pciehp: Resume to D0 on enable/disable
  PCI: pciehp: Support interrupts sent from D3hot
  PCI: pciehp: Obey compulsory command delay after resume
  PCI: pciehp: Clear spurious events earlier on resume
  PCI: portdrv: Deduplicate PM callback iterator
  PCI: pciehp: Avoid slot access during reset
  PCI: pciehp: Always enable occupied slot on probe
  PCI: pciehp: Become resilient to missed events
  PCI: pciehp: Tolerate initially unstable link
  PCI: pciehp: Declare pciehp_enable/disable_slot() static
  PCI: pciehp: Drop enable/disable lock
  PCI: pciehp: Enable/disable exclusively from IRQ thread
  PCI: pciehp: Track enable/disable status
  PCI: pciehp: Publish to user space last on probe
  PCI: hotplug: Demidlayer registration with the core
  PCI: pciehp: Drop slot workqueue
  PCI: pciehp: Handle events synchronously
  PCI: pciehp: Stop blinking on slot enable failure
  PCI: pciehp: Convert to threaded polling
  PCI: pciehp: Convert to threaded IRQ
  PCI: pciehp: Document struct slot and struct controller
  PCI: pciehp: Declare pciehp_unconfigure_device() void
  PCI: pciehp: Drop unnecessary NULL pointer check
  PCI: pciehp: Fix unprotected list iteration in IRQ handler
  PCI: pciehp: Fix use-after-free on unplug
  PCI: hotplug: Don't leak pci_slot on registration failure
  PCI: hotplug: Delete skeleton driver
  PCI: shpchp: Separate existence of SHPC and permission to use it
parents a8bcb5e5 4e6a1335
...@@ -73,20 +73,6 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) ...@@ -73,20 +73,6 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev)
acpi_handle chandle, handle; acpi_handle chandle, handle;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
/*
* Per PCI firmware specification, we should run the ACPI _OSC
* method to get control of hotplug hardware before using it. If
* an _OSC is missing, we look for an OSHP to do the same thing.
* To handle different BIOS behavior, we look for _OSC on a root
* bridge preferentially (according to PCI fw spec). Later for
* OSHP within the scope of the hotplug controller and its parents,
* up to the host bridge under which this controller exists.
*/
if (shpchp_is_native(pdev))
return 0;
/* If _OSC exists, we should not evaluate OSHP */
/* /*
* If there's no ACPI host bridge (i.e., ACPI support is compiled * If there's no ACPI host bridge (i.e., ACPI support is compiled
* into the kernel but the hardware platform doesn't support ACPI), * into the kernel but the hardware platform doesn't support ACPI),
...@@ -97,9 +83,25 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) ...@@ -97,9 +83,25 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev)
if (!root) if (!root)
return 0; return 0;
if (root->osc_support_set) /*
goto no_control; * If _OSC exists, it determines whether we're allowed to manage
* the SHPC. We executed it while enumerating the host bridge.
*/
if (root->osc_support_set) {
if (host->native_shpc_hotplug)
return 0;
return -ENODEV;
}
/*
* In the absence of _OSC, we're always allowed to manage the SHPC.
* However, if an OSHP method is present, we must execute it so the
* firmware can transfer control to the OS, e.g., direct interrupts
* to the OS instead of to the firmware.
*
* N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse
* searching up the ACPI hierarchy, so the loops below are suspect.
*/
handle = ACPI_HANDLE(&pdev->dev); handle = ACPI_HANDLE(&pdev->dev);
if (!handle) { if (!handle) {
/* /*
...@@ -128,7 +130,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) ...@@ -128,7 +130,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev)
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
break; break;
} }
no_control:
pci_info(pdev, "Cannot get control of SHPC hotplug\n"); pci_info(pdev, "Cannot get control of SHPC hotplug\n");
kfree(string.pointer); kfree(string.pointer);
return -ENODEV; return -ENODEV;
......
...@@ -254,20 +254,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) ...@@ -254,20 +254,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0; return 0;
} }
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
kfree(slot->hotplug_slot);
kfree(slot);
}
/* callback routine to initialize 'struct slot' for each slot */ /* callback routine to initialize 'struct slot' for each slot */
int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
unsigned int sun) unsigned int sun)
...@@ -287,7 +273,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, ...@@ -287,7 +273,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
slot->hotplug_slot->info = &slot->info; slot->hotplug_slot->info = &slot->info;
slot->hotplug_slot->private = slot; slot->hotplug_slot->private = slot;
slot->hotplug_slot->release = &release_slot;
slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
slot->acpi_slot = acpiphp_slot; slot->acpi_slot = acpiphp_slot;
...@@ -324,13 +309,12 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, ...@@ -324,13 +309,12 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
{ {
struct slot *slot = acpiphp_slot->slot; struct slot *slot = acpiphp_slot->slot;
int retval = 0;
pr_info("Slot [%s] unregistered\n", slot_name(slot)); pr_info("Slot [%s] unregistered\n", slot_name(slot));
retval = pci_hp_deregister(slot->hotplug_slot); pci_hp_deregister(slot->hotplug_slot);
if (retval) kfree(slot->hotplug_slot);
pr_err("pci_hp_deregister failed with error %d\n", retval); kfree(slot);
} }
......
...@@ -195,10 +195,8 @@ get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) ...@@ -195,10 +195,8 @@ get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0; return 0;
} }
static void release_slot(struct hotplug_slot *hotplug_slot) static void release_slot(struct slot *slot)
{ {
struct slot *slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot); kfree(slot->hotplug_slot);
pci_dev_put(slot->dev); pci_dev_put(slot->dev);
...@@ -253,7 +251,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) ...@@ -253,7 +251,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i); snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
hotplug_slot->private = slot; hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
hotplug_slot->ops = &cpci_hotplug_slot_ops; hotplug_slot->ops = &cpci_hotplug_slot_ops;
/* /*
...@@ -308,12 +305,8 @@ cpci_hp_unregister_bus(struct pci_bus *bus) ...@@ -308,12 +305,8 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
slots--; slots--;
dbg("deregistering slot %s", slot_name(slot)); dbg("deregistering slot %s", slot_name(slot));
status = pci_hp_deregister(slot->hotplug_slot); pci_hp_deregister(slot->hotplug_slot);
if (status) { release_slot(slot);
err("pci_hp_deregister failed with error %d",
status);
break;
}
} }
} }
up_write(&list_rwsem); up_write(&list_rwsem);
...@@ -623,6 +616,7 @@ cleanup_slots(void) ...@@ -623,6 +616,7 @@ cleanup_slots(void)
list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) { list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
list_del(&slot->slot_list); list_del(&slot->slot_list);
pci_hp_deregister(slot->hotplug_slot); pci_hp_deregister(slot->hotplug_slot);
release_slot(slot);
} }
cleanup_null: cleanup_null:
up_write(&list_rwsem); up_write(&list_rwsem);
......
...@@ -266,17 +266,6 @@ static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start, ...@@ -266,17 +266,6 @@ static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start,
return previous; return previous;
} }
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
}
static int ctrl_slot_cleanup(struct controller *ctrl) static int ctrl_slot_cleanup(struct controller *ctrl)
{ {
struct slot *old_slot, *next_slot; struct slot *old_slot, *next_slot;
...@@ -285,9 +274,11 @@ static int ctrl_slot_cleanup(struct controller *ctrl) ...@@ -285,9 +274,11 @@ static int ctrl_slot_cleanup(struct controller *ctrl)
ctrl->slot = NULL; ctrl->slot = NULL;
while (old_slot) { while (old_slot) {
/* memory will be freed by the release_slot callback */
next_slot = old_slot->next; next_slot = old_slot->next;
pci_hp_deregister(old_slot->hotplug_slot); pci_hp_deregister(old_slot->hotplug_slot);
kfree(old_slot->hotplug_slot->info);
kfree(old_slot->hotplug_slot);
kfree(old_slot);
old_slot = next_slot; old_slot = next_slot;
} }
...@@ -678,7 +669,6 @@ static int ctrl_slot_setup(struct controller *ctrl, ...@@ -678,7 +669,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
/* register this slot with the hotplug pci core */ /* register this slot with the hotplug pci core */
hotplug_slot->release = &release_slot;
hotplug_slot->private = slot; hotplug_slot->private = slot;
snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
hotplug_slot->ops = &cpqphp_hotplug_slot_ops; hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
......
...@@ -673,7 +673,20 @@ static void free_slots(void) ...@@ -673,7 +673,20 @@ static void free_slots(void)
list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head, list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head,
ibm_slot_list) { ibm_slot_list) {
pci_hp_deregister(slot_cur->hotplug_slot); pci_hp_del(slot_cur->hotplug_slot);
slot_cur->ctrl = NULL;
slot_cur->bus_on = NULL;
/*
* We don't want to actually remove the resources,
* since ibmphp_free_resources() will do just that.
*/
ibmphp_unconfigure_card(&slot_cur, -1);
pci_hp_destroy(slot_cur->hotplug_slot);
kfree(slot_cur->hotplug_slot->info);
kfree(slot_cur->hotplug_slot);
kfree(slot_cur);
} }
debug("%s -- exit\n", __func__); debug("%s -- exit\n", __func__);
} }
......
...@@ -699,25 +699,6 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot) ...@@ -699,25 +699,6 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot)
return rc; return rc;
} }
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot;
if (!hotplug_slot || !hotplug_slot->private)
return;
slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
slot->ctrl = NULL;
slot->bus_on = NULL;
/* we don't want to actually remove the resources, since free_resources will do just that */
ibmphp_unconfigure_card(&slot, -1);
kfree(slot);
}
static struct pci_driver ibmphp_driver; static struct pci_driver ibmphp_driver;
/* /*
...@@ -941,7 +922,6 @@ static int __init ebda_rsrc_controller(void) ...@@ -941,7 +922,6 @@ static int __init ebda_rsrc_controller(void)
tmp_slot->hotplug_slot = hp_slot_ptr; tmp_slot->hotplug_slot = hp_slot_ptr;
hp_slot_ptr->private = tmp_slot; hp_slot_ptr->private = tmp_slot;
hp_slot_ptr->release = release_slot;
rc = fillslotinfo(hp_slot_ptr); rc = fillslotinfo(hp_slot_ptr);
if (rc) if (rc)
......
...@@ -396,8 +396,9 @@ static struct hotplug_slot *get_slot_from_name(const char *name) ...@@ -396,8 +396,9 @@ static struct hotplug_slot *get_slot_from_name(const char *name)
* @owner: caller module owner * @owner: caller module owner
* @mod_name: caller module name * @mod_name: caller module name
* *
* Registers a hotplug slot with the pci hotplug subsystem, which will allow * Prepares a hotplug slot for in-kernel use and immediately publishes it to
* userspace interaction to the slot. * user space in one go. Drivers may alternatively carry out the two steps
* separately by invoking pci_hp_initialize() and pci_hp_add().
* *
* Returns 0 if successful, anything else for an error. * Returns 0 if successful, anything else for an error.
*/ */
...@@ -406,45 +407,91 @@ int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, ...@@ -406,45 +407,91 @@ int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
struct module *owner, const char *mod_name) struct module *owner, const char *mod_name)
{ {
int result; int result;
result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
if (result)
return result;
result = pci_hp_add(slot);
if (result)
pci_hp_destroy(slot);
return result;
}
EXPORT_SYMBOL_GPL(__pci_hp_register);
/**
* __pci_hp_initialize - prepare hotplug slot for in-kernel use
* @slot: pointer to the &struct hotplug_slot to initialize
* @bus: bus this slot is on
* @devnr: slot number
* @name: name registered with kobject core
* @owner: caller module owner
* @mod_name: caller module name
*
* Allocate and fill in a PCI slot for use by a hotplug driver. Once this has
* been called, the driver may invoke hotplug_slot_name() to get the slot's
* unique name. The driver must be prepared to handle a ->reset_slot callback
* from this point on.
*
* Returns 0 on success or a negative int on error.
*/
int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
int devnr, const char *name, struct module *owner,
const char *mod_name)
{
struct pci_slot *pci_slot; struct pci_slot *pci_slot;
if (slot == NULL) if (slot == NULL)
return -ENODEV; return -ENODEV;
if ((slot->info == NULL) || (slot->ops == NULL)) if ((slot->info == NULL) || (slot->ops == NULL))
return -EINVAL; return -EINVAL;
if (slot->release == NULL) {
dbg("Why are you trying to register a hotplug slot without a proper release function?\n");
return -EINVAL;
}
slot->ops->owner = owner; slot->ops->owner = owner;
slot->ops->mod_name = mod_name; slot->ops->mod_name = mod_name;
mutex_lock(&pci_hp_mutex);
/* /*
* No problems if we call this interface from both ACPI_PCI_SLOT * No problems if we call this interface from both ACPI_PCI_SLOT
* driver and call it here again. If we've already created the * driver and call it here again. If we've already created the
* pci_slot, the interface will simply bump the refcount. * pci_slot, the interface will simply bump the refcount.
*/ */
pci_slot = pci_create_slot(bus, devnr, name, slot); pci_slot = pci_create_slot(bus, devnr, name, slot);
if (IS_ERR(pci_slot)) { if (IS_ERR(pci_slot))
result = PTR_ERR(pci_slot); return PTR_ERR(pci_slot);
goto out;
}
slot->pci_slot = pci_slot; slot->pci_slot = pci_slot;
pci_slot->hotplug = slot; pci_slot->hotplug = slot;
return 0;
}
EXPORT_SYMBOL_GPL(__pci_hp_initialize);
list_add(&slot->slot_list, &pci_hotplug_slot_list); /**
* pci_hp_add - publish hotplug slot to user space
* @slot: pointer to the &struct hotplug_slot to publish
*
* Make a hotplug slot's sysfs interface available and inform user space of its
* addition by sending a uevent. The hotplug driver must be prepared to handle
* all &struct hotplug_slot_ops callbacks from this point on.
*
* Returns 0 on success or a negative int on error.
*/
int pci_hp_add(struct hotplug_slot *slot)
{
struct pci_slot *pci_slot = slot->pci_slot;
int result;
result = fs_add_slot(pci_slot); result = fs_add_slot(pci_slot);
if (result)
return result;
kobject_uevent(&pci_slot->kobj, KOBJ_ADD); kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
dbg("Added slot %s to the list\n", name); mutex_lock(&pci_hp_mutex);
out: list_add(&slot->slot_list, &pci_hotplug_slot_list);
mutex_unlock(&pci_hp_mutex); mutex_unlock(&pci_hp_mutex);
return result; dbg("Added slot %s to the list\n", hotplug_slot_name(slot));
return 0;
} }
EXPORT_SYMBOL_GPL(__pci_hp_register); EXPORT_SYMBOL_GPL(pci_hp_add);
/** /**
* pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
...@@ -455,35 +502,62 @@ EXPORT_SYMBOL_GPL(__pci_hp_register); ...@@ -455,35 +502,62 @@ EXPORT_SYMBOL_GPL(__pci_hp_register);
* *
* Returns 0 if successful, anything else for an error. * Returns 0 if successful, anything else for an error.
*/ */
int pci_hp_deregister(struct hotplug_slot *slot) void pci_hp_deregister(struct hotplug_slot *slot)
{
pci_hp_del(slot);
pci_hp_destroy(slot);
}
EXPORT_SYMBOL_GPL(pci_hp_deregister);
/**
* pci_hp_del - unpublish hotplug slot from user space
* @slot: pointer to the &struct hotplug_slot to unpublish
*
* Remove a hotplug slot's sysfs interface.
*
* Returns 0 on success or a negative int on error.
*/
void pci_hp_del(struct hotplug_slot *slot)
{ {
struct hotplug_slot *temp; struct hotplug_slot *temp;
struct pci_slot *pci_slot;
if (!slot) if (WARN_ON(!slot))
return -ENODEV; return;
mutex_lock(&pci_hp_mutex); mutex_lock(&pci_hp_mutex);
temp = get_slot_from_name(hotplug_slot_name(slot)); temp = get_slot_from_name(hotplug_slot_name(slot));
if (temp != slot) { if (WARN_ON(temp != slot)) {
mutex_unlock(&pci_hp_mutex); mutex_unlock(&pci_hp_mutex);
return -ENODEV; return;
} }
list_del(&slot->slot_list); list_del(&slot->slot_list);
mutex_unlock(&pci_hp_mutex);
pci_slot = slot->pci_slot;
fs_remove_slot(pci_slot);
dbg("Removed slot %s from the list\n", hotplug_slot_name(slot)); dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
fs_remove_slot(slot->pci_slot);
}
EXPORT_SYMBOL_GPL(pci_hp_del);
slot->release(slot); /**
* pci_hp_destroy - remove hotplug slot from in-kernel use
* @slot: pointer to the &struct hotplug_slot to destroy
*
* Destroy a PCI slot used by a hotplug driver. Once this has been called,
* the driver may no longer invoke hotplug_slot_name() to get the slot's
* unique name. The driver no longer needs to handle a ->reset_slot callback
* from this point on.
*
* Returns 0 on success or a negative int on error.
*/
void pci_hp_destroy(struct hotplug_slot *slot)
{
struct pci_slot *pci_slot = slot->pci_slot;
slot->pci_slot = NULL;
pci_slot->hotplug = NULL; pci_slot->hotplug = NULL;
pci_destroy_slot(pci_slot); pci_destroy_slot(pci_slot);
mutex_unlock(&pci_hp_mutex);
return 0;
} }
EXPORT_SYMBOL_GPL(pci_hp_deregister); EXPORT_SYMBOL_GPL(pci_hp_destroy);
/** /**
* pci_hp_change_slot_info - changes the slot's information structure in the core * pci_hp_change_slot_info - changes the slot's information structure in the core
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/sched/signal.h> /* signal_pending() */ #include <linux/sched/signal.h> /* signal_pending() */
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "../pcie/portdrv.h" #include "../pcie/portdrv.h"
...@@ -57,49 +58,111 @@ do { \ ...@@ -57,49 +58,111 @@ do { \
dev_warn(&ctrl->pcie->device, format, ## arg) dev_warn(&ctrl->pcie->device, format, ## arg)
#define SLOT_NAME_SIZE 10 #define SLOT_NAME_SIZE 10
/**
* struct slot - PCIe hotplug slot
* @state: current state machine position
* @ctrl: pointer to the slot's controller structure
* @hotplug_slot: pointer to the structure registered with the PCI hotplug core
* @work: work item to turn the slot on or off after 5 seconds in response to
* an Attention Button press
* @lock: protects reads and writes of @state;
* protects scheduling, execution and cancellation of @work
*/
struct slot { struct slot {
u8 state; u8 state;
struct controller *ctrl; struct controller *ctrl;
struct hotplug_slot *hotplug_slot; struct hotplug_slot *hotplug_slot;
struct delayed_work work; /* work for button event */ struct delayed_work work;
struct mutex lock; struct mutex lock;
struct mutex hotplug_lock;
struct workqueue_struct *wq;
};
struct event_info {
u32 event_type;
struct slot *p_slot;
struct work_struct work;
}; };
/**
* struct controller - PCIe hotplug controller
* @ctrl_lock: serializes writes to the Slot Control register
* @pcie: pointer to the controller's PCIe port service device
* @reset_lock: prevents access to the Data Link Layer Link Active bit in the
* Link Status register and to the Presence Detect State bit in the Slot
* Status register during a slot reset which may cause them to flap
* @slot: pointer to the controller's slot structure
* @queue: wait queue to wake up on reception of a Command Completed event,
* used for synchronous writes to the Slot Control register
* @slot_cap: cached copy of the Slot Capabilities register
* @slot_ctrl: cached copy of the Slot Control register
* @poll_thread: thread to poll for slot events if no IRQ is available,
* enabled with pciehp_poll_mode module parameter
* @cmd_started: jiffies when the Slot Control register was last written;
* the next write is allowed 1 second later, absent a Command Completed
* interrupt (PCIe r4.0, sec 6.7.3.2)
* @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler
* on reception of a Command Completed event
* @link_active_reporting: cached copy of Data Link Layer Link Active Reporting
* Capable bit in Link Capabilities register; if this bit is zero, the
* Data Link Layer Link Active bit in the Link Status register will never
* be set and the driver is thus confined to wait 1 second before assuming
* the link to a hotplugged device is up and accessing it
* @notification_enabled: whether the IRQ was requested successfully
* @power_fault_detected: whether a power fault was detected by the hardware
* that has not yet been cleared by the user
* @pending_events: used by the IRQ handler to save events retrieved from the
* Slot Status register for later consumption by the IRQ thread
* @request_result: result of last user request submitted to the IRQ thread
* @requester: wait queue to wake up on completion of user request,
* used for synchronous slot enable/disable request via sysfs
*/
struct controller { struct controller {
struct mutex ctrl_lock; /* controller lock */ struct mutex ctrl_lock;
struct pcie_device *pcie; /* PCI Express port service */ struct pcie_device *pcie;
struct rw_semaphore reset_lock;
struct slot *slot; struct slot *slot;
wait_queue_head_t queue; /* sleep & wake process */ wait_queue_head_t queue;
u32 slot_cap; u32 slot_cap;
u16 slot_ctrl; u16 slot_ctrl;
struct timer_list poll_timer; struct task_struct *poll_thread;
unsigned long cmd_started; /* jiffies */ unsigned long cmd_started; /* jiffies */
unsigned int cmd_busy:1; unsigned int cmd_busy:1;
unsigned int link_active_reporting:1; unsigned int link_active_reporting:1;
unsigned int notification_enabled:1; unsigned int notification_enabled:1;
unsigned int power_fault_detected; unsigned int power_fault_detected;
atomic_t pending_events;
int request_result;
wait_queue_head_t requester;
}; };
#define INT_PRESENCE_ON 1 /**
#define INT_PRESENCE_OFF 2 * DOC: Slot state
#define INT_POWER_FAULT 3 *
#define INT_BUTTON_PRESS 4 * @OFF_STATE: slot is powered off, no subordinate devices are enumerated
#define INT_LINK_UP 5 * @BLINKINGON_STATE: slot will be powered on after the 5 second delay,
#define INT_LINK_DOWN 6 * green led is blinking
* @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay,
#define STATIC_STATE 0 * green led is blinking
* @POWERON_STATE: slot is currently powering on
* @POWEROFF_STATE: slot is currently powering off
* @ON_STATE: slot is powered on, subordinate devices have been enumerated
*/
#define OFF_STATE 0
#define BLINKINGON_STATE 1 #define BLINKINGON_STATE 1
#define BLINKINGOFF_STATE 2 #define BLINKINGOFF_STATE 2
#define POWERON_STATE 3 #define POWERON_STATE 3
#define POWEROFF_STATE 4 #define POWEROFF_STATE 4
#define ON_STATE 5
/**
* DOC: Flags to request an action from the IRQ thread
*
* These are stored together with events read from the Slot Status register,
* hence must be greater than its 16-bit width.
*
* %DISABLE_SLOT: Disable the slot in response to a user request via sysfs or
* an Attention Button press after the 5 second delay
* %RERUN_ISR: Used by the IRQ handler to inform the IRQ thread that the
* hotplug port was inaccessible when the interrupt occurred, requiring
* that the IRQ handler is rerun by the IRQ thread after it has made the
* hotplug port accessible by runtime resuming its parents to D0
*/
#define DISABLE_SLOT (1 << 16)
#define RERUN_ISR (1 << 17)
#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP) #define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP) #define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
...@@ -113,15 +176,17 @@ struct controller { ...@@ -113,15 +176,17 @@ struct controller {
int pciehp_sysfs_enable_slot(struct slot *slot); int pciehp_sysfs_enable_slot(struct slot *slot);
int pciehp_sysfs_disable_slot(struct slot *slot); int pciehp_sysfs_disable_slot(struct slot *slot);
void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type); void pciehp_request(struct controller *ctrl, int action);
void pciehp_handle_button_press(struct slot *slot);
void pciehp_handle_disable_request(struct slot *slot);
void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events);
int pciehp_configure_device(struct slot *p_slot); int pciehp_configure_device(struct slot *p_slot);
int pciehp_unconfigure_device(struct slot *p_slot); void pciehp_unconfigure_device(struct slot *p_slot);
void pciehp_queue_pushbutton_work(struct work_struct *work); void pciehp_queue_pushbutton_work(struct work_struct *work);
struct controller *pcie_init(struct pcie_device *dev); struct controller *pcie_init(struct pcie_device *dev);
int pcie_init_notification(struct controller *ctrl); int pcie_init_notification(struct controller *ctrl);
int pciehp_enable_slot(struct slot *p_slot); void pcie_shutdown_notification(struct controller *ctrl);
int pciehp_disable_slot(struct slot *p_slot); void pcie_clear_hotplug_events(struct controller *ctrl);
void pcie_reenable_notification(struct controller *ctrl);
int pciehp_power_on_slot(struct slot *slot); int pciehp_power_on_slot(struct slot *slot);
void pciehp_power_off_slot(struct slot *slot); void pciehp_power_off_slot(struct slot *slot);
void pciehp_get_power_status(struct slot *slot, u8 *status); void pciehp_get_power_status(struct slot *slot, u8 *status);
......
...@@ -26,11 +26,12 @@ ...@@ -26,11 +26,12 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/time.h> #include <linux/time.h>
#include "../pci.h"
/* Global variables */ /* Global variables */
bool pciehp_debug; bool pciehp_debug;
bool pciehp_poll_mode; bool pciehp_poll_mode;
int pciehp_poll_time; int pciehp_poll_time;
static bool pciehp_force;
/* /*
* not really modular, but the easiest way to keep compat with existing * not really modular, but the easiest way to keep compat with existing
...@@ -39,11 +40,9 @@ static bool pciehp_force; ...@@ -39,11 +40,9 @@ static bool pciehp_force;
module_param(pciehp_debug, bool, 0644); module_param(pciehp_debug, bool, 0644);
module_param(pciehp_poll_mode, bool, 0644); module_param(pciehp_poll_mode, bool, 0644);
module_param(pciehp_poll_time, int, 0644); module_param(pciehp_poll_time, int, 0644);
module_param(pciehp_force, bool, 0644);
MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
#define PCIE_MODULE_NAME "pciehp" #define PCIE_MODULE_NAME "pciehp"
...@@ -56,17 +55,6 @@ static int get_latch_status(struct hotplug_slot *slot, u8 *value); ...@@ -56,17 +55,6 @@ static int get_latch_status(struct hotplug_slot *slot, u8 *value);
static int get_adapter_status(struct hotplug_slot *slot, u8 *value); static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
static int reset_slot(struct hotplug_slot *slot, int probe); static int reset_slot(struct hotplug_slot *slot, int probe);
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
kfree(hotplug_slot->ops);
kfree(hotplug_slot->info);
kfree(hotplug_slot);
}
static int init_slot(struct controller *ctrl) static int init_slot(struct controller *ctrl)
{ {
struct slot *slot = ctrl->slot; struct slot *slot = ctrl->slot;
...@@ -107,15 +95,14 @@ static int init_slot(struct controller *ctrl) ...@@ -107,15 +95,14 @@ static int init_slot(struct controller *ctrl)
/* register this slot with the hotplug pci core */ /* register this slot with the hotplug pci core */
hotplug->info = info; hotplug->info = info;
hotplug->private = slot; hotplug->private = slot;
hotplug->release = &release_slot;
hotplug->ops = ops; hotplug->ops = ops;
slot->hotplug_slot = hotplug; slot->hotplug_slot = hotplug;
snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
retval = pci_hp_register(hotplug, retval = pci_hp_initialize(hotplug,
ctrl->pcie->port->subordinate, 0, name); ctrl->pcie->port->subordinate, 0, name);
if (retval) if (retval)
ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval); ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
out: out:
if (retval) { if (retval) {
kfree(ops); kfree(ops);
...@@ -127,7 +114,12 @@ static int init_slot(struct controller *ctrl) ...@@ -127,7 +114,12 @@ static int init_slot(struct controller *ctrl)
static void cleanup_slot(struct controller *ctrl) static void cleanup_slot(struct controller *ctrl)
{ {
pci_hp_deregister(ctrl->slot->hotplug_slot); struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot;
pci_hp_destroy(hotplug_slot);
kfree(hotplug_slot->ops);
kfree(hotplug_slot->info);
kfree(hotplug_slot);
} }
/* /*
...@@ -136,8 +128,11 @@ static void cleanup_slot(struct controller *ctrl) ...@@ -136,8 +128,11 @@ static void cleanup_slot(struct controller *ctrl)
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{ {
struct slot *slot = hotplug_slot->private; struct slot *slot = hotplug_slot->private;
struct pci_dev *pdev = slot->ctrl->pcie->port;
pci_config_pm_runtime_get(pdev);
pciehp_set_attention_status(slot, status); pciehp_set_attention_status(slot, status);
pci_config_pm_runtime_put(pdev);
return 0; return 0;
} }
...@@ -160,8 +155,11 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) ...@@ -160,8 +155,11 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{ {
struct slot *slot = hotplug_slot->private; struct slot *slot = hotplug_slot->private;
struct pci_dev *pdev = slot->ctrl->pcie->port;
pci_config_pm_runtime_get(pdev);
pciehp_get_power_status(slot, value); pciehp_get_power_status(slot, value);
pci_config_pm_runtime_put(pdev);
return 0; return 0;
} }
...@@ -176,16 +174,22 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) ...@@ -176,16 +174,22 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{ {
struct slot *slot = hotplug_slot->private; struct slot *slot = hotplug_slot->private;
struct pci_dev *pdev = slot->ctrl->pcie->port;
pci_config_pm_runtime_get(pdev);
pciehp_get_latch_status(slot, value); pciehp_get_latch_status(slot, value);
pci_config_pm_runtime_put(pdev);
return 0; return 0;
} }
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{ {
struct slot *slot = hotplug_slot->private; struct slot *slot = hotplug_slot->private;
struct pci_dev *pdev = slot->ctrl->pcie->port;
pci_config_pm_runtime_get(pdev);
pciehp_get_adapter_status(slot, value); pciehp_get_adapter_status(slot, value);
pci_config_pm_runtime_put(pdev);
return 0; return 0;
} }
...@@ -196,12 +200,40 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) ...@@ -196,12 +200,40 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
return pciehp_reset_slot(slot, probe); return pciehp_reset_slot(slot, probe);
} }
/**
* pciehp_check_presence() - synthesize event if presence has changed
*
* On probe and resume, an explicit presence check is necessary to bring up an
* occupied slot or bring down an unoccupied slot. This can't be triggered by
* events in the Slot Status register, they may be stale and are therefore
* cleared. Secondly, sending an interrupt for "events that occur while
* interrupt generation is disabled [when] interrupt generation is subsequently
* enabled" is optional per PCIe r4.0, sec 6.7.3.4.
*/
static void pciehp_check_presence(struct controller *ctrl)
{
struct slot *slot = ctrl->slot;
u8 occupied;
down_read(&ctrl->reset_lock);
mutex_lock(&slot->lock);
pciehp_get_adapter_status(slot, &occupied);
if ((occupied && (slot->state == OFF_STATE ||
slot->state == BLINKINGON_STATE)) ||
(!occupied && (slot->state == ON_STATE ||
slot->state == BLINKINGOFF_STATE)))
pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
mutex_unlock(&slot->lock);
up_read(&ctrl->reset_lock);
}
static int pciehp_probe(struct pcie_device *dev) static int pciehp_probe(struct pcie_device *dev)
{ {
int rc; int rc;
struct controller *ctrl; struct controller *ctrl;
struct slot *slot; struct slot *slot;
u8 occupied, poweron;
/* If this is not a "hotplug" service, we have no business here. */ /* If this is not a "hotplug" service, we have no business here. */
if (dev->service != PCIE_PORT_SERVICE_HP) if (dev->service != PCIE_PORT_SERVICE_HP)
...@@ -238,21 +270,20 @@ static int pciehp_probe(struct pcie_device *dev) ...@@ -238,21 +270,20 @@ static int pciehp_probe(struct pcie_device *dev)
goto err_out_free_ctrl_slot; goto err_out_free_ctrl_slot;
} }
/* Check if slot is occupied */ /* Publish to user space */
slot = ctrl->slot; slot = ctrl->slot;
pciehp_get_adapter_status(slot, &occupied); rc = pci_hp_add(slot->hotplug_slot);
pciehp_get_power_status(slot, &poweron); if (rc) {
if (occupied && pciehp_force) { ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
mutex_lock(&slot->hotplug_lock); goto err_out_shutdown_notification;
pciehp_enable_slot(slot);
mutex_unlock(&slot->hotplug_lock);
} }
/* If empty slot's power status is on, turn power off */
if (!occupied && poweron && POWER_CTRL(ctrl)) pciehp_check_presence(ctrl);
pciehp_power_off_slot(slot);
return 0; return 0;
err_out_shutdown_notification:
pcie_shutdown_notification(ctrl);
err_out_free_ctrl_slot: err_out_free_ctrl_slot:
cleanup_slot(ctrl); cleanup_slot(ctrl);
err_out_release_ctlr: err_out_release_ctlr:
...@@ -264,6 +295,8 @@ static void pciehp_remove(struct pcie_device *dev) ...@@ -264,6 +295,8 @@ static void pciehp_remove(struct pcie_device *dev)
{ {
struct controller *ctrl = get_service_data(dev); struct controller *ctrl = get_service_data(dev);
pci_hp_del(ctrl->slot->hotplug_slot);
pcie_shutdown_notification(ctrl);
cleanup_slot(ctrl); cleanup_slot(ctrl);
pciehp_release_ctrl(ctrl); pciehp_release_ctrl(ctrl);
} }
...@@ -274,27 +307,28 @@ static int pciehp_suspend(struct pcie_device *dev) ...@@ -274,27 +307,28 @@ static int pciehp_suspend(struct pcie_device *dev)
return 0; return 0;
} }
static int pciehp_resume(struct pcie_device *dev) static int pciehp_resume_noirq(struct pcie_device *dev)
{ {
struct controller *ctrl; struct controller *ctrl = get_service_data(dev);
struct slot *slot; struct slot *slot = ctrl->slot;
u8 status;
ctrl = get_service_data(dev); /* pci_restore_state() just wrote to the Slot Control register */
ctrl->cmd_started = jiffies;
ctrl->cmd_busy = true;
/* reinitialize the chipset's event detection logic */ /* clear spurious events from rediscovery of inserted card */
pcie_reenable_notification(ctrl); if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE)
pcie_clear_hotplug_events(ctrl);
slot = ctrl->slot; return 0;
}
static int pciehp_resume(struct pcie_device *dev)
{
struct controller *ctrl = get_service_data(dev);
pciehp_check_presence(ctrl);
/* Check if slot is occupied */
pciehp_get_adapter_status(slot, &status);
mutex_lock(&slot->hotplug_lock);
if (status)
pciehp_enable_slot(slot);
else
pciehp_disable_slot(slot);
mutex_unlock(&slot->hotplug_lock);
return 0; return 0;
} }
#endif /* PM */ #endif /* PM */
...@@ -309,6 +343,7 @@ static struct pcie_port_service_driver hpdriver_portdrv = { ...@@ -309,6 +343,7 @@ static struct pcie_port_service_driver hpdriver_portdrv = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = pciehp_suspend, .suspend = pciehp_suspend,
.resume_noirq = pciehp_resume_noirq,
.resume = pciehp_resume, .resume = pciehp_resume,
#endif /* PM */ #endif /* PM */
}; };
......
This diff is collapsed.
This diff is collapsed.
...@@ -62,9 +62,8 @@ int pciehp_configure_device(struct slot *p_slot) ...@@ -62,9 +62,8 @@ int pciehp_configure_device(struct slot *p_slot)
return ret; return ret;
} }
int pciehp_unconfigure_device(struct slot *p_slot) void pciehp_unconfigure_device(struct slot *p_slot)
{ {
int rc = 0;
u8 presence = 0; u8 presence = 0;
struct pci_dev *dev, *temp; struct pci_dev *dev, *temp;
struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
...@@ -107,5 +106,4 @@ int pciehp_unconfigure_device(struct slot *p_slot) ...@@ -107,5 +106,4 @@ int pciehp_unconfigure_device(struct slot *p_slot)
} }
pci_unlock_rescan_remove(); pci_unlock_rescan_remove();
return rc;
} }
// SPDX-License-Identifier: GPL-2.0+
/*
* PCI Hot Plug Controller Skeleton Driver - 0.3
*
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2003 IBM Corp.
*
* All rights reserved.
*
* This driver is to be used as a skeleton driver to show how to interface
* with the pci hotplug core easily.
*
* Send feedback to <greg@kroah.com>
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/init.h>
#define SLOT_NAME_SIZE 10
struct slot {
u8 number;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
char name[SLOT_NAME_SIZE];
};
static LIST_HEAD(slot_list);
#define MY_NAME "pcihp_skeleton"
#define dbg(format, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: " format "\n", \
MY_NAME, ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg)
/* local variables */
static bool debug;
static int num_slots;
#define DRIVER_VERSION "0.3"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
static int enable_slot(struct hotplug_slot *slot);
static int disable_slot(struct hotplug_slot *slot);
static int set_attention_status(struct hotplug_slot *slot, u8 value);
static int hardware_test(struct hotplug_slot *slot, u32 value);
static int get_power_status(struct hotplug_slot *slot, u8 *value);
static int get_attention_status(struct hotplug_slot *slot, u8 *value);
static int get_latch_status(struct hotplug_slot *slot, u8 *value);
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops skel_hotplug_slot_ops = {
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
.hardware_test = hardware_test,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
};
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in code here to enable the specified slot
*/
return retval;
}
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in code here to disable the specified slot
*/
return retval;
}
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
switch (status) {
case 0:
/*
* Fill in code here to turn light off
*/
break;
case 1:
default:
/*
* Fill in code here to turn light on
*/
break;
}
return retval;
}
static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
switch (value) {
case 0:
/* Specify a test here */
break;
case 1:
/* Specify another test here */
break;
}
return retval;
}
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current power status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current attention status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current latch status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current adapter status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
}
static void make_slot_name(struct slot *slot)
{
/*
* Stupid way to make a filename out of the slot name.
* replace this if your hardware provides a better way to name slots.
*/
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number);
}
/**
* init_slots - initialize 'struct slot' structures for each slot
*
*/
static int __init init_slots(void)
{
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
int retval;
int i;
/*
* Create a structure for each slot, and register that slot
* with the pci_hotplug subsystem.
*/
for (i = 0; i < num_slots; ++i) {
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot) {
retval = -ENOMEM;
goto error;
}
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
if (!hotplug_slot) {
retval = -ENOMEM;
goto error_slot;
}
slot->hotplug_slot = hotplug_slot;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
retval = -ENOMEM;
goto error_hpslot;
}
hotplug_slot->info = info;
slot->number = i;
hotplug_slot->name = slot->name;
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
make_slot_name(slot);
hotplug_slot->ops = &skel_hotplug_slot_ops;
/*
* Initialize the slot info structure with some known
* good values.
*/
get_power_status(hotplug_slot, &info->power_status);
get_attention_status(hotplug_slot, &info->attention_status);
get_latch_status(hotplug_slot, &info->latch_status);
get_adapter_status(hotplug_slot, &info->adapter_status);
dbg("registering slot %d\n", i);
retval = pci_hp_register(slot->hotplug_slot);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_info;
}
/* add slot to our internal list */
list_add(&slot->slot_list, &slot_list);
}
return 0;
error_info:
kfree(info);
error_hpslot:
kfree(hotplug_slot);
error_slot:
kfree(slot);
error:
return retval;
}
static void __exit cleanup_slots(void)
{
struct slot *slot, *next;
/*
* Unregister all of our slots with the pci_hotplug subsystem.
* Memory will be freed in release_slot() callback after slot's
* lifespan is finished.
*/
list_for_each_entry_safe(slot, next, &slot_list, slot_list) {
list_del(&slot->slot_list);
pci_hp_deregister(slot->hotplug_slot);
}
}
static int __init pcihp_skel_init(void)
{
int retval;
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
/*
* Do specific initialization stuff for your driver here
* like initializing your controller hardware (if any) and
* determining the number of slots you have in the system
* right now.
*/
num_slots = 5;
return init_slots();
}
static void __exit pcihp_skel_exit(void)
{
/*
* Clean everything up.
*/
cleanup_slots();
}
module_init(pcihp_skel_init);
module_exit(pcihp_skel_exit);
...@@ -538,9 +538,8 @@ static struct hotplug_slot_ops php_slot_ops = { ...@@ -538,9 +538,8 @@ static struct hotplug_slot_ops php_slot_ops = {
.disable_slot = pnv_php_disable_slot, .disable_slot = pnv_php_disable_slot,
}; };
static void pnv_php_release(struct hotplug_slot *slot) static void pnv_php_release(struct pnv_php_slot *php_slot)
{ {
struct pnv_php_slot *php_slot = slot->private;
unsigned long flags; unsigned long flags;
/* Remove from global or child list */ /* Remove from global or child list */
...@@ -596,7 +595,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) ...@@ -596,7 +595,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
php_slot->power_state_check = false; php_slot->power_state_check = false;
php_slot->slot.ops = &php_slot_ops; php_slot->slot.ops = &php_slot_ops;
php_slot->slot.info = &php_slot->slot_info; php_slot->slot.info = &php_slot->slot_info;
php_slot->slot.release = pnv_php_release;
php_slot->slot.private = php_slot; php_slot->slot.private = php_slot;
INIT_LIST_HEAD(&php_slot->children); INIT_LIST_HEAD(&php_slot->children);
...@@ -924,6 +922,7 @@ static void pnv_php_unregister_one(struct device_node *dn) ...@@ -924,6 +922,7 @@ static void pnv_php_unregister_one(struct device_node *dn)
php_slot->state = PNV_PHP_STATE_OFFLINE; php_slot->state = PNV_PHP_STATE_OFFLINE;
pci_hp_deregister(&php_slot->slot); pci_hp_deregister(&php_slot->slot);
pnv_php_release(php_slot);
pnv_php_put_slot(php_slot); pnv_php_put_slot(php_slot);
} }
......
...@@ -404,13 +404,13 @@ static void __exit cleanup_slots(void) ...@@ -404,13 +404,13 @@ static void __exit cleanup_slots(void)
/* /*
* Unregister all of our slots with the pci_hotplug subsystem, * Unregister all of our slots with the pci_hotplug subsystem,
* and free up all memory that we had allocated. * and free up all memory that we had allocated.
* memory will be freed in release_slot callback.
*/ */
list_for_each_entry_safe(slot, next, &rpaphp_slot_head, list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
rpaphp_slot_list) { rpaphp_slot_list) {
list_del(&slot->rpaphp_slot_list); list_del(&slot->rpaphp_slot_list);
pci_hp_deregister(slot->hotplug_slot); pci_hp_deregister(slot->hotplug_slot);
dealloc_slot_struct(slot);
} }
return; return;
} }
......
...@@ -19,12 +19,6 @@ ...@@ -19,12 +19,6 @@
#include "rpaphp.h" #include "rpaphp.h"
/* free up the memory used by a slot */ /* free up the memory used by a slot */
static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = (struct slot *) hotplug_slot->private;
dealloc_slot_struct(slot);
}
void dealloc_slot_struct(struct slot *slot) void dealloc_slot_struct(struct slot *slot)
{ {
kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot->info);
...@@ -56,7 +50,6 @@ struct slot *alloc_slot_struct(struct device_node *dn, ...@@ -56,7 +50,6 @@ struct slot *alloc_slot_struct(struct device_node *dn,
slot->power_domain = power_domain; slot->power_domain = power_domain;
slot->hotplug_slot->private = slot; slot->hotplug_slot->private = slot;
slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops; slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
slot->hotplug_slot->release = &rpaphp_release_slot;
return (slot); return (slot);
...@@ -90,10 +83,8 @@ int rpaphp_deregister_slot(struct slot *slot) ...@@ -90,10 +83,8 @@ int rpaphp_deregister_slot(struct slot *slot)
__func__, slot->name); __func__, slot->name);
list_del(&slot->rpaphp_slot_list); list_del(&slot->rpaphp_slot_list);
pci_hp_deregister(php_slot);
retval = pci_hp_deregister(php_slot); dealloc_slot_struct(slot);
if (retval)
err("Problem unregistering a slot %s\n", slot->name);
dbg("%s - Exit: rc[%d]\n", __func__, retval); dbg("%s - Exit: rc[%d]\n", __func__, retval);
return retval; return retval;
......
...@@ -130,15 +130,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) ...@@ -130,15 +130,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0; return 0;
} }
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
}
static struct hotplug_slot_ops s390_hotplug_slot_ops = { static struct hotplug_slot_ops s390_hotplug_slot_ops = {
.enable_slot = enable_slot, .enable_slot = enable_slot,
.disable_slot = disable_slot, .disable_slot = disable_slot,
...@@ -175,7 +166,6 @@ int zpci_init_slot(struct zpci_dev *zdev) ...@@ -175,7 +166,6 @@ int zpci_init_slot(struct zpci_dev *zdev)
hotplug_slot->info = info; hotplug_slot->info = info;
hotplug_slot->ops = &s390_hotplug_slot_ops; hotplug_slot->ops = &s390_hotplug_slot_ops;
hotplug_slot->release = &release_slot;
get_power_status(hotplug_slot, &info->power_status); get_power_status(hotplug_slot, &info->power_status);
get_adapter_status(hotplug_slot, &info->adapter_status); get_adapter_status(hotplug_slot, &info->adapter_status);
...@@ -209,5 +199,8 @@ void zpci_exit_slot(struct zpci_dev *zdev) ...@@ -209,5 +199,8 @@ void zpci_exit_slot(struct zpci_dev *zdev)
continue; continue;
list_del(&slot->slot_list); list_del(&slot->slot_list);
pci_hp_deregister(slot->hotplug_slot); pci_hp_deregister(slot->hotplug_slot);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
} }
} }
...@@ -628,7 +628,6 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) ...@@ -628,7 +628,6 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
goto alloc_err; goto alloc_err;
} }
bss_hotplug_slot->ops = &sn_hotplug_slot_ops; bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
bss_hotplug_slot->release = &sn_release_slot;
rc = pci_hp_register(bss_hotplug_slot, pci_bus, device, name); rc = pci_hp_register(bss_hotplug_slot, pci_bus, device, name);
if (rc) if (rc)
...@@ -656,8 +655,10 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) ...@@ -656,8 +655,10 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
sn_release_slot(bss_hotplug_slot); sn_release_slot(bss_hotplug_slot);
/* destroy anything else on the list */ /* destroy anything else on the list */
while ((bss_hotplug_slot = sn_hp_destroy())) while ((bss_hotplug_slot = sn_hp_destroy())) {
pci_hp_deregister(bss_hotplug_slot); pci_hp_deregister(bss_hotplug_slot);
sn_release_slot(bss_hotplug_slot);
}
return rc; return rc;
} }
...@@ -703,8 +704,10 @@ static void __exit sn_pci_hotplug_exit(void) ...@@ -703,8 +704,10 @@ static void __exit sn_pci_hotplug_exit(void)
{ {
struct hotplug_slot *bss_hotplug_slot; struct hotplug_slot *bss_hotplug_slot;
while ((bss_hotplug_slot = sn_hp_destroy())) while ((bss_hotplug_slot = sn_hp_destroy())) {
pci_hp_deregister(bss_hotplug_slot); pci_hp_deregister(bss_hotplug_slot);
sn_release_slot(bss_hotplug_slot);
}
if (!list_empty(&sn_hp_list)) if (!list_empty(&sn_hp_list))
printk(KERN_ERR "%s: internal list is not empty\n", __FILE__); printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
......
...@@ -61,22 +61,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { ...@@ -61,22 +61,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.get_adapter_status = get_adapter_status, .get_adapter_status = get_adapter_status,
}; };
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
__func__, slot_name(slot));
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
}
static int init_slots(struct controller *ctrl) static int init_slots(struct controller *ctrl)
{ {
struct slot *slot; struct slot *slot;
...@@ -125,7 +109,6 @@ static int init_slots(struct controller *ctrl) ...@@ -125,7 +109,6 @@ static int init_slots(struct controller *ctrl)
/* register this slot with the hotplug pci core */ /* register this slot with the hotplug pci core */
hotplug_slot->private = slot; hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
snprintf(name, SLOT_NAME_SIZE, "%d", slot->number); snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
hotplug_slot->ops = &shpchp_hotplug_slot_ops; hotplug_slot->ops = &shpchp_hotplug_slot_ops;
...@@ -171,6 +154,9 @@ void cleanup_slots(struct controller *ctrl) ...@@ -171,6 +154,9 @@ void cleanup_slots(struct controller *ctrl)
cancel_delayed_work(&slot->work); cancel_delayed_work(&slot->work);
destroy_workqueue(slot->wq); destroy_workqueue(slot->wq);
pci_hp_deregister(slot->hotplug_slot); pci_hp_deregister(slot->hotplug_slot);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
} }
} }
...@@ -270,11 +256,30 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) ...@@ -270,11 +256,30 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0; return 0;
} }
static bool shpc_capable(struct pci_dev *bridge)
{
/*
* It is assumed that AMD GOLAM chips support SHPC but they do not
* have SHPC capability.
*/
if (bridge->vendor == PCI_VENDOR_ID_AMD &&
bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
return true;
if (pci_find_capability(bridge, PCI_CAP_ID_SHPC))
return true;
return false;
}
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
int rc; int rc;
struct controller *ctrl; struct controller *ctrl;
if (!shpc_capable(pdev))
return -ENODEV;
if (acpi_get_hp_hw_control_from_firmware(pdev)) if (acpi_get_hp_hw_control_from_firmware(pdev))
return -ENODEV; return -ENODEV;
...@@ -303,6 +308,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -303,6 +308,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc) if (rc)
goto err_cleanup_slots; goto err_cleanup_slots;
pdev->shpc_managed = 1;
return 0; return 0;
err_cleanup_slots: err_cleanup_slots:
...@@ -319,6 +325,7 @@ static void shpc_remove(struct pci_dev *dev) ...@@ -319,6 +325,7 @@ static void shpc_remove(struct pci_dev *dev)
{ {
struct controller *ctrl = pci_get_drvdata(dev); struct controller *ctrl = pci_get_drvdata(dev);
dev->shpc_managed = 0;
shpchp_remove_ctrl_files(ctrl); shpchp_remove_ctrl_files(ctrl);
ctrl->hpc_ops->release_ctlr(ctrl); ctrl->hpc_ops->release_ctlr(ctrl);
kfree(ctrl); kfree(ctrl);
......
...@@ -403,24 +403,7 @@ bool pciehp_is_native(struct pci_dev *bridge) ...@@ -403,24 +403,7 @@ bool pciehp_is_native(struct pci_dev *bridge)
*/ */
bool shpchp_is_native(struct pci_dev *bridge) bool shpchp_is_native(struct pci_dev *bridge)
{ {
const struct pci_host_bridge *host; return bridge->shpc_managed;
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_SHPC))
return false;
/*
* It is assumed that AMD GOLAM chips support SHPC but they do not
* have SHPC capability.
*/
if (bridge->vendor == PCI_VENDOR_ID_AMD &&
bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
return true;
if (!pci_find_capability(bridge, PCI_CAP_ID_SHPC))
return false;
host = pci_find_host_bridge(bridge->bus);
return host->native_shpc_hotplug;
} }
/** /**
......
...@@ -1448,7 +1448,9 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr, ...@@ -1448,7 +1448,9 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
if (val != 1) if (val != 1)
return -EINVAL; return -EINVAL;
pm_runtime_get_sync(dev);
result = pci_reset_function(pdev); result = pci_reset_function(pdev);
pm_runtime_put(dev);
if (result < 0) if (result < 0)
return result; return result;
......
...@@ -2289,7 +2289,7 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev) ...@@ -2289,7 +2289,7 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
* @bridge: Bridge to check * @bridge: Bridge to check
* *
* This function checks if it is possible to move the bridge to D3. * This function checks if it is possible to move the bridge to D3.
* Currently we only allow D3 for recent enough PCIe ports. * Currently we only allow D3 for recent enough PCIe ports and Thunderbolt.
*/ */
bool pci_bridge_d3_possible(struct pci_dev *bridge) bool pci_bridge_d3_possible(struct pci_dev *bridge)
{ {
...@@ -2304,18 +2304,27 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) ...@@ -2304,18 +2304,27 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
return false; return false;
/* /*
* Hotplug interrupts cannot be delivered if the link is down, * Hotplug ports handled by firmware in System Management Mode
* so parents of a hotplug port must stay awake. In addition,
* hotplug ports handled by firmware in System Management Mode
* may not be put into D3 by the OS (Thunderbolt on non-Macs). * may not be put into D3 by the OS (Thunderbolt on non-Macs).
* For simplicity, disallow in general for now.
*/ */
if (bridge->is_hotplug_bridge) if (bridge->is_hotplug_bridge && !pciehp_is_native(bridge))
return false; return false;
if (pci_bridge_d3_force) if (pci_bridge_d3_force)
return true; return true;
/* Even the oldest 2010 Thunderbolt controller supports D3. */
if (bridge->is_thunderbolt)
return true;
/*
* Hotplug ports handled natively by the OS were not validated
* by vendors for runtime D3 at least until 2018 because there
* was no OS support.
*/
if (bridge->is_hotplug_bridge)
return false;
/* /*
* It should be safe to put PCIe ports from 2015 or newer * It should be safe to put PCIe ports from 2015 or newer
* to D3. * to D3.
......
...@@ -50,6 +50,7 @@ struct pcie_port_service_driver { ...@@ -50,6 +50,7 @@ struct pcie_port_service_driver {
int (*probe) (struct pcie_device *dev); int (*probe) (struct pcie_device *dev);
void (*remove) (struct pcie_device *dev); void (*remove) (struct pcie_device *dev);
int (*suspend) (struct pcie_device *dev); int (*suspend) (struct pcie_device *dev);
int (*resume_noirq) (struct pcie_device *dev);
int (*resume) (struct pcie_device *dev); int (*resume) (struct pcie_device *dev);
/* Device driver may resume normal operations */ /* Device driver may resume normal operations */
...@@ -82,6 +83,7 @@ extern struct bus_type pcie_port_bus_type; ...@@ -82,6 +83,7 @@ extern struct bus_type pcie_port_bus_type;
int pcie_port_device_register(struct pci_dev *dev); int pcie_port_device_register(struct pci_dev *dev);
#ifdef CONFIG_PM #ifdef CONFIG_PM
int pcie_port_device_suspend(struct device *dev); int pcie_port_device_suspend(struct device *dev);
int pcie_port_device_resume_noirq(struct device *dev);
int pcie_port_device_resume(struct device *dev); int pcie_port_device_resume(struct device *dev);
#endif #endif
void pcie_port_device_remove(struct pci_dev *dev); void pcie_port_device_remove(struct pci_dev *dev);
......
...@@ -353,14 +353,19 @@ int pcie_port_device_register(struct pci_dev *dev) ...@@ -353,14 +353,19 @@ int pcie_port_device_register(struct pci_dev *dev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int suspend_iter(struct device *dev, void *data) typedef int (*pcie_pm_callback_t)(struct pcie_device *);
static int pm_iter(struct device *dev, void *data)
{ {
struct pcie_port_service_driver *service_driver; struct pcie_port_service_driver *service_driver;
size_t offset = *(size_t *)data;
pcie_pm_callback_t cb;
if ((dev->bus == &pcie_port_bus_type) && dev->driver) { if ((dev->bus == &pcie_port_bus_type) && dev->driver) {
service_driver = to_service_driver(dev->driver); service_driver = to_service_driver(dev->driver);
if (service_driver->suspend) cb = *(pcie_pm_callback_t *)((void *)service_driver + offset);
service_driver->suspend(to_pcie_device(dev)); if (cb)
return cb(to_pcie_device(dev));
} }
return 0; return 0;
} }
...@@ -371,20 +376,14 @@ static int suspend_iter(struct device *dev, void *data) ...@@ -371,20 +376,14 @@ static int suspend_iter(struct device *dev, void *data)
*/ */
int pcie_port_device_suspend(struct device *dev) int pcie_port_device_suspend(struct device *dev)
{ {
return device_for_each_child(dev, NULL, suspend_iter); size_t off = offsetof(struct pcie_port_service_driver, suspend);
return device_for_each_child(dev, &off, pm_iter);
} }
static int resume_iter(struct device *dev, void *data) int pcie_port_device_resume_noirq(struct device *dev)
{ {
struct pcie_port_service_driver *service_driver; size_t off = offsetof(struct pcie_port_service_driver, resume_noirq);
return device_for_each_child(dev, &off, pm_iter);
if ((dev->bus == &pcie_port_bus_type) &&
(dev->driver)) {
service_driver = to_service_driver(dev->driver);
if (service_driver->resume)
service_driver->resume(to_pcie_device(dev));
}
return 0;
} }
/** /**
...@@ -393,7 +392,8 @@ static int resume_iter(struct device *dev, void *data) ...@@ -393,7 +392,8 @@ static int resume_iter(struct device *dev, void *data)
*/ */
int pcie_port_device_resume(struct device *dev) int pcie_port_device_resume(struct device *dev)
{ {
return device_for_each_child(dev, NULL, resume_iter); size_t off = offsetof(struct pcie_port_service_driver, resume);
return device_for_each_child(dev, &off, pm_iter);
} }
#endif /* PM */ #endif /* PM */
......
...@@ -65,10 +65,12 @@ static int pcie_port_runtime_idle(struct device *dev) ...@@ -65,10 +65,12 @@ static int pcie_port_runtime_idle(struct device *dev)
static const struct dev_pm_ops pcie_portdrv_pm_ops = { static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.suspend = pcie_port_device_suspend, .suspend = pcie_port_device_suspend,
.resume_noirq = pcie_port_device_resume_noirq,
.resume = pcie_port_device_resume, .resume = pcie_port_device_resume,
.freeze = pcie_port_device_suspend, .freeze = pcie_port_device_suspend,
.thaw = pcie_port_device_resume, .thaw = pcie_port_device_resume,
.poweroff = pcie_port_device_suspend, .poweroff = pcie_port_device_suspend,
.restore_noirq = pcie_port_device_resume_noirq,
.restore = pcie_port_device_resume, .restore = pcie_port_device_resume,
.runtime_suspend = pcie_port_runtime_suspend, .runtime_suspend = pcie_port_runtime_suspend,
.runtime_resume = pcie_port_runtime_resume, .runtime_resume = pcie_port_runtime_resume,
......
...@@ -858,12 +858,6 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, ...@@ -858,12 +858,6 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
return 0; return 0;
} }
static void asus_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
{
kfree(hotplug_slot->info);
kfree(hotplug_slot);
}
static struct hotplug_slot_ops asus_hotplug_slot_ops = { static struct hotplug_slot_ops asus_hotplug_slot_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.get_adapter_status = asus_get_adapter_status, .get_adapter_status = asus_get_adapter_status,
...@@ -905,7 +899,6 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) ...@@ -905,7 +899,6 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus)
goto error_info; goto error_info;
asus->hotplug_slot->private = asus; asus->hotplug_slot->private = asus;
asus->hotplug_slot->release = &asus_cleanup_pci_hotplug;
asus->hotplug_slot->ops = &asus_hotplug_slot_ops; asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
asus_get_adapter_status(asus->hotplug_slot, asus_get_adapter_status(asus->hotplug_slot,
&asus->hotplug_slot->info->adapter_status); &asus->hotplug_slot->info->adapter_status);
...@@ -1051,8 +1044,11 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus) ...@@ -1051,8 +1044,11 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
* asus_unregister_rfkill_notifier() * asus_unregister_rfkill_notifier()
*/ */
asus_rfkill_hotplug(asus); asus_rfkill_hotplug(asus);
if (asus->hotplug_slot) if (asus->hotplug_slot) {
pci_hp_deregister(asus->hotplug_slot); pci_hp_deregister(asus->hotplug_slot);
kfree(asus->hotplug_slot->info);
kfree(asus->hotplug_slot);
}
if (asus->hotplug_workqueue) if (asus->hotplug_workqueue)
destroy_workqueue(asus->hotplug_workqueue); destroy_workqueue(asus->hotplug_workqueue);
......
...@@ -726,12 +726,6 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, ...@@ -726,12 +726,6 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
return 0; return 0;
} }
static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
{
kfree(hotplug_slot->info);
kfree(hotplug_slot);
}
static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.get_adapter_status = eeepc_get_adapter_status, .get_adapter_status = eeepc_get_adapter_status,
...@@ -758,7 +752,6 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) ...@@ -758,7 +752,6 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
goto error_info; goto error_info;
eeepc->hotplug_slot->private = eeepc; eeepc->hotplug_slot->private = eeepc;
eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
eeepc_get_adapter_status(eeepc->hotplug_slot, eeepc_get_adapter_status(eeepc->hotplug_slot,
&eeepc->hotplug_slot->info->adapter_status); &eeepc->hotplug_slot->info->adapter_status);
...@@ -837,8 +830,11 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) ...@@ -837,8 +830,11 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
eeepc->wlan_rfkill = NULL; eeepc->wlan_rfkill = NULL;
} }
if (eeepc->hotplug_slot) if (eeepc->hotplug_slot) {
pci_hp_deregister(eeepc->hotplug_slot); pci_hp_deregister(eeepc->hotplug_slot);
kfree(eeepc->hotplug_slot->info);
kfree(eeepc->hotplug_slot);
}
if (eeepc->bluetooth_rfkill) { if (eeepc->bluetooth_rfkill) {
rfkill_unregister(eeepc->bluetooth_rfkill); rfkill_unregister(eeepc->bluetooth_rfkill);
......
...@@ -388,6 +388,7 @@ struct pci_dev { ...@@ -388,6 +388,7 @@ struct pci_dev {
unsigned int is_virtfn:1; unsigned int is_virtfn:1;
unsigned int reset_fn:1; unsigned int reset_fn:1;
unsigned int is_hotplug_bridge:1; unsigned int is_hotplug_bridge:1;
unsigned int shpc_managed:1; /* SHPC owned by shpchp */
unsigned int is_thunderbolt:1; /* Thunderbolt controller */ unsigned int is_thunderbolt:1; /* Thunderbolt controller */
unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first_valid:1;
unsigned int __aer_firmware_first:1; unsigned int __aer_firmware_first:1;
......
...@@ -80,15 +80,12 @@ struct hotplug_slot_info { ...@@ -80,15 +80,12 @@ struct hotplug_slot_info {
* @ops: pointer to the &struct hotplug_slot_ops to be used for this slot * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
* @info: pointer to the &struct hotplug_slot_info for the initial values for * @info: pointer to the &struct hotplug_slot_info for the initial values for
* this slot. * this slot.
* @release: called during pci_hp_deregister to free memory allocated in a
* hotplug_slot structure.
* @private: used by the hotplug pci controller driver to store whatever it * @private: used by the hotplug pci controller driver to store whatever it
* needs. * needs.
*/ */
struct hotplug_slot { struct hotplug_slot {
struct hotplug_slot_ops *ops; struct hotplug_slot_ops *ops;
struct hotplug_slot_info *info; struct hotplug_slot_info *info;
void (*release) (struct hotplug_slot *slot);
void *private; void *private;
/* Variables below this are for use only by the hotplug pci core. */ /* Variables below this are for use only by the hotplug pci core. */
...@@ -104,13 +101,23 @@ static inline const char *hotplug_slot_name(const struct hotplug_slot *slot) ...@@ -104,13 +101,23 @@ static inline const char *hotplug_slot_name(const struct hotplug_slot *slot)
int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *pbus, int nr, int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *pbus, int nr,
const char *name, struct module *owner, const char *name, struct module *owner,
const char *mod_name); const char *mod_name);
int pci_hp_deregister(struct hotplug_slot *slot); int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus, int nr,
const char *name, struct module *owner,
const char *mod_name);
int pci_hp_add(struct hotplug_slot *slot);
void pci_hp_del(struct hotplug_slot *slot);
void pci_hp_destroy(struct hotplug_slot *slot);
void pci_hp_deregister(struct hotplug_slot *slot);
int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
struct hotplug_slot_info *info); struct hotplug_slot_info *info);
/* use a define to avoid include chaining to get THIS_MODULE & friends */ /* use a define to avoid include chaining to get THIS_MODULE & friends */
#define pci_hp_register(slot, pbus, devnr, name) \ #define pci_hp_register(slot, pbus, devnr, name) \
__pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME) __pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME)
#define pci_hp_initialize(slot, bus, nr, name) \
__pci_hp_initialize(slot, bus, nr, name, THIS_MODULE, KBUILD_MODNAME)
/* PCI Setting Record (Type 0) */ /* PCI Setting Record (Type 0) */
struct hpp_type0 { struct hpp_type0 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment