2018-01-27 04:22:04 +08:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
2005-04-17 06:20:36 +08: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-17 06:16:10 +08:00
|
|
|
* Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
#ifndef _PCIEHP_H
|
|
|
|
#define _PCIEHP_H
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/pci.h>
|
2006-10-14 11:05:19 +08:00
|
|
|
#include <linux/pci_hotplug.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/delay.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
#include <linux/sched/signal.h> /* signal_pending() */
|
2006-01-13 23:02:15 +08:00
|
|
|
#include <linux/mutex.h>
|
2010-10-18 14:31:02 +08:00
|
|
|
#include <linux/workqueue.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-03-10 01:42:01 +08:00
|
|
|
#include "../pcie/portdrv.h"
|
2018-02-14 11:52:18 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define MY_NAME "pciehp"
|
|
|
|
|
2012-01-13 07:02:20 +08:00
|
|
|
extern bool pciehp_poll_mode;
|
2005-04-17 06:20:36 +08:00
|
|
|
extern int pciehp_poll_time;
|
2012-01-13 07:02:20 +08:00
|
|
|
extern bool pciehp_debug;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-22 09:01:07 +08:00
|
|
|
#define dbg(format, arg...) \
|
2009-02-06 17:23:36 +08:00
|
|
|
do { \
|
|
|
|
if (pciehp_debug) \
|
2015-12-28 05:21:11 +08:00
|
|
|
printk(KERN_DEBUG "%s: " format, MY_NAME, ## arg); \
|
2009-02-06 17:23:36 +08:00
|
|
|
} while (0)
|
2006-12-22 09:01:07 +08:00
|
|
|
#define err(format, arg...) \
|
2015-12-28 05:21:11 +08:00
|
|
|
printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
|
2006-12-22 09:01:07 +08:00
|
|
|
#define info(format, arg...) \
|
2015-12-28 05:21:11 +08:00
|
|
|
printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
|
2006-12-22 09:01:07 +08:00
|
|
|
#define warn(format, arg...) \
|
2015-12-28 05:21:11 +08:00
|
|
|
printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-09-05 11:11:26 +08:00
|
|
|
#define ctrl_dbg(ctrl, format, arg...) \
|
|
|
|
do { \
|
|
|
|
if (pciehp_debug) \
|
2009-02-06 17:23:36 +08:00
|
|
|
dev_printk(KERN_DEBUG, &ctrl->pcie->device, \
|
2008-09-05 11:11:26 +08: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 09:01:02 +08:00
|
|
|
#define SLOT_NAME_SIZE 10
|
2018-07-20 06:27:36 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* @hotplug_lock: serializes calls to pciehp_enable_slot() and
|
|
|
|
* pciehp_disable_slot()
|
|
|
|
* @wq: work queue on which @work is scheduled;
|
|
|
|
* also used to queue interrupt events and slot enablement and disablement
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
struct slot {
|
|
|
|
u8 state;
|
|
|
|
struct controller *ctrl;
|
|
|
|
struct hotplug_slot *hotplug_slot;
|
2018-07-20 06:27:36 +08:00
|
|
|
struct delayed_work work;
|
2007-03-07 07:02:26 +08:00
|
|
|
struct mutex lock;
|
2014-02-05 10:31:11 +08:00
|
|
|
struct mutex hotplug_lock;
|
2013-01-11 10:15:54 +08:00
|
|
|
struct workqueue_struct *wq;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct event_info {
|
|
|
|
u32 event_type;
|
2007-03-07 07:02:26 +08:00
|
|
|
struct slot *p_slot;
|
|
|
|
struct work_struct work;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2018-07-20 06:27:36 +08: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
|
|
|
|
* @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_timer: timer 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
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
struct controller {
|
2018-07-20 06:27:36 +08:00
|
|
|
struct mutex ctrl_lock;
|
|
|
|
struct pcie_device *pcie;
|
2009-09-15 16:24:46 +08:00
|
|
|
struct slot *slot;
|
2018-07-20 06:27:36 +08:00
|
|
|
wait_queue_head_t queue;
|
2008-04-26 05:39:06 +08:00
|
|
|
u32 slot_cap;
|
2014-08-16 07:18:44 +08:00
|
|
|
u16 slot_ctrl;
|
2006-12-22 09:01:04 +08:00
|
|
|
struct timer_list poll_timer;
|
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 23:55:49 +08:00
|
|
|
unsigned long cmd_started; /* jiffies */
|
2009-02-03 14:06:18 +08:00
|
|
|
unsigned int cmd_busy:1;
|
2008-10-22 13:31:44 +08:00
|
|
|
unsigned int link_active_reporting:1;
|
2009-01-29 11:31:18 +08:00
|
|
|
unsigned int notification_enabled:1;
|
2009-02-03 14:06:16 +08:00
|
|
|
unsigned int power_fault_detected;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define INT_PRESENCE_ON 1
|
|
|
|
#define INT_PRESENCE_OFF 2
|
2015-07-02 06:17:49 +08:00
|
|
|
#define INT_POWER_FAULT 3
|
|
|
|
#define INT_BUTTON_PRESS 4
|
|
|
|
#define INT_LINK_UP 5
|
|
|
|
#define INT_LINK_DOWN 6
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define STATIC_STATE 0
|
|
|
|
#define BLINKINGON_STATE 1
|
|
|
|
#define BLINKINGOFF_STATE 2
|
|
|
|
#define POWERON_STATE 3
|
|
|
|
#define POWEROFF_STATE 4
|
|
|
|
|
2009-09-15 16:34:05 +08: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-06 05:05:07 +08:00
|
|
|
#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-04-13 02:02:59 +08:00
|
|
|
int pciehp_sysfs_enable_slot(struct slot *slot);
|
|
|
|
int pciehp_sysfs_disable_slot(struct slot *slot);
|
2015-06-15 10:35:13 +08:00
|
|
|
void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type);
|
2013-04-13 02:02:59 +08:00
|
|
|
int pciehp_configure_device(struct slot *p_slot);
|
2018-07-20 06:27:36 +08:00
|
|
|
void pciehp_unconfigure_device(struct slot *p_slot);
|
2013-04-13 02:02:59 +08:00
|
|
|
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
2008-06-20 11:07:08 +08:00
|
|
|
struct controller *pcie_init(struct pcie_device *dev);
|
2009-01-29 11:31:18 +08:00
|
|
|
int pcie_init_notification(struct controller *ctrl);
|
2018-07-20 06:27:32 +08:00
|
|
|
void pcie_shutdown_notification(struct controller *ctrl);
|
2007-11-29 07:11:28 +08:00
|
|
|
int pciehp_enable_slot(struct slot *p_slot);
|
2007-11-29 07:12:00 +08:00
|
|
|
int pciehp_disable_slot(struct slot *p_slot);
|
2018-05-24 06:14:39 +08:00
|
|
|
void pcie_reenable_notification(struct controller *ctrl);
|
2009-09-15 16:30:48 +08:00
|
|
|
int pciehp_power_on_slot(struct slot *slot);
|
2013-12-15 04:06:16 +08:00
|
|
|
void pciehp_power_off_slot(struct slot *slot);
|
|
|
|
void pciehp_get_power_status(struct slot *slot, u8 *status);
|
|
|
|
void pciehp_get_attention_status(struct slot *slot, u8 *status);
|
2009-09-15 16:30:48 +08:00
|
|
|
|
2013-12-15 04:06:16 +08:00
|
|
|
void pciehp_set_attention_status(struct slot *slot, u8 status);
|
|
|
|
void pciehp_get_latch_status(struct slot *slot, u8 *status);
|
|
|
|
void pciehp_get_adapter_status(struct slot *slot, u8 *status);
|
2009-09-15 16:30:48 +08:00
|
|
|
int pciehp_query_power_fault(struct slot *slot);
|
|
|
|
void pciehp_green_led_on(struct slot *slot);
|
|
|
|
void pciehp_green_led_off(struct slot *slot);
|
|
|
|
void pciehp_green_led_blink(struct slot *slot);
|
|
|
|
int pciehp_check_link_status(struct controller *ctrl);
|
2014-02-05 10:28:43 +08:00
|
|
|
bool pciehp_check_link_active(struct controller *ctrl);
|
2009-09-15 16:30:48 +08:00
|
|
|
void pciehp_release_ctrl(struct controller *ctrl);
|
2013-08-09 04:09:37 +08:00
|
|
|
int pciehp_reset_slot(struct slot *slot, int probe);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
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-14 00:31:59 +08: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);
|
|
|
|
|
2008-10-21 07:41:38 +08:00
|
|
|
static inline const char *slot_name(struct slot *slot)
|
|
|
|
{
|
|
|
|
return hotplug_slot_name(slot->hotplug_slot);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* _PCIEHP_H */
|