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:
Linus Torvalds 2008-05-27 18:47:59 -07:00
commit 1ec7d99c16
7 changed files with 159 additions and 54 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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 */

View file

@ -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);

View file

@ -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, &reg16);
dbg("Slot Status : 0x%04x\n", reg16);
pciehp_readw(ctrl, SLOTSTATUS, &reg16);
@ -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,

View file

@ -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;
}

View file

@ -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);