Commit 576243b3 authored by Keith Busch's avatar Keith Busch Committed by Bjorn Helgaas

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: default avatarKeith Busch <keith.busch@intel.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 29a654e5
...@@ -152,6 +152,9 @@ bool pciehp_check_link_active(struct controller *ctrl); ...@@ -152,6 +152,9 @@ bool pciehp_check_link_active(struct controller *ctrl);
void pciehp_release_ctrl(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl);
int pciehp_reset_slot(struct slot *slot, int probe); int pciehp_reset_slot(struct slot *slot, int probe);
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);
static inline const char *slot_name(struct slot *slot) static inline const char *slot_name(struct slot *slot)
{ {
return hotplug_slot_name(slot->hotplug_slot); return hotplug_slot_name(slot->hotplug_slot);
......
...@@ -114,6 +114,9 @@ static int init_slot(struct controller *ctrl) ...@@ -114,6 +114,9 @@ static int init_slot(struct controller *ctrl)
if (ATTN_LED(ctrl)) { if (ATTN_LED(ctrl)) {
ops->get_attention_status = get_attention_status; ops->get_attention_status = get_attention_status;
ops->set_attention_status = set_attention_status; ops->set_attention_status = set_attention_status;
} else if (ctrl->pcie->port->hotplug_user_indicators) {
ops->get_attention_status = pciehp_get_raw_indicator_status;
ops->set_attention_status = pciehp_set_raw_indicator_status;
} }
/* register this slot with the hotplug pci core */ /* register this slot with the hotplug pci core */
......
...@@ -355,6 +355,18 @@ static int pciehp_link_enable(struct controller *ctrl) ...@@ -355,6 +355,18 @@ static int pciehp_link_enable(struct controller *ctrl)
return __pciehp_link_set(ctrl, true); return __pciehp_link_set(ctrl, true);
} }
int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
u8 *status)
{
struct slot *slot = hotplug_slot->private;
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
u16 slot_ctrl;
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
*status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
return 0;
}
void pciehp_get_attention_status(struct slot *slot, u8 *status) void pciehp_get_attention_status(struct slot *slot, u8 *status)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
...@@ -431,6 +443,17 @@ int pciehp_query_power_fault(struct slot *slot) ...@@ -431,6 +443,17 @@ int pciehp_query_power_fault(struct slot *slot)
return !!(slot_status & PCI_EXP_SLTSTA_PFD); return !!(slot_status & PCI_EXP_SLTSTA_PFD);
} }
int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
u8 status)
{
struct slot *slot = hotplug_slot->private;
struct controller *ctrl = slot->ctrl;
pcie_write_cmd_nowait(ctrl, status << 6,
PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
return 0;
}
void pciehp_set_attention_status(struct slot *slot, u8 value) void pciehp_set_attention_status(struct slot *slot, u8 value)
{ {
struct controller *ctrl = slot->ctrl; struct controller *ctrl = slot->ctrl;
...@@ -814,6 +837,10 @@ struct controller *pcie_init(struct pcie_device *dev) ...@@ -814,6 +837,10 @@ struct controller *pcie_init(struct pcie_device *dev)
} }
ctrl->pcie = dev; ctrl->pcie = dev;
pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
if (pdev->hotplug_user_indicators)
slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP);
ctrl->slot_cap = slot_cap; ctrl->slot_cap = slot_cap;
mutex_init(&ctrl->ctrl_lock); mutex_init(&ctrl->ctrl_lock);
init_waitqueue_head(&ctrl->queue); init_waitqueue_head(&ctrl->queue);
......
...@@ -308,6 +308,9 @@ struct pci_dev { ...@@ -308,6 +308,9 @@ struct pci_dev {
powered on/off by the powered on/off by the
corresponding bridge */ corresponding bridge */
unsigned int ignore_hotplug:1; /* Ignore hotplug events */ unsigned int ignore_hotplug:1; /* Ignore hotplug events */
unsigned int hotplug_user_indicators:1; /* SlotCtl indicators
controlled exclusively by
user sysfs */
unsigned int d3_delay; /* D3->D0 transition time in ms */ unsigned int d3_delay; /* D3->D0 transition time in ms */
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */
......
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