aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeith Busch <keith.busch@intel.com>2016-09-13 12:31:59 -0400
committerBjorn Helgaas <bhelgaas@google.com>2016-09-22 19:20:11 -0400
commit576243b3f9eaa47ab568ac49574b3a095c2365f1 (patch)
treebb0eec720e02b97afa0a493c5c56088939875ea5
parent29a654e59f3698d70d85e289fc5ce7261493bba2 (diff)
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>
-rw-r--r--drivers/pci/hotplug/pciehp.h3
-rw-r--r--drivers/pci/hotplug/pciehp_core.c3
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c27
-rw-r--r--include/linux/pci.h3
4 files changed, 36 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index e764918641ae..37d70b5ad22f 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -152,6 +152,9 @@ bool pciehp_check_link_active(struct controller *ctrl);
152void pciehp_release_ctrl(struct controller *ctrl); 152void pciehp_release_ctrl(struct controller *ctrl);
153int pciehp_reset_slot(struct slot *slot, int probe); 153int pciehp_reset_slot(struct slot *slot, int probe);
154 154
155int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
156int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);
157
155static inline const char *slot_name(struct slot *slot) 158static inline const char *slot_name(struct slot *slot)
156{ 159{
157 return hotplug_slot_name(slot->hotplug_slot); 160 return hotplug_slot_name(slot->hotplug_slot);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index ac531e674a05..276e39c64d06 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -114,6 +114,9 @@ static int init_slot(struct controller *ctrl)
114 if (ATTN_LED(ctrl)) { 114 if (ATTN_LED(ctrl)) {
115 ops->get_attention_status = get_attention_status; 115 ops->get_attention_status = get_attention_status;
116 ops->set_attention_status = set_attention_status; 116 ops->set_attention_status = set_attention_status;
117 } else if (ctrl->pcie->port->hotplug_user_indicators) {
118 ops->get_attention_status = pciehp_get_raw_indicator_status;
119 ops->set_attention_status = pciehp_set_raw_indicator_status;
117 } 120 }
118 121
119 /* register this slot with the hotplug pci core */ 122 /* register this slot with the hotplug pci core */
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 4582fdf2d8b5..b57fc6d6e28a 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -355,6 +355,18 @@ static int pciehp_link_enable(struct controller *ctrl)
355 return __pciehp_link_set(ctrl, true); 355 return __pciehp_link_set(ctrl, true);
356} 356}
357 357
358int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
359 u8 *status)
360{
361 struct slot *slot = hotplug_slot->private;
362 struct pci_dev *pdev = ctrl_dev(slot->ctrl);
363 u16 slot_ctrl;
364
365 pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
366 *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
367 return 0;
368}
369
358void pciehp_get_attention_status(struct slot *slot, u8 *status) 370void pciehp_get_attention_status(struct slot *slot, u8 *status)
359{ 371{
360 struct controller *ctrl = slot->ctrl; 372 struct controller *ctrl = slot->ctrl;
@@ -431,6 +443,17 @@ int pciehp_query_power_fault(struct slot *slot)
431 return !!(slot_status & PCI_EXP_SLTSTA_PFD); 443 return !!(slot_status & PCI_EXP_SLTSTA_PFD);
432} 444}
433 445
446int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
447 u8 status)
448{
449 struct slot *slot = hotplug_slot->private;
450 struct controller *ctrl = slot->ctrl;
451
452 pcie_write_cmd_nowait(ctrl, status << 6,
453 PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
454 return 0;
455}
456
434void pciehp_set_attention_status(struct slot *slot, u8 value) 457void pciehp_set_attention_status(struct slot *slot, u8 value)
435{ 458{
436 struct controller *ctrl = slot->ctrl; 459 struct controller *ctrl = slot->ctrl;
@@ -814,6 +837,10 @@ struct controller *pcie_init(struct pcie_device *dev)
814 } 837 }
815 ctrl->pcie = dev; 838 ctrl->pcie = dev;
816 pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); 839 pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
840
841 if (pdev->hotplug_user_indicators)
842 slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP);
843
817 ctrl->slot_cap = slot_cap; 844 ctrl->slot_cap = slot_cap;
818 mutex_init(&ctrl->ctrl_lock); 845 mutex_init(&ctrl->ctrl_lock);
819 init_waitqueue_head(&ctrl->queue); 846 init_waitqueue_head(&ctrl->queue);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2599a980340f..c81fbf7d5e9e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -308,6 +308,9 @@ struct pci_dev {
308 powered on/off by the 308 powered on/off by the
309 corresponding bridge */ 309 corresponding bridge */
310 unsigned int ignore_hotplug:1; /* Ignore hotplug events */ 310 unsigned int ignore_hotplug:1; /* Ignore hotplug events */
311 unsigned int hotplug_user_indicators:1; /* SlotCtl indicators
312 controlled exclusively by
313 user sysfs */
311 unsigned int d3_delay; /* D3->D0 transition time in ms */ 314 unsigned int d3_delay; /* D3->D0 transition time in ms */
312 unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ 315 unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */
313 316