2018-01-26 20:22:04 +00:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* PCI Express Hot Plug Controller Driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
|
|
|
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
|
|
|
* Copyright (C) 2001 IBM Corp.
|
|
|
|
* Copyright (C) 2003-2004 Intel Corporation
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2005-08-16 22:16:10 +00:00
|
|
|
* Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
#ifndef _PCIEHP_H
|
|
|
|
#define _PCIEHP_H
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/pci.h>
|
2006-10-14 03:05:19 +00:00
|
|
|
#include <linux/pci_hotplug.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/delay.h>
|
2006-01-13 15:02:15 +00:00
|
|
|
#include <linux/mutex.h>
|
2018-07-28 05:18:00 +00:00
|
|
|
#include <linux/rwsem.h>
|
2010-10-18 06:31:02 +00:00
|
|
|
#include <linux/workqueue.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-03-09 17:42:01 +00:00
|
|
|
#include "../pcie/portdrv.h"
|
2018-02-14 03:52:18 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
#define MY_NAME "pciehp"
|
|
|
|
|
2012-01-12 23:02:20 +00:00
|
|
|
extern bool pciehp_poll_mode;
|
2005-04-16 22:20:36 +00:00
|
|
|
extern int pciehp_poll_time;
|
2012-01-12 23:02:20 +00:00
|
|
|
extern bool pciehp_debug;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-12-22 01:01:07 +00:00
|
|
|
#define dbg(format, arg...) \
|
2009-02-06 09:23:36 +00:00
|
|
|
do { \
|
|
|
|
if (pciehp_debug) \
|
2015-12-27 21:21:11 +00:00
|
|
|
printk(KERN_DEBUG "%s: " format, MY_NAME, ## arg); \
|
2009-02-06 09:23:36 +00:00
|
|
|
} while (0)
|
2006-12-22 01:01:07 +00:00
|
|
|
#define err(format, arg...) \
|
2015-12-27 21:21:11 +00:00
|
|
|
printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
|
2006-12-22 01:01:07 +00:00
|
|
|
#define info(format, arg...) \
|
2015-12-27 21:21:11 +00:00
|
|
|
printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
|
2006-12-22 01:01:07 +00:00
|
|
|
#define warn(format, arg...) \
|
2015-12-27 21:21:11 +00:00
|
|
|
printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-09-05 03:11:26 +00:00
|
|
|
#define ctrl_dbg(ctrl, format, arg...) \
|
|
|
|
do { \
|
|
|
|
if (pciehp_debug) \
|
2009-02-06 09:23:36 +00:00
|
|
|
dev_printk(KERN_DEBUG, &ctrl->pcie->device, \
|
2008-09-05 03:11:26 +00:00
|
|
|
format, ## arg); \
|
|
|
|
} while (0)
|
|
|
|
#define ctrl_err(ctrl, format, arg...) \
|
|
|
|
dev_err(&ctrl->pcie->device, format, ## arg)
|
|
|
|
#define ctrl_info(ctrl, format, arg...) \
|
|
|
|
dev_info(&ctrl->pcie->device, format, ## arg)
|
|
|
|
#define ctrl_warn(ctrl, format, arg...) \
|
|
|
|
dev_warn(&ctrl->pcie->device, format, ## arg)
|
|
|
|
|
2006-12-22 01:01:02 +00:00
|
|
|
#define SLOT_NAME_SIZE 10
|
2018-07-19 22:27:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* struct controller - PCIe hotplug controller
|
|
|
|
* @ctrl_lock: serializes writes to the Slot Control register
|
|
|
|
* @pcie: pointer to the controller's PCIe port service device
|
2018-07-28 05:18:00 +00:00
|
|
|
* @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
|
2018-07-19 22:27:36 +00:00
|
|
|
* @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
|
2018-07-19 22:27:39 +00:00
|
|
|
* @poll_thread: thread to poll for slot events if no IRQ is available,
|
2018-07-19 22:27:36 +00:00
|
|
|
* 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
|
PCI: pciehp: Convert to threaded IRQ
pciehp's IRQ handler queues up a work item for each event signaled by
the hardware. A more modern alternative is to let a long running
kthread service the events. The IRQ handler's sole job is then to check
whether the IRQ originated from the device in question, acknowledge its
receipt to the hardware to quiesce the interrupt and wake up the kthread.
One benefit is reduced latency to handle the IRQ, which is a necessity
for realtime environments. Another benefit is that we can make pciehp
simpler and more robust by handling events synchronously in process
context, rather than asynchronously by queueing up work items. pciehp's
usage of work items is a historic artifact, it predates the introduction
of threaded IRQ handlers by two years. (The former was introduced in
2007 with commit 5d386e1ac402 ("pciehp: Event handling rework"), the
latter in 2009 with commit 3aa551c9b4c4 ("genirq: add threaded interrupt
handler support").)
Convert pciehp to threaded IRQ handling by retrieving the pending events
in pciehp_isr(), saving them for later consumption by the thread handler
pciehp_ist() and clearing them in the Slot Status register.
By clearing the Slot Status (and thereby acknowledging the events) in
pciehp_isr(), we can avoid requesting the IRQ with IRQF_ONESHOT, which
would have the unpleasant side effect of starving devices sharing the
IRQ until pciehp_ist() has finished.
pciehp_isr() does not count how many times each event occurred, but
merely records the fact *that* an event occurred. If the same event
occurs a second time before pciehp_ist() is woken, that second event
will not be recorded separately, which is problematic according to
commit fad214b0aa72 ("PCI: pciehp: Process all hotplug events before
looking for new ones") because we may miss removal of a card in-between
two back-to-back insertions. We're about to make pciehp_ist() resilient
to missed events. The present commit regresses the driver's behavior
temporarily in order to separate the changes into reviewable chunks.
This doesn't affect regular slow-motion hotplug, only plug-unplug-plug
operations that happen in a timespan shorter than wakeup of the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Mayurkumar Patel <mayurkumar.patel@intel.com>
Cc: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
2018-07-19 22:27:38 +00:00
|
|
|
* @pending_events: used by the IRQ handler to save events retrieved from the
|
|
|
|
* Slot Status register for later consumption by the IRQ thread
|
2018-09-18 19:46:17 +00:00
|
|
|
* @state: current state machine position
|
|
|
|
* @lock: protects reads and writes of @state;
|
|
|
|
* protects scheduling, execution and cancellation of @work
|
|
|
|
* @work: work item to turn the slot on or off after 5 seconds
|
|
|
|
* in response to an Attention Button press
|
|
|
|
* @hotplug_slot: pointer to the structure registered with the PCI hotplug core
|
PCI: pciehp: Enable/disable exclusively from IRQ thread
Besides the IRQ thread, there are several other places in the driver
which enable or disable the slot:
- pciehp_probe() enables the slot if it's occupied and the pciehp_force
module parameter is used.
- pciehp_resume() enables or disables the slot after system sleep.
- pciehp_queue_pushbutton_work() enables or disables the slot after the
5 second delay following an Attention Button press.
- pciehp_sysfs_enable_slot() and pciehp_sysfs_disable_slot() enable or
disable the slot on sysfs write.
This requires locking and complicates pciehp's state machine.
A simplification can be achieved by enabling and disabling the slot
exclusively from the IRQ thread.
Amend the functions listed above to request slot enable/disablement from
the IRQ thread by either synthesizing a Presence Detect Changed event or,
in the case of a disable user request (via sysfs or an Attention Button
press), submitting a newly introduced force disable request. The latter
is needed because the slot shall be forced off despite being occupied.
For this force disable request, avoid colliding with Slot Status register
bits by using a bit number greater than 16.
For synchronous execution of requests (on sysfs write), wait for the
request to finish and retrieve the result. There can only ever be one
sysfs write in flight due to the locking in kernfs_fop_write(), hence
there is no risk of returning the result of a different sysfs request to
user space.
The POWERON_STATE and POWEROFF_STATE is now no longer entered by the
above-listed functions, but solely by the IRQ thread when it begins a
power transition. Afterwards, it moves to STATIC_STATE. The same
applies to canceling the Attention Button work, it likewise becomes an
IRQ thread only operation.
An immediate consequence is that the POWERON_STATE and POWEROFF_STATE is
never observed by the IRQ thread itself, only by functions called in a
different context, such as pciehp_sysfs_enable_slot(). So remove
handling of these states from pciehp_handle_button_press() and
pciehp_handle_link_change() which are exclusively called from the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2018-07-19 22:27:46 +00:00
|
|
|
* @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
|
2018-09-18 19:46:17 +00:00
|
|
|
*
|
|
|
|
* PCIe hotplug has a 1:1 relationship between controller and slot, hence
|
|
|
|
* unlike other drivers, the two aren't represented by separate structures.
|
2018-07-19 22:27:36 +00:00
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
struct controller {
|
2018-07-19 22:27:36 +00:00
|
|
|
struct mutex ctrl_lock;
|
|
|
|
struct pcie_device *pcie;
|
2018-07-28 05:18:00 +00:00
|
|
|
struct rw_semaphore reset_lock;
|
2018-07-19 22:27:36 +00:00
|
|
|
wait_queue_head_t queue;
|
2008-04-25 21:39:06 +00:00
|
|
|
u32 slot_cap;
|
2014-08-15 23:18:44 +00:00
|
|
|
u16 slot_ctrl;
|
2018-07-19 22:27:39 +00:00
|
|
|
struct task_struct *poll_thread;
|
PCI: pciehp: Compute timeout from hotplug command start time
If we issue a hotplug command, go do something else, then come back and
wait for the command to complete, we don't have to wait the whole timeout
period, because some of it elapsed while we were doing something else.
Keep track of the time we issued the command, and wait only until the
timeout period from that point has elapsed.
For controllers with errata like Intel CF118, we previously timed out
before issuing the second hotplug command:
At time T1 (during boot):
- Write DLLSCE, ABPE, PDCE, etc. to Slot Control
At time T2 (hotplug event):
- Wait for command completion (CC) in Slot Status
- Timeout at T2 + 1 second because CC is never set in Slot Status
- Write PCC, PIC, etc. to Slot Control
With this change, we wait until T1 + 1 second instead of T2 + 1 second.
If the hotplug event is more than 1 second after the boot-time
initialization, we won't wait for the timeout at all.
We still emit a "Timeout on hotplug command" message if it timed out; we
should see this on the first hotplug event on every controller with this
erratum, as well as on real errors on controllers without the erratum.
Link: http://www.intel.com/content/www/us/en/processors/xeon/xeon-e7-v2-spec-update.html
Tested-by: Rajat Jain <rajatxjain@gmail.com> (IDT 807a controller)
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Yinghai Lu <yinghai@kernel.org>
2014-06-14 15:55:49 +00:00
|
|
|
unsigned long cmd_started; /* jiffies */
|
2009-02-03 06:06:18 +00:00
|
|
|
unsigned int cmd_busy:1;
|
2008-10-22 05:31:44 +00:00
|
|
|
unsigned int link_active_reporting:1;
|
2009-01-29 03:31:18 +00:00
|
|
|
unsigned int notification_enabled:1;
|
2009-02-03 06:06:16 +00:00
|
|
|
unsigned int power_fault_detected;
|
PCI: pciehp: Convert to threaded IRQ
pciehp's IRQ handler queues up a work item for each event signaled by
the hardware. A more modern alternative is to let a long running
kthread service the events. The IRQ handler's sole job is then to check
whether the IRQ originated from the device in question, acknowledge its
receipt to the hardware to quiesce the interrupt and wake up the kthread.
One benefit is reduced latency to handle the IRQ, which is a necessity
for realtime environments. Another benefit is that we can make pciehp
simpler and more robust by handling events synchronously in process
context, rather than asynchronously by queueing up work items. pciehp's
usage of work items is a historic artifact, it predates the introduction
of threaded IRQ handlers by two years. (The former was introduced in
2007 with commit 5d386e1ac402 ("pciehp: Event handling rework"), the
latter in 2009 with commit 3aa551c9b4c4 ("genirq: add threaded interrupt
handler support").)
Convert pciehp to threaded IRQ handling by retrieving the pending events
in pciehp_isr(), saving them for later consumption by the thread handler
pciehp_ist() and clearing them in the Slot Status register.
By clearing the Slot Status (and thereby acknowledging the events) in
pciehp_isr(), we can avoid requesting the IRQ with IRQF_ONESHOT, which
would have the unpleasant side effect of starving devices sharing the
IRQ until pciehp_ist() has finished.
pciehp_isr() does not count how many times each event occurred, but
merely records the fact *that* an event occurred. If the same event
occurs a second time before pciehp_ist() is woken, that second event
will not be recorded separately, which is problematic according to
commit fad214b0aa72 ("PCI: pciehp: Process all hotplug events before
looking for new ones") because we may miss removal of a card in-between
two back-to-back insertions. We're about to make pciehp_ist() resilient
to missed events. The present commit regresses the driver's behavior
temporarily in order to separate the changes into reviewable chunks.
This doesn't affect regular slow-motion hotplug, only plug-unplug-plug
operations that happen in a timespan shorter than wakeup of the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Mayurkumar Patel <mayurkumar.patel@intel.com>
Cc: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
2018-07-19 22:27:38 +00:00
|
|
|
atomic_t pending_events;
|
2018-09-18 19:46:17 +00:00
|
|
|
u8 state;
|
|
|
|
struct mutex lock;
|
|
|
|
struct delayed_work work;
|
|
|
|
struct hotplug_slot *hotplug_slot;
|
PCI: pciehp: Enable/disable exclusively from IRQ thread
Besides the IRQ thread, there are several other places in the driver
which enable or disable the slot:
- pciehp_probe() enables the slot if it's occupied and the pciehp_force
module parameter is used.
- pciehp_resume() enables or disables the slot after system sleep.
- pciehp_queue_pushbutton_work() enables or disables the slot after the
5 second delay following an Attention Button press.
- pciehp_sysfs_enable_slot() and pciehp_sysfs_disable_slot() enable or
disable the slot on sysfs write.
This requires locking and complicates pciehp's state machine.
A simplification can be achieved by enabling and disabling the slot
exclusively from the IRQ thread.
Amend the functions listed above to request slot enable/disablement from
the IRQ thread by either synthesizing a Presence Detect Changed event or,
in the case of a disable user request (via sysfs or an Attention Button
press), submitting a newly introduced force disable request. The latter
is needed because the slot shall be forced off despite being occupied.
For this force disable request, avoid colliding with Slot Status register
bits by using a bit number greater than 16.
For synchronous execution of requests (on sysfs write), wait for the
request to finish and retrieve the result. There can only ever be one
sysfs write in flight due to the locking in kernfs_fop_write(), hence
there is no risk of returning the result of a different sysfs request to
user space.
The POWERON_STATE and POWEROFF_STATE is now no longer entered by the
above-listed functions, but solely by the IRQ thread when it begins a
power transition. Afterwards, it moves to STATIC_STATE. The same
applies to canceling the Attention Button work, it likewise becomes an
IRQ thread only operation.
An immediate consequence is that the POWERON_STATE and POWEROFF_STATE is
never observed by the IRQ thread itself, only by functions called in a
different context, such as pciehp_sysfs_enable_slot(). So remove
handling of these states from pciehp_handle_button_press() and
pciehp_handle_link_change() which are exclusively called from the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2018-07-19 22:27:46 +00:00
|
|
|
int request_result;
|
|
|
|
wait_queue_head_t requester;
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2018-07-19 22:27:45 +00:00
|
|
|
/**
|
|
|
|
* DOC: Slot state
|
|
|
|
*
|
|
|
|
* @OFF_STATE: slot is powered off, no subordinate devices are enumerated
|
|
|
|
* @BLINKINGON_STATE: slot will be powered on after the 5 second delay,
|
|
|
|
* green led is blinking
|
|
|
|
* @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay,
|
|
|
|
* 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
|
2005-04-16 22:20:36 +00:00
|
|
|
#define BLINKINGON_STATE 1
|
|
|
|
#define BLINKINGOFF_STATE 2
|
|
|
|
#define POWERON_STATE 3
|
|
|
|
#define POWEROFF_STATE 4
|
2018-07-19 22:27:45 +00:00
|
|
|
#define ON_STATE 5
|
2005-04-16 22:20:36 +00:00
|
|
|
|
PCI: pciehp: Enable/disable exclusively from IRQ thread
Besides the IRQ thread, there are several other places in the driver
which enable or disable the slot:
- pciehp_probe() enables the slot if it's occupied and the pciehp_force
module parameter is used.
- pciehp_resume() enables or disables the slot after system sleep.
- pciehp_queue_pushbutton_work() enables or disables the slot after the
5 second delay following an Attention Button press.
- pciehp_sysfs_enable_slot() and pciehp_sysfs_disable_slot() enable or
disable the slot on sysfs write.
This requires locking and complicates pciehp's state machine.
A simplification can be achieved by enabling and disabling the slot
exclusively from the IRQ thread.
Amend the functions listed above to request slot enable/disablement from
the IRQ thread by either synthesizing a Presence Detect Changed event or,
in the case of a disable user request (via sysfs or an Attention Button
press), submitting a newly introduced force disable request. The latter
is needed because the slot shall be forced off despite being occupied.
For this force disable request, avoid colliding with Slot Status register
bits by using a bit number greater than 16.
For synchronous execution of requests (on sysfs write), wait for the
request to finish and retrieve the result. There can only ever be one
sysfs write in flight due to the locking in kernfs_fop_write(), hence
there is no risk of returning the result of a different sysfs request to
user space.
The POWERON_STATE and POWEROFF_STATE is now no longer entered by the
above-listed functions, but solely by the IRQ thread when it begins a
power transition. Afterwards, it moves to STATIC_STATE. The same
applies to canceling the Attention Button work, it likewise becomes an
IRQ thread only operation.
An immediate consequence is that the POWERON_STATE and POWEROFF_STATE is
never observed by the IRQ thread itself, only by functions called in a
different context, such as pciehp_sysfs_enable_slot(). So remove
handling of these states from pciehp_handle_button_press() and
pciehp_handle_link_change() which are exclusively called from the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2018-07-19 22:27:46 +00:00
|
|
|
/**
|
|
|
|
* 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
|
PCI: pciehp: Support interrupts sent from D3hot
If a hotplug port is able to send an interrupt, one would naively assume
that it is accessible at that moment. After all, if it wouldn't be
accessible, i.e. if its parent is in D3hot and the link to the hotplug
port is thus down, how should an interrupt come through?
It turns out that assumption is wrong at least for Thunderbolt: Even
though its parents are in D3hot, a Thunderbolt hotplug port is able to
signal interrupts. Because the port's config space is inaccessible and
resuming the parents may sleep, the hard IRQ handler has to defer
runtime resuming the parents and reading the Slot Status register to the
IRQ thread.
If the hotplug port uses a level-triggered INTx interrupt, it needs to
be masked until the IRQ thread has cleared the signaled events. For
simplicity, this commit also masks edge-triggered MSI/MSI-X interrupts.
Note that if the interrupt is shared (which can only happen for INTx),
other devices are starved from receiving interrupts until the IRQ thread
is scheduled, has runtime resumed the hotplug port's parents and has
read and cleared the Slot Status register.
That delay is dominated by the 10 ms D3hot->D0 transition time of each
parent port. The worst case is a Thunderbolt downstream port at the
end of a daisy chain: There may be up to six Thunderbolt controllers
in-between it and the root port, each comprising an upstream and
downstream port, plus its own upstream port. That's 13 x 10 = 130 ms.
Possible mitigations are polling the interrupt while it's disabled or
reducing the d3_delay of Thunderbolt ports if possible.
Open code masking of the interrupt instead of requesting it with the
IRQF_ONESHOT flag to minimize the period during which it is masked.
(IRQF_ONESHOT unmasks the IRQ only after the IRQ thread has finished.)
PCIe r4.0 sec 6.7.3.4 states that "If wake generation is required by the
associated form factor specification, a hotplug capable Downstream Port
must support generation of a wakeup event (using the PME mechanism) on
hotplug events that occur when the system is in a sleep state or the
Port is in device state D1, D2, or D3Hot."
This would seem to imply that PME needs to be enabled on the hotplug
port when it is runtime suspended. pci_enable_wake() currently doesn't
enable PME on bridges, it may be necessary to add an exemption for
hotplug bridges there. On "Light Ridge" Thunderbolt controllers, the
PME_Status bit is not set when an interrupt occurs while the hotplug
port is in D3hot, even if PME is enabled. (I've tested this on a Mac
and we hardcode the OSC_PCI_EXPRESS_PME_CONTROL bit to 0 on Macs in
negotiate_os_control(), modifying it to 1 didn't change the behavior.)
(Side note: Section 6.7.3.4 also states that "PME and Hot-Plug Event
interrupts (when both are implemented) always share the same MSI or
MSI-X vector". That would only seem to apply to Root Ports, however
the section never mentions Root Ports, only Downstream Ports. This is
explained in the definition of "Downstream Port" in the "Terms and
Acronyms" section of the PCIe Base Spec: "The Ports on a Switch that
are not the Upstream Port are Downstream Ports. All Ports on a Root
Complex are Downstream Ports.")
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
Cc: Ashok Raj <ashok.raj@intel.com>
Cc: Keith Busch <keith.busch@intel.com>
Cc: Yinghai Lu <yinghai@kernel.org>
2018-07-28 05:18:00 +00:00
|
|
|
* %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
|
PCI: pciehp: Enable/disable exclusively from IRQ thread
Besides the IRQ thread, there are several other places in the driver
which enable or disable the slot:
- pciehp_probe() enables the slot if it's occupied and the pciehp_force
module parameter is used.
- pciehp_resume() enables or disables the slot after system sleep.
- pciehp_queue_pushbutton_work() enables or disables the slot after the
5 second delay following an Attention Button press.
- pciehp_sysfs_enable_slot() and pciehp_sysfs_disable_slot() enable or
disable the slot on sysfs write.
This requires locking and complicates pciehp's state machine.
A simplification can be achieved by enabling and disabling the slot
exclusively from the IRQ thread.
Amend the functions listed above to request slot enable/disablement from
the IRQ thread by either synthesizing a Presence Detect Changed event or,
in the case of a disable user request (via sysfs or an Attention Button
press), submitting a newly introduced force disable request. The latter
is needed because the slot shall be forced off despite being occupied.
For this force disable request, avoid colliding with Slot Status register
bits by using a bit number greater than 16.
For synchronous execution of requests (on sysfs write), wait for the
request to finish and retrieve the result. There can only ever be one
sysfs write in flight due to the locking in kernfs_fop_write(), hence
there is no risk of returning the result of a different sysfs request to
user space.
The POWERON_STATE and POWEROFF_STATE is now no longer entered by the
above-listed functions, but solely by the IRQ thread when it begins a
power transition. Afterwards, it moves to STATIC_STATE. The same
applies to canceling the Attention Button work, it likewise becomes an
IRQ thread only operation.
An immediate consequence is that the POWERON_STATE and POWEROFF_STATE is
never observed by the IRQ thread itself, only by functions called in a
different context, such as pciehp_sysfs_enable_slot(). So remove
handling of these states from pciehp_handle_button_press() and
pciehp_handle_link_change() which are exclusively called from the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2018-07-19 22:27:46 +00:00
|
|
|
*/
|
|
|
|
#define DISABLE_SLOT (1 << 16)
|
PCI: pciehp: Support interrupts sent from D3hot
If a hotplug port is able to send an interrupt, one would naively assume
that it is accessible at that moment. After all, if it wouldn't be
accessible, i.e. if its parent is in D3hot and the link to the hotplug
port is thus down, how should an interrupt come through?
It turns out that assumption is wrong at least for Thunderbolt: Even
though its parents are in D3hot, a Thunderbolt hotplug port is able to
signal interrupts. Because the port's config space is inaccessible and
resuming the parents may sleep, the hard IRQ handler has to defer
runtime resuming the parents and reading the Slot Status register to the
IRQ thread.
If the hotplug port uses a level-triggered INTx interrupt, it needs to
be masked until the IRQ thread has cleared the signaled events. For
simplicity, this commit also masks edge-triggered MSI/MSI-X interrupts.
Note that if the interrupt is shared (which can only happen for INTx),
other devices are starved from receiving interrupts until the IRQ thread
is scheduled, has runtime resumed the hotplug port's parents and has
read and cleared the Slot Status register.
That delay is dominated by the 10 ms D3hot->D0 transition time of each
parent port. The worst case is a Thunderbolt downstream port at the
end of a daisy chain: There may be up to six Thunderbolt controllers
in-between it and the root port, each comprising an upstream and
downstream port, plus its own upstream port. That's 13 x 10 = 130 ms.
Possible mitigations are polling the interrupt while it's disabled or
reducing the d3_delay of Thunderbolt ports if possible.
Open code masking of the interrupt instead of requesting it with the
IRQF_ONESHOT flag to minimize the period during which it is masked.
(IRQF_ONESHOT unmasks the IRQ only after the IRQ thread has finished.)
PCIe r4.0 sec 6.7.3.4 states that "If wake generation is required by the
associated form factor specification, a hotplug capable Downstream Port
must support generation of a wakeup event (using the PME mechanism) on
hotplug events that occur when the system is in a sleep state or the
Port is in device state D1, D2, or D3Hot."
This would seem to imply that PME needs to be enabled on the hotplug
port when it is runtime suspended. pci_enable_wake() currently doesn't
enable PME on bridges, it may be necessary to add an exemption for
hotplug bridges there. On "Light Ridge" Thunderbolt controllers, the
PME_Status bit is not set when an interrupt occurs while the hotplug
port is in D3hot, even if PME is enabled. (I've tested this on a Mac
and we hardcode the OSC_PCI_EXPRESS_PME_CONTROL bit to 0 on Macs in
negotiate_os_control(), modifying it to 1 didn't change the behavior.)
(Side note: Section 6.7.3.4 also states that "PME and Hot-Plug Event
interrupts (when both are implemented) always share the same MSI or
MSI-X vector". That would only seem to apply to Root Ports, however
the section never mentions Root Ports, only Downstream Ports. This is
explained in the definition of "Downstream Port" in the "Terms and
Acronyms" section of the PCIe Base Spec: "The Ports on a Switch that
are not the Upstream Port are Downstream Ports. All Ports on a Root
Complex are Downstream Ports.")
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
Cc: Ashok Raj <ashok.raj@intel.com>
Cc: Keith Busch <keith.busch@intel.com>
Cc: Yinghai Lu <yinghai@kernel.org>
2018-07-28 05:18:00 +00:00
|
|
|
#define RERUN_ISR (1 << 17)
|
PCI: pciehp: Enable/disable exclusively from IRQ thread
Besides the IRQ thread, there are several other places in the driver
which enable or disable the slot:
- pciehp_probe() enables the slot if it's occupied and the pciehp_force
module parameter is used.
- pciehp_resume() enables or disables the slot after system sleep.
- pciehp_queue_pushbutton_work() enables or disables the slot after the
5 second delay following an Attention Button press.
- pciehp_sysfs_enable_slot() and pciehp_sysfs_disable_slot() enable or
disable the slot on sysfs write.
This requires locking and complicates pciehp's state machine.
A simplification can be achieved by enabling and disabling the slot
exclusively from the IRQ thread.
Amend the functions listed above to request slot enable/disablement from
the IRQ thread by either synthesizing a Presence Detect Changed event or,
in the case of a disable user request (via sysfs or an Attention Button
press), submitting a newly introduced force disable request. The latter
is needed because the slot shall be forced off despite being occupied.
For this force disable request, avoid colliding with Slot Status register
bits by using a bit number greater than 16.
For synchronous execution of requests (on sysfs write), wait for the
request to finish and retrieve the result. There can only ever be one
sysfs write in flight due to the locking in kernfs_fop_write(), hence
there is no risk of returning the result of a different sysfs request to
user space.
The POWERON_STATE and POWEROFF_STATE is now no longer entered by the
above-listed functions, but solely by the IRQ thread when it begins a
power transition. Afterwards, it moves to STATIC_STATE. The same
applies to canceling the Attention Button work, it likewise becomes an
IRQ thread only operation.
An immediate consequence is that the POWERON_STATE and POWEROFF_STATE is
never observed by the IRQ thread itself, only by functions called in a
different context, such as pciehp_sysfs_enable_slot(). So remove
handling of these states from pciehp_handle_button_press() and
pciehp_handle_link_change() which are exclusively called from the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2018-07-19 22:27:46 +00:00
|
|
|
|
2009-09-15 08:34:05 +00:00
|
|
|
#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
|
|
|
|
#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
|
|
|
|
#define MRL_SENS(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_MRLSP)
|
|
|
|
#define ATTN_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_AIP)
|
|
|
|
#define PWR_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PIP)
|
|
|
|
#define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_HPS)
|
|
|
|
#define EMI(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_EIP)
|
|
|
|
#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
|
2014-04-05 21:05:07 +00:00
|
|
|
#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
|
2005-04-16 22:20:36 +00:00
|
|
|
|
PCI: pciehp: Enable/disable exclusively from IRQ thread
Besides the IRQ thread, there are several other places in the driver
which enable or disable the slot:
- pciehp_probe() enables the slot if it's occupied and the pciehp_force
module parameter is used.
- pciehp_resume() enables or disables the slot after system sleep.
- pciehp_queue_pushbutton_work() enables or disables the slot after the
5 second delay following an Attention Button press.
- pciehp_sysfs_enable_slot() and pciehp_sysfs_disable_slot() enable or
disable the slot on sysfs write.
This requires locking and complicates pciehp's state machine.
A simplification can be achieved by enabling and disabling the slot
exclusively from the IRQ thread.
Amend the functions listed above to request slot enable/disablement from
the IRQ thread by either synthesizing a Presence Detect Changed event or,
in the case of a disable user request (via sysfs or an Attention Button
press), submitting a newly introduced force disable request. The latter
is needed because the slot shall be forced off despite being occupied.
For this force disable request, avoid colliding with Slot Status register
bits by using a bit number greater than 16.
For synchronous execution of requests (on sysfs write), wait for the
request to finish and retrieve the result. There can only ever be one
sysfs write in flight due to the locking in kernfs_fop_write(), hence
there is no risk of returning the result of a different sysfs request to
user space.
The POWERON_STATE and POWEROFF_STATE is now no longer entered by the
above-listed functions, but solely by the IRQ thread when it begins a
power transition. Afterwards, it moves to STATIC_STATE. The same
applies to canceling the Attention Button work, it likewise becomes an
IRQ thread only operation.
An immediate consequence is that the POWERON_STATE and POWEROFF_STATE is
never observed by the IRQ thread itself, only by functions called in a
different context, such as pciehp_sysfs_enable_slot(). So remove
handling of these states from pciehp_handle_button_press() and
pciehp_handle_link_change() which are exclusively called from the IRQ
thread.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2018-07-19 22:27:46 +00:00
|
|
|
void pciehp_request(struct controller *ctrl, int action);
|
2018-09-18 19:46:17 +00:00
|
|
|
void pciehp_handle_button_press(struct controller *ctrl);
|
|
|
|
void pciehp_handle_disable_request(struct controller *ctrl);
|
|
|
|
void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events);
|
|
|
|
int pciehp_configure_device(struct controller *ctrl);
|
|
|
|
void pciehp_unconfigure_device(struct controller *ctrl, bool presence);
|
2013-04-12 18:02:59 +00:00
|
|
|
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
2008-06-20 03:07:08 +00:00
|
|
|
struct controller *pcie_init(struct pcie_device *dev);
|
2009-01-29 03:31:18 +00:00
|
|
|
int pcie_init_notification(struct controller *ctrl);
|
2018-07-19 22:27:32 +00:00
|
|
|
void pcie_shutdown_notification(struct controller *ctrl);
|
2018-07-19 22:27:53 +00:00
|
|
|
void pcie_clear_hotplug_events(struct controller *ctrl);
|
2018-09-18 19:46:17 +00:00
|
|
|
int pciehp_power_on_slot(struct controller *ctrl);
|
|
|
|
void pciehp_power_off_slot(struct controller *ctrl);
|
|
|
|
void pciehp_get_power_status(struct controller *ctrl, u8 *status);
|
|
|
|
|
|
|
|
void pciehp_set_attention_status(struct controller *ctrl, u8 status);
|
|
|
|
void pciehp_get_latch_status(struct controller *ctrl, u8 *status);
|
|
|
|
int pciehp_query_power_fault(struct controller *ctrl);
|
|
|
|
void pciehp_green_led_on(struct controller *ctrl);
|
|
|
|
void pciehp_green_led_off(struct controller *ctrl);
|
|
|
|
void pciehp_green_led_blink(struct controller *ctrl);
|
2018-09-08 07:59:01 +00:00
|
|
|
bool pciehp_card_present(struct controller *ctrl);
|
|
|
|
bool pciehp_card_present_or_link_active(struct controller *ctrl);
|
2009-09-15 08:30:48 +00:00
|
|
|
int pciehp_check_link_status(struct controller *ctrl);
|
2014-02-05 02:28:43 +00:00
|
|
|
bool pciehp_check_link_active(struct controller *ctrl);
|
2009-09-15 08:30:48 +00:00
|
|
|
void pciehp_release_ctrl(struct controller *ctrl);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-08-19 14:29:00 +00:00
|
|
|
int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot);
|
|
|
|
int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot);
|
|
|
|
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe);
|
|
|
|
int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status);
|
PCI: pciehp: Allow exclusive userspace control of indicators
PCIe hotplug supports optional Attention and Power Indicators, which are
used internally by pciehp. Users can't control the Power Indicator, but
they can control the Attention Indicator by writing to a sysfs "attention"
file.
The Slot Control register has two bits for each indicator, and the PCIe
spec defines the encodings for each as (Reserved/On/Blinking/Off). For
sysfs "attention" writes, pciehp_set_attention_status() maps into these
encodings, so the only useful write values are 0 (Off), 1 (On), and 2
(Blinking).
However, some platforms use all four bits for platform-specific indicators,
and they need to allow direct user control of them while preventing pciehp
from using them at all.
Add a "hotplug_user_indicators" flag to the pci_dev structure. When set,
pciehp does not use either the Attention Indicator or the Power Indicator,
and the low four bits (values 0x0 - 0xf) of sysfs "attention" write values
are written directly to the Attention Indicator Control and Power Indicator
Control fields.
[bhelgaas: changelog, rename flag and accessors to s/attention/indicator/]
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2016-09-13 16:31:59 +00:00
|
|
|
int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
|
|
|
|
int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);
|
|
|
|
|
2018-09-18 19:46:17 +00:00
|
|
|
static inline const char *slot_name(struct controller *ctrl)
|
2008-10-20 23:41:38 +00:00
|
|
|
{
|
2018-09-18 19:46:17 +00:00
|
|
|
return hotplug_slot_name(ctrl->hotplug_slot);
|
2008-10-20 23:41:38 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif /* _PCIEHP_H */
|