Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: pciehp: add message about pciehp_slot_with_bus option pci hotplug core: add check of duplicate slot name pciehp: move msleep after power off pciehp: poll cmd completion if hotplug interrupt is disabled pciehp: fix slow probing pciehp: fix NULL dereference in interrupt handler shpchp: add message about shpchp_slot_with_bus option PCI: don't enable ASPM on devices with mixed PCIe/PCI functions
This commit is contained in:
commit
1ec7d99c16
7 changed files with 159 additions and 54 deletions
|
@ -619,6 +619,7 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
|
|||
int pci_hp_register (struct hotplug_slot *slot)
|
||||
{
|
||||
int result;
|
||||
struct hotplug_slot *tmp;
|
||||
|
||||
if (slot == NULL)
|
||||
return -ENODEV;
|
||||
|
@ -630,7 +631,11 @@ int pci_hp_register (struct hotplug_slot *slot)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* this can fail if we have already registered a slot with the same name */
|
||||
/* Check if we have already registered a slot with the same name. */
|
||||
tmp = get_slot_from_name(slot->name);
|
||||
if (tmp)
|
||||
return -EEXIST;
|
||||
|
||||
slot->kobj.kset = pci_hotplug_slots_kset;
|
||||
result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL,
|
||||
"%s", slot->name);
|
||||
|
|
|
@ -97,6 +97,7 @@ struct controller {
|
|||
u8 cap_base;
|
||||
struct timer_list poll_timer;
|
||||
volatile int cmd_busy;
|
||||
unsigned int no_cmd_complete:1;
|
||||
};
|
||||
|
||||
#define INT_BUTTON_IGNORE 0
|
||||
|
@ -135,6 +136,7 @@ struct controller {
|
|||
#define PWR_LED_PRSN 0x00000010
|
||||
#define HP_SUPR_RM_SUP 0x00000020
|
||||
#define EMI_PRSN 0x00020000
|
||||
#define NO_CMD_CMPL_SUP 0x00040000
|
||||
|
||||
#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & ATTN_BUTTN_PRSN)
|
||||
#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PWR_CTRL_PRSN)
|
||||
|
@ -143,13 +145,14 @@ struct controller {
|
|||
#define PWR_LED(ctrl) ((ctrl)->slot_cap & PWR_LED_PRSN)
|
||||
#define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & HP_SUPR_RM_SUP)
|
||||
#define EMI(ctrl) ((ctrl)->slot_cap & EMI_PRSN)
|
||||
#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & NO_CMD_CMPL_SUP)
|
||||
|
||||
extern int pciehp_sysfs_enable_slot(struct slot *slot);
|
||||
extern int pciehp_sysfs_disable_slot(struct slot *slot);
|
||||
extern u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
|
||||
extern u8 pciehp_handle_attention_button(struct slot *p_slot);
|
||||
extern u8 pciehp_handle_switch_change(struct slot *p_slot);
|
||||
extern u8 pciehp_handle_presence_change(struct slot *p_slot);
|
||||
extern u8 pciehp_handle_power_fault(struct slot *p_slot);
|
||||
extern int pciehp_configure_device(struct slot *p_slot);
|
||||
extern int pciehp_unconfigure_device(struct slot *p_slot);
|
||||
extern void pciehp_queue_pushbutton_work(struct work_struct *work);
|
||||
|
|
|
@ -254,7 +254,11 @@ static int init_slots(struct controller *ctrl)
|
|||
slot->hp_slot, slot->number, ctrl->slot_device_offset);
|
||||
retval = pci_hp_register(hotplug_slot);
|
||||
if (retval) {
|
||||
err ("pci_hp_register failed with error %d\n", retval);
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
if (retval == -EEXIST)
|
||||
err("Failed to register slot because of name "
|
||||
"collision. Try \'pciehp_slot_with_bus\' "
|
||||
"module option.\n");
|
||||
goto error_info;
|
||||
}
|
||||
/* create additional sysfs entries */
|
||||
|
|
|
@ -55,16 +55,13 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
|
||||
u8 pciehp_handle_attention_button(struct slot *p_slot)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u32 event_type;
|
||||
|
||||
/* Attention Button Change */
|
||||
dbg("pciehp: Attention button interrupt received.\n");
|
||||
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
/*
|
||||
* Button pressed - See if need to TAKE ACTION!!!
|
||||
*/
|
||||
|
@ -76,18 +73,15 @@ u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
|
|||
return 0;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
|
||||
u8 pciehp_handle_switch_change(struct slot *p_slot)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u8 getstatus;
|
||||
u32 event_type;
|
||||
|
||||
/* Switch Change */
|
||||
dbg("pciehp: Switch interrupt received.\n");
|
||||
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
|
||||
|
||||
if (getstatus) {
|
||||
/*
|
||||
* Switch opened
|
||||
|
@ -107,17 +101,14 @@ u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
|
|||
return 1;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
|
||||
u8 pciehp_handle_presence_change(struct slot *p_slot)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u32 event_type;
|
||||
u8 presence_save;
|
||||
|
||||
/* Presence Change */
|
||||
dbg("pciehp: Presence/Notify input change.\n");
|
||||
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
/* Switch is open, assume a presence change
|
||||
* Save the presence state
|
||||
*/
|
||||
|
@ -141,16 +132,13 @@ u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
|
|||
return 1;
|
||||
}
|
||||
|
||||
u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
|
||||
u8 pciehp_handle_power_fault(struct slot *p_slot)
|
||||
{
|
||||
struct slot *p_slot;
|
||||
u32 event_type;
|
||||
|
||||
/* power fault */
|
||||
dbg("pciehp: Power fault interrupt received.\n");
|
||||
|
||||
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
|
||||
|
||||
if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
|
||||
/*
|
||||
* power fault Cleared
|
||||
|
@ -163,7 +151,7 @@ u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
|
|||
*/
|
||||
info("Power fault on Slot(%s)\n", p_slot->name);
|
||||
event_type = INT_POWER_FAULT;
|
||||
info("power fault bit %x set\n", hp_slot);
|
||||
info("power fault bit %x set\n", 0);
|
||||
}
|
||||
|
||||
queue_interrupt_event(p_slot, event_type);
|
||||
|
@ -186,6 +174,13 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* After turning power off, we must wait for at least 1 second
|
||||
* before taking any action that relies on power having been
|
||||
* removed from the slot/adapter.
|
||||
*/
|
||||
msleep(1000);
|
||||
|
||||
if (PWR_LED(ctrl))
|
||||
pslot->hpc_ops->green_led_off(pslot);
|
||||
|
||||
|
@ -289,6 +284,13 @@ static int remove_board(struct slot *p_slot)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* After turning power off, we must wait for at least 1 second
|
||||
* before taking any action that relies on power having been
|
||||
* removed from the slot/adapter.
|
||||
*/
|
||||
msleep(1000);
|
||||
|
||||
if (PWR_LED(ctrl))
|
||||
/* turn off Green LED */
|
||||
p_slot->hpc_ops->green_led_off(p_slot);
|
||||
|
|
|
@ -247,14 +247,38 @@ static inline void pciehp_free_irq(struct controller *ctrl)
|
|||
free_irq(ctrl->pci_dev->irq, ctrl);
|
||||
}
|
||||
|
||||
static inline int pcie_wait_cmd(struct controller *ctrl)
|
||||
static inline int pcie_poll_cmd(struct controller *ctrl)
|
||||
{
|
||||
u16 slot_status;
|
||||
int timeout = 1000;
|
||||
|
||||
if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status))
|
||||
if (slot_status & CMD_COMPLETED)
|
||||
goto completed;
|
||||
for (timeout = 1000; timeout > 0; timeout -= 100) {
|
||||
msleep(100);
|
||||
if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status))
|
||||
if (slot_status & CMD_COMPLETED)
|
||||
goto completed;
|
||||
}
|
||||
return 0; /* timeout */
|
||||
|
||||
completed:
|
||||
pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
|
||||
{
|
||||
int retval = 0;
|
||||
unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
|
||||
unsigned long timeout = msecs_to_jiffies(msecs);
|
||||
int rc;
|
||||
|
||||
rc = wait_event_interruptible_timeout(ctrl->queue,
|
||||
if (poll)
|
||||
rc = pcie_poll_cmd(ctrl);
|
||||
else
|
||||
rc = wait_event_interruptible_timeout(ctrl->queue,
|
||||
!ctrl->cmd_busy, timeout);
|
||||
if (!rc)
|
||||
dbg("Command not completed in 1000 msec\n");
|
||||
|
@ -286,12 +310,28 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) {
|
||||
/* After 1 sec and CMD_COMPLETED still not set, just
|
||||
proceed forward to issue the next command according
|
||||
to spec. Just print out the error message */
|
||||
dbg("%s: CMD_COMPLETED not clear after 1 sec.\n",
|
||||
__func__);
|
||||
if (slot_status & CMD_COMPLETED) {
|
||||
if (!ctrl->no_cmd_complete) {
|
||||
/*
|
||||
* After 1 sec and CMD_COMPLETED still not set, just
|
||||
* proceed forward to issue the next command according
|
||||
* to spec. Just print out the error message.
|
||||
*/
|
||||
dbg("%s: CMD_COMPLETED not clear after 1 sec.\n",
|
||||
__func__);
|
||||
} else if (!NO_CMD_CMPL(ctrl)) {
|
||||
/*
|
||||
* This controller semms to notify of command completed
|
||||
* event even though it supports none of power
|
||||
* controller, attention led, power led and EMI.
|
||||
*/
|
||||
dbg("%s: Unexpected CMD_COMPLETED. Need to wait for "
|
||||
"command completed event.\n", __func__);
|
||||
ctrl->no_cmd_complete = 0;
|
||||
} else {
|
||||
dbg("%s: Unexpected CMD_COMPLETED. Maybe the "
|
||||
"controller is broken.\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
|
||||
|
@ -315,8 +355,18 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
|
|||
/*
|
||||
* Wait for command completion.
|
||||
*/
|
||||
if (!retval)
|
||||
retval = pcie_wait_cmd(ctrl);
|
||||
if (!retval && !ctrl->no_cmd_complete) {
|
||||
int poll = 0;
|
||||
/*
|
||||
* if hotplug interrupt is not enabled or command
|
||||
* completed interrupt is not enabled, we need to poll
|
||||
* command completed event.
|
||||
*/
|
||||
if (!(slot_ctrl & HP_INTR_ENABLE) ||
|
||||
!(slot_ctrl & CMD_CMPL_INTR_ENABLE))
|
||||
poll = 1;
|
||||
retval = pcie_wait_cmd(ctrl, poll);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ctrl->ctrl_lock);
|
||||
return retval;
|
||||
|
@ -704,13 +754,6 @@ static int hpc_power_off_slot(struct slot * slot)
|
|||
}
|
||||
dbg("%s: SLOTCTRL %x write cmd %x\n",
|
||||
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
|
||||
|
||||
/*
|
||||
* After turning power off, we must wait for at least 1 second
|
||||
* before taking any action that relies on power having been
|
||||
* removed from the slot/adapter.
|
||||
*/
|
||||
msleep(1000);
|
||||
out:
|
||||
if (changed)
|
||||
pcie_unmask_bad_dllp(ctrl);
|
||||
|
@ -722,6 +765,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
|||
{
|
||||
struct controller *ctrl = (struct controller *)dev_id;
|
||||
u16 detected, intr_loc;
|
||||
struct slot *p_slot;
|
||||
|
||||
/*
|
||||
* In order to guarantee that all interrupt events are
|
||||
|
@ -756,21 +800,38 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
|||
wake_up_interruptible(&ctrl->queue);
|
||||
}
|
||||
|
||||
if (!(intr_loc & ~CMD_COMPLETED))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
* Return without handling events if this handler routine is
|
||||
* called before controller initialization is done. This may
|
||||
* happen if hotplug event or another interrupt that shares
|
||||
* the IRQ with pciehp arrives before slot initialization is
|
||||
* done after interrupt handler is registered.
|
||||
*
|
||||
* FIXME - Need more structural fixes. We need to be ready to
|
||||
* handle the event before installing interrupt handler.
|
||||
*/
|
||||
p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
|
||||
if (!p_slot || !p_slot->hpc_ops)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* Check MRL Sensor Changed */
|
||||
if (intr_loc & MRL_SENS_CHANGED)
|
||||
pciehp_handle_switch_change(0, ctrl);
|
||||
pciehp_handle_switch_change(p_slot);
|
||||
|
||||
/* Check Attention Button Pressed */
|
||||
if (intr_loc & ATTN_BUTTN_PRESSED)
|
||||
pciehp_handle_attention_button(0, ctrl);
|
||||
pciehp_handle_attention_button(p_slot);
|
||||
|
||||
/* Check Presence Detect Changed */
|
||||
if (intr_loc & PRSN_DETECT_CHANGED)
|
||||
pciehp_handle_presence_change(0, ctrl);
|
||||
pciehp_handle_presence_change(p_slot);
|
||||
|
||||
/* Check Power Fault Detected */
|
||||
if (intr_loc & PWR_FAULT_DETECTED)
|
||||
pciehp_handle_power_fault(0, ctrl);
|
||||
pciehp_handle_power_fault(p_slot);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -1028,6 +1089,12 @@ static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
|
|||
static int pcie_init_hardware_part1(struct controller *ctrl,
|
||||
struct pcie_device *dev)
|
||||
{
|
||||
/* Clear all remaining event bits in Slot Status register */
|
||||
if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) {
|
||||
err("%s: Cannot write to SLOTSTATUS register\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Mask Hot-plug Interrupt Enable */
|
||||
if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) {
|
||||
err("%s: Cannot mask hotplug interrupt enable\n", __func__);
|
||||
|
@ -1040,16 +1107,6 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
|
|||
{
|
||||
u16 cmd, mask;
|
||||
|
||||
/*
|
||||
* We need to clear all events before enabling hotplug interrupt
|
||||
* notification mechanism in order for hotplug controler to
|
||||
* generate interrupts.
|
||||
*/
|
||||
if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) {
|
||||
err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd = PRSN_DETECT_ENABLE;
|
||||
if (ATTN_BUTTN(ctrl))
|
||||
cmd |= ATTN_BUTTN_ENABLE;
|
||||
|
@ -1116,6 +1173,7 @@ static inline void dbg_ctrl(struct controller *ctrl)
|
|||
dbg(" Power Indicator : %3s\n", PWR_LED(ctrl) ? "yes" : "no");
|
||||
dbg(" Hot-Plug Surprise : %3s\n", HP_SUPR_RM(ctrl) ? "yes" : "no");
|
||||
dbg(" EMI Present : %3s\n", EMI(ctrl) ? "yes" : "no");
|
||||
dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes");
|
||||
pciehp_readw(ctrl, SLOTSTATUS, ®16);
|
||||
dbg("Slot Status : 0x%04x\n", reg16);
|
||||
pciehp_readw(ctrl, SLOTSTATUS, ®16);
|
||||
|
@ -1147,6 +1205,15 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
|
|||
mutex_init(&ctrl->ctrl_lock);
|
||||
init_waitqueue_head(&ctrl->queue);
|
||||
dbg_ctrl(ctrl);
|
||||
/*
|
||||
* Controller doesn't notify of command completion if the "No
|
||||
* Command Completed Support" bit is set in Slot Capability
|
||||
* register or the controller supports none of power
|
||||
* controller, attention led, power led and EMI.
|
||||
*/
|
||||
if (NO_CMD_CMPL(ctrl) ||
|
||||
!(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl)))
|
||||
ctrl->no_cmd_complete = 1;
|
||||
|
||||
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
|
||||
pdev->vendor, pdev->device,
|
||||
|
|
|
@ -162,6 +162,10 @@ static int init_slots(struct controller *ctrl)
|
|||
retval = pci_hp_register(slot->hotplug_slot);
|
||||
if (retval) {
|
||||
err("pci_hp_register failed with error %d\n", retval);
|
||||
if (retval == -EEXIST)
|
||||
err("Failed to register slot because of name "
|
||||
"collision. Try \'shpchp_slot_with_bus\' "
|
||||
"module option.\n");
|
||||
goto error_info;
|
||||
}
|
||||
|
||||
|
|
|
@ -506,6 +506,23 @@ static void free_link_state(struct pci_dev *pdev)
|
|||
pdev->link_state = NULL;
|
||||
}
|
||||
|
||||
static int pcie_aspm_sanity_check(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *child_dev;
|
||||
int child_pos;
|
||||
|
||||
/*
|
||||
* Some functions in a slot might not all be PCIE functions, very
|
||||
* strange. Disable ASPM for the whole slot
|
||||
*/
|
||||
list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
|
||||
child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP);
|
||||
if (!child_pos)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pcie_aspm_init_link_state: Initiate PCI express link state.
|
||||
* It is called after the pcie and its children devices are scaned.
|
||||
|
@ -526,6 +543,9 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
|
|||
if (list_empty(&pdev->subordinate->devices))
|
||||
goto out;
|
||||
|
||||
if (pcie_aspm_sanity_check(pdev))
|
||||
goto out;
|
||||
|
||||
mutex_lock(&aspm_lock);
|
||||
|
||||
link_state = kzalloc(sizeof(*link_state), GFP_KERNEL);
|
||||
|
|
Loading…
Reference in a new issue