mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Commit a97396c6eb
("PCI: pciehp: Ignore Link Down/Up caused by DPC")
amended PCIe hotplug to not bring down the slot upon Data Link Layer State
Changed events caused by Downstream Port Containment.
However Keith reports off-list that if the slot uses in-band presence
detect (i.e. Presence Detect State is derived from Data Link Layer Link
Active), DPC also causes a spurious Presence Detect Changed event.
This needs to be ignored as well.
Unfortunately there's no register indicating that in-band presence detect
is used. PCIe r5.0 sec 7.5.3.10 introduced the In-Band PD Disable bit in
the Slot Control Register. The PCIe hotplug driver sets this bit on
ports supporting it. But older ports may still use in-band presence
detect.
If in-band presence detect can be disabled, Presence Detect Changed events
occurring during DPC must not be ignored because they signal device
replacement. On all other ports, device replacement cannot be detected
reliably because the Presence Detect Changed event could be a side effect
of DPC. On those (older) ports, perform a best-effort device replacement
check by comparing the Vendor ID, Device ID and other data in Config Space
with the values cached in struct pci_dev. Use the existing helper
pciehp_device_replaced() to accomplish this. It is currently #ifdef'ed to
CONFIG_PM_SLEEP in pciehp_core.c, so move it to pciehp_hpc.c where most
other functions accessing config space reside.
Reported-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/fa264ff71952915c4e35a53c89eb0cde8455a5c5.1744298239.git.lukas@wunner.de
212 lines
8.5 KiB
C
212 lines
8.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
/*
|
|
* 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.
|
|
*
|
|
* Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
|
*
|
|
*/
|
|
#ifndef _PCIEHP_H
|
|
#define _PCIEHP_H
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_hotplug.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include "../pcie/portdrv.h"
|
|
|
|
extern bool pciehp_poll_mode;
|
|
extern int pciehp_poll_time;
|
|
|
|
/*
|
|
* Set CONFIG_DYNAMIC_DEBUG=y and boot with 'dyndbg="file pciehp* +p"' to
|
|
* enable debug messages.
|
|
*/
|
|
#define ctrl_dbg(ctrl, format, arg...) \
|
|
pci_dbg(ctrl->pcie->port, format, ## arg)
|
|
#define ctrl_err(ctrl, format, arg...) \
|
|
pci_err(ctrl->pcie->port, format, ## arg)
|
|
#define ctrl_info(ctrl, format, arg...) \
|
|
pci_info(ctrl->pcie->port, format, ## arg)
|
|
#define ctrl_warn(ctrl, format, arg...) \
|
|
pci_warn(ctrl->pcie->port, format, ## arg)
|
|
|
|
#define SLOT_NAME_SIZE 10
|
|
|
|
/**
|
|
* struct controller - PCIe hotplug controller
|
|
* @pcie: pointer to the controller's PCIe port service device
|
|
* @dsn: cached copy of Device Serial Number of Function 0 in the hotplug slot
|
|
* (PCIe r6.2 sec 7.9.3); used to determine whether a hotplugged device
|
|
* was replaced with a different one during system sleep
|
|
* @slot_cap: cached copy of the Slot Capabilities register
|
|
* @inband_presence_disabled: In-Band Presence Detect Disable supported by
|
|
* controller and disabled per spec recommendation (PCIe r5.0, appendix I
|
|
* implementation note)
|
|
* @slot_ctrl: cached copy of the Slot Control register
|
|
* @ctrl_lock: serializes writes to the Slot Control register
|
|
* @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
|
|
* @queue: wait queue to wake up on reception of a Command Completed event,
|
|
* used for synchronous writes to the Slot Control register
|
|
* @pending_events: used by the IRQ handler to save events retrieved from the
|
|
* Slot Status register for later consumption by the IRQ thread
|
|
* @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
|
|
* @poll_thread: thread to poll for slot events if no IRQ is available,
|
|
* enabled with pciehp_poll_mode module parameter
|
|
* @state: current state machine position
|
|
* @state_lock: protects reads and writes of @state;
|
|
* protects scheduling, execution and cancellation of @button_work
|
|
* @button_work: work item to turn the slot on or off after 5 seconds
|
|
* in response to an Attention Button press
|
|
* @hotplug_slot: structure registered with the PCI hotplug core
|
|
* @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
|
|
* @depth: Number of additional hotplug ports in the path to the root bus,
|
|
* used as lock subclass for @reset_lock
|
|
* @ist_running: flag to keep user request waiting while IRQ thread is running
|
|
* @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
|
|
*
|
|
* PCIe hotplug has a 1:1 relationship between controller and slot, hence
|
|
* unlike other drivers, the two aren't represented by separate structures.
|
|
*/
|
|
struct controller {
|
|
struct pcie_device *pcie;
|
|
u64 dsn;
|
|
|
|
u32 slot_cap; /* capabilities and quirks */
|
|
unsigned int inband_presence_disabled:1;
|
|
|
|
u16 slot_ctrl; /* control register access */
|
|
struct mutex ctrl_lock;
|
|
unsigned long cmd_started;
|
|
unsigned int cmd_busy:1;
|
|
wait_queue_head_t queue;
|
|
|
|
atomic_t pending_events; /* event handling */
|
|
unsigned int notification_enabled:1;
|
|
unsigned int power_fault_detected;
|
|
struct task_struct *poll_thread;
|
|
|
|
u8 state; /* state machine */
|
|
struct mutex state_lock;
|
|
struct delayed_work button_work;
|
|
|
|
struct hotplug_slot hotplug_slot; /* hotplug core interface */
|
|
struct rw_semaphore reset_lock;
|
|
unsigned int depth;
|
|
unsigned int ist_running;
|
|
int request_result;
|
|
wait_queue_head_t requester;
|
|
};
|
|
|
|
/**
|
|
* 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,
|
|
* Power Indicator is blinking
|
|
* @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay,
|
|
* Power Indicator 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 BLINKINGOFF_STATE 2
|
|
#define POWERON_STATE 3
|
|
#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 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 NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
|
|
#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
|
|
|
|
void pciehp_request(struct controller *ctrl, int action);
|
|
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);
|
|
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
|
struct controller *pcie_init(struct pcie_device *dev);
|
|
int pcie_init_notification(struct controller *ctrl);
|
|
void pcie_shutdown_notification(struct controller *ctrl);
|
|
void pcie_clear_hotplug_events(struct controller *ctrl);
|
|
void pcie_enable_interrupt(struct controller *ctrl);
|
|
void pcie_disable_interrupt(struct controller *ctrl);
|
|
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);
|
|
|
|
#define INDICATOR_NOOP -1 /* Leave indicator unchanged */
|
|
void pciehp_set_indicators(struct controller *ctrl, int pwr, int attn);
|
|
|
|
void pciehp_get_latch_status(struct controller *ctrl, u8 *status);
|
|
int pciehp_query_power_fault(struct controller *ctrl);
|
|
int pciehp_card_present(struct controller *ctrl);
|
|
int pciehp_card_present_or_link_active(struct controller *ctrl);
|
|
int pciehp_check_link_status(struct controller *ctrl);
|
|
int pciehp_check_link_active(struct controller *ctrl);
|
|
bool pciehp_device_replaced(struct controller *ctrl);
|
|
void pciehp_release_ctrl(struct controller *ctrl);
|
|
|
|
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, bool probe);
|
|
int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status);
|
|
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);
|
|
|
|
int pciehp_slot_reset(struct pcie_device *dev);
|
|
|
|
static inline const char *slot_name(struct controller *ctrl)
|
|
{
|
|
return hotplug_slot_name(&ctrl->hotplug_slot);
|
|
}
|
|
|
|
static inline struct controller *to_ctrl(struct hotplug_slot *hotplug_slot)
|
|
{
|
|
return container_of(hotplug_slot, struct controller, hotplug_slot);
|
|
}
|
|
|
|
#endif /* _PCIEHP_H */
|