mirror of https://gitee.com/openkylin/linux.git
Merge branch 'pci/pciehp' into next
* pci/pciehp: PCI: pciehp: Cleanup whitespace PCI: pciehp: Remove a non-existent card, regardless of "surprise" capability PCI: pciehp: Don't turn slot off when hot-added device already exists PCI: pciehp: Add hotplug_lock to serialize hotplug events PCI: pciehp: Ensure very fast hotplug events are also processed PCI: pciehp: Disable link notification across slot reset PCI: pciehp: Don't check adapter or latch status while disabling PCI: pciehp: Don't disable the link permanently during removal PCI: pciehp: Enable link state change notifications PCI: pciehp: Use link change notifications for hot-plug and removal PCI: pciehp: Make check_link_active() non-static
This commit is contained in:
commit
9d68c783d7
|
@ -76,6 +76,7 @@ struct slot {
|
||||||
struct hotplug_slot *hotplug_slot;
|
struct hotplug_slot *hotplug_slot;
|
||||||
struct delayed_work work; /* work for button event */
|
struct delayed_work work; /* work for button event */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
struct mutex hotplug_lock;
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,6 +110,8 @@ struct controller {
|
||||||
#define INT_BUTTON_PRESS 7
|
#define INT_BUTTON_PRESS 7
|
||||||
#define INT_BUTTON_RELEASE 8
|
#define INT_BUTTON_RELEASE 8
|
||||||
#define INT_BUTTON_CANCEL 9
|
#define INT_BUTTON_CANCEL 9
|
||||||
|
#define INT_LINK_UP 10
|
||||||
|
#define INT_LINK_DOWN 11
|
||||||
|
|
||||||
#define STATIC_STATE 0
|
#define STATIC_STATE 0
|
||||||
#define BLINKINGON_STATE 1
|
#define BLINKINGON_STATE 1
|
||||||
|
@ -132,6 +135,7 @@ u8 pciehp_handle_attention_button(struct slot *p_slot);
|
||||||
u8 pciehp_handle_switch_change(struct slot *p_slot);
|
u8 pciehp_handle_switch_change(struct slot *p_slot);
|
||||||
u8 pciehp_handle_presence_change(struct slot *p_slot);
|
u8 pciehp_handle_presence_change(struct slot *p_slot);
|
||||||
u8 pciehp_handle_power_fault(struct slot *p_slot);
|
u8 pciehp_handle_power_fault(struct slot *p_slot);
|
||||||
|
void pciehp_handle_linkstate_change(struct slot *p_slot);
|
||||||
int pciehp_configure_device(struct slot *p_slot);
|
int pciehp_configure_device(struct slot *p_slot);
|
||||||
int pciehp_unconfigure_device(struct slot *p_slot);
|
int pciehp_unconfigure_device(struct slot *p_slot);
|
||||||
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
void pciehp_queue_pushbutton_work(struct work_struct *work);
|
||||||
|
@ -153,6 +157,7 @@ void pciehp_green_led_on(struct slot *slot);
|
||||||
void pciehp_green_led_off(struct slot *slot);
|
void pciehp_green_led_off(struct slot *slot);
|
||||||
void pciehp_green_led_blink(struct slot *slot);
|
void pciehp_green_led_blink(struct slot *slot);
|
||||||
int pciehp_check_link_status(struct controller *ctrl);
|
int pciehp_check_link_status(struct controller *ctrl);
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ static struct pcie_port_service_driver __initdata dummy_driver = {
|
||||||
static int __init select_detection_mode(void)
|
static int __init select_detection_mode(void)
|
||||||
{
|
{
|
||||||
struct dummy_slot *slot, *tmp;
|
struct dummy_slot *slot, *tmp;
|
||||||
|
|
||||||
if (pcie_port_service_register(&dummy_driver))
|
if (pcie_port_service_register(&dummy_driver))
|
||||||
return PCIEHP_DETECT_ACPI;
|
return PCIEHP_DETECT_ACPI;
|
||||||
pcie_port_service_unregister(&dummy_driver);
|
pcie_port_service_unregister(&dummy_driver);
|
||||||
|
|
|
@ -108,6 +108,7 @@ static int init_slot(struct controller *ctrl)
|
||||||
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
|
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
|
||||||
if (!ops)
|
if (!ops)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ops->enable_slot = enable_slot;
|
ops->enable_slot = enable_slot;
|
||||||
ops->disable_slot = disable_slot;
|
ops->disable_slot = disable_slot;
|
||||||
ops->get_power_status = get_power_status;
|
ops->get_power_status = get_power_status;
|
||||||
|
@ -283,8 +284,11 @@ static int pciehp_probe(struct pcie_device *dev)
|
||||||
slot = ctrl->slot;
|
slot = ctrl->slot;
|
||||||
pciehp_get_adapter_status(slot, &occupied);
|
pciehp_get_adapter_status(slot, &occupied);
|
||||||
pciehp_get_power_status(slot, &poweron);
|
pciehp_get_power_status(slot, &poweron);
|
||||||
if (occupied && pciehp_force)
|
if (occupied && pciehp_force) {
|
||||||
|
mutex_lock(&slot->hotplug_lock);
|
||||||
pciehp_enable_slot(slot);
|
pciehp_enable_slot(slot);
|
||||||
|
mutex_unlock(&slot->hotplug_lock);
|
||||||
|
}
|
||||||
/* If empty slot's power status is on, turn power off */
|
/* If empty slot's power status is on, turn power off */
|
||||||
if (!occupied && poweron && POWER_CTRL(ctrl))
|
if (!occupied && poweron && POWER_CTRL(ctrl))
|
||||||
pciehp_power_off_slot(slot);
|
pciehp_power_off_slot(slot);
|
||||||
|
@ -328,10 +332,12 @@ static int pciehp_resume (struct pcie_device *dev)
|
||||||
|
|
||||||
/* Check if slot is occupied */
|
/* Check if slot is occupied */
|
||||||
pciehp_get_adapter_status(slot, &status);
|
pciehp_get_adapter_status(slot, &status);
|
||||||
|
mutex_lock(&slot->hotplug_lock);
|
||||||
if (status)
|
if (status)
|
||||||
pciehp_enable_slot(slot);
|
pciehp_enable_slot(slot);
|
||||||
else
|
else
|
||||||
pciehp_disable_slot(slot);
|
pciehp_disable_slot(slot);
|
||||||
|
mutex_unlock(&slot->hotplug_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* PM */
|
#endif /* PM */
|
||||||
|
|
|
@ -150,6 +150,27 @@ u8 pciehp_handle_power_fault(struct slot *p_slot)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pciehp_handle_linkstate_change(struct slot *p_slot)
|
||||||
|
{
|
||||||
|
u32 event_type;
|
||||||
|
struct controller *ctrl = p_slot->ctrl;
|
||||||
|
|
||||||
|
/* Link Status Change */
|
||||||
|
ctrl_dbg(ctrl, "Data Link Layer State change\n");
|
||||||
|
|
||||||
|
if (pciehp_check_link_active(ctrl)) {
|
||||||
|
ctrl_info(ctrl, "slot(%s): Link Up event\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
event_type = INT_LINK_UP;
|
||||||
|
} else {
|
||||||
|
ctrl_info(ctrl, "slot(%s): Link Down event\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
event_type = INT_LINK_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_interrupt_event(p_slot, event_type);
|
||||||
|
}
|
||||||
|
|
||||||
/* The following routines constitute the bulk of the
|
/* The following routines constitute the bulk of the
|
||||||
hotplug controller logic
|
hotplug controller logic
|
||||||
*/
|
*/
|
||||||
|
@ -212,7 +233,8 @@ static int board_added(struct slot *p_slot)
|
||||||
if (retval) {
|
if (retval) {
|
||||||
ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
|
ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
|
||||||
pci_domain_nr(parent), parent->number);
|
pci_domain_nr(parent), parent->number);
|
||||||
goto err_exit;
|
if (retval != -EEXIST)
|
||||||
|
goto err_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pciehp_green_led_on(p_slot);
|
pciehp_green_led_on(p_slot);
|
||||||
|
@ -255,6 +277,9 @@ static int remove_board(struct slot *p_slot)
|
||||||
struct power_work_info {
|
struct power_work_info {
|
||||||
struct slot *p_slot;
|
struct slot *p_slot;
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
|
unsigned int req;
|
||||||
|
#define DISABLE_REQ 0
|
||||||
|
#define ENABLE_REQ 1
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,30 +294,38 @@ static void pciehp_power_thread(struct work_struct *work)
|
||||||
struct power_work_info *info =
|
struct power_work_info *info =
|
||||||
container_of(work, struct power_work_info, work);
|
container_of(work, struct power_work_info, work);
|
||||||
struct slot *p_slot = info->p_slot;
|
struct slot *p_slot = info->p_slot;
|
||||||
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&p_slot->lock);
|
switch (info->req) {
|
||||||
switch (p_slot->state) {
|
case DISABLE_REQ:
|
||||||
case POWEROFF_STATE:
|
|
||||||
mutex_unlock(&p_slot->lock);
|
|
||||||
ctrl_dbg(p_slot->ctrl,
|
ctrl_dbg(p_slot->ctrl,
|
||||||
"Disabling domain:bus:device=%04x:%02x:00\n",
|
"Disabling domain:bus:device=%04x:%02x:00\n",
|
||||||
pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
|
pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
|
||||||
p_slot->ctrl->pcie->port->subordinate->number);
|
p_slot->ctrl->pcie->port->subordinate->number);
|
||||||
|
mutex_lock(&p_slot->hotplug_lock);
|
||||||
pciehp_disable_slot(p_slot);
|
pciehp_disable_slot(p_slot);
|
||||||
|
mutex_unlock(&p_slot->hotplug_lock);
|
||||||
mutex_lock(&p_slot->lock);
|
mutex_lock(&p_slot->lock);
|
||||||
p_slot->state = STATIC_STATE;
|
p_slot->state = STATIC_STATE;
|
||||||
break;
|
|
||||||
case POWERON_STATE:
|
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&p_slot->lock);
|
||||||
if (pciehp_enable_slot(p_slot))
|
break;
|
||||||
|
case ENABLE_REQ:
|
||||||
|
ctrl_dbg(p_slot->ctrl,
|
||||||
|
"Enabling domain:bus:device=%04x:%02x:00\n",
|
||||||
|
pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
|
||||||
|
p_slot->ctrl->pcie->port->subordinate->number);
|
||||||
|
mutex_lock(&p_slot->hotplug_lock);
|
||||||
|
ret = pciehp_enable_slot(p_slot);
|
||||||
|
mutex_unlock(&p_slot->hotplug_lock);
|
||||||
|
if (ret)
|
||||||
pciehp_green_led_off(p_slot);
|
pciehp_green_led_off(p_slot);
|
||||||
mutex_lock(&p_slot->lock);
|
mutex_lock(&p_slot->lock);
|
||||||
p_slot->state = STATIC_STATE;
|
p_slot->state = STATIC_STATE;
|
||||||
|
mutex_unlock(&p_slot->lock);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&p_slot->lock);
|
|
||||||
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
}
|
}
|
||||||
|
@ -315,9 +348,11 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
|
||||||
switch (p_slot->state) {
|
switch (p_slot->state) {
|
||||||
case BLINKINGOFF_STATE:
|
case BLINKINGOFF_STATE:
|
||||||
p_slot->state = POWEROFF_STATE;
|
p_slot->state = POWEROFF_STATE;
|
||||||
|
info->req = DISABLE_REQ;
|
||||||
break;
|
break;
|
||||||
case BLINKINGON_STATE:
|
case BLINKINGON_STATE:
|
||||||
p_slot->state = POWERON_STATE;
|
p_slot->state = POWERON_STATE;
|
||||||
|
info->req = ENABLE_REQ;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
kfree(info);
|
kfree(info);
|
||||||
|
@ -364,11 +399,10 @@ static void handle_button_press_event(struct slot *p_slot)
|
||||||
*/
|
*/
|
||||||
ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
|
ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
|
||||||
cancel_delayed_work(&p_slot->work);
|
cancel_delayed_work(&p_slot->work);
|
||||||
if (p_slot->state == BLINKINGOFF_STATE) {
|
if (p_slot->state == BLINKINGOFF_STATE)
|
||||||
pciehp_green_led_on(p_slot);
|
pciehp_green_led_on(p_slot);
|
||||||
} else {
|
else
|
||||||
pciehp_green_led_off(p_slot);
|
pciehp_green_led_off(p_slot);
|
||||||
}
|
|
||||||
pciehp_set_attention_status(p_slot, 0);
|
pciehp_set_attention_status(p_slot, 0);
|
||||||
ctrl_info(ctrl, "PCI slot #%s - action canceled "
|
ctrl_info(ctrl, "PCI slot #%s - action canceled "
|
||||||
"due to button press\n", slot_name(p_slot));
|
"due to button press\n", slot_name(p_slot));
|
||||||
|
@ -407,14 +441,81 @@ static void handle_surprise_event(struct slot *p_slot)
|
||||||
INIT_WORK(&info->work, pciehp_power_thread);
|
INIT_WORK(&info->work, pciehp_power_thread);
|
||||||
|
|
||||||
pciehp_get_adapter_status(p_slot, &getstatus);
|
pciehp_get_adapter_status(p_slot, &getstatus);
|
||||||
if (!getstatus)
|
if (!getstatus) {
|
||||||
p_slot->state = POWEROFF_STATE;
|
p_slot->state = POWEROFF_STATE;
|
||||||
else
|
info->req = DISABLE_REQ;
|
||||||
|
} else {
|
||||||
p_slot->state = POWERON_STATE;
|
p_slot->state = POWERON_STATE;
|
||||||
|
info->req = ENABLE_REQ;
|
||||||
|
}
|
||||||
|
|
||||||
queue_work(p_slot->wq, &info->work);
|
queue_work(p_slot->wq, &info->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: This function must be called with slot->lock held
|
||||||
|
*/
|
||||||
|
static void handle_link_event(struct slot *p_slot, u32 event)
|
||||||
|
{
|
||||||
|
struct controller *ctrl = p_slot->ctrl;
|
||||||
|
struct power_work_info *info;
|
||||||
|
|
||||||
|
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||||
|
if (!info) {
|
||||||
|
ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
|
||||||
|
__func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info->p_slot = p_slot;
|
||||||
|
info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ;
|
||||||
|
INIT_WORK(&info->work, pciehp_power_thread);
|
||||||
|
|
||||||
|
switch (p_slot->state) {
|
||||||
|
case BLINKINGON_STATE:
|
||||||
|
case BLINKINGOFF_STATE:
|
||||||
|
cancel_delayed_work(&p_slot->work);
|
||||||
|
/* Fall through */
|
||||||
|
case STATIC_STATE:
|
||||||
|
p_slot->state = event == INT_LINK_UP ?
|
||||||
|
POWERON_STATE : POWEROFF_STATE;
|
||||||
|
queue_work(p_slot->wq, &info->work);
|
||||||
|
break;
|
||||||
|
case POWERON_STATE:
|
||||||
|
if (event == INT_LINK_UP) {
|
||||||
|
ctrl_info(ctrl,
|
||||||
|
"Link Up event ignored on slot(%s): already powering on\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
kfree(info);
|
||||||
|
} else {
|
||||||
|
ctrl_info(ctrl,
|
||||||
|
"Link Down event queued on slot(%s): currently getting powered on\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
p_slot->state = POWEROFF_STATE;
|
||||||
|
queue_work(p_slot->wq, &info->work);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case POWEROFF_STATE:
|
||||||
|
if (event == INT_LINK_UP) {
|
||||||
|
ctrl_info(ctrl,
|
||||||
|
"Link Up event queued on slot(%s): currently getting powered off\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
p_slot->state = POWERON_STATE;
|
||||||
|
queue_work(p_slot->wq, &info->work);
|
||||||
|
} else {
|
||||||
|
ctrl_info(ctrl,
|
||||||
|
"Link Down event ignored on slot(%s): already powering off\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
kfree(info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctrl_err(ctrl, "Not a valid state on slot(%s)\n",
|
||||||
|
slot_name(p_slot));
|
||||||
|
kfree(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void interrupt_event_handler(struct work_struct *work)
|
static void interrupt_event_handler(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct event_info *info = container_of(work, struct event_info, work);
|
struct event_info *info = container_of(work, struct event_info, work);
|
||||||
|
@ -433,12 +534,23 @@ static void interrupt_event_handler(struct work_struct *work)
|
||||||
pciehp_green_led_off(p_slot);
|
pciehp_green_led_off(p_slot);
|
||||||
break;
|
break;
|
||||||
case INT_PRESENCE_ON:
|
case INT_PRESENCE_ON:
|
||||||
case INT_PRESENCE_OFF:
|
|
||||||
if (!HP_SUPR_RM(ctrl))
|
if (!HP_SUPR_RM(ctrl))
|
||||||
break;
|
break;
|
||||||
|
ctrl_dbg(ctrl, "Surprise Insertion\n");
|
||||||
|
handle_surprise_event(p_slot);
|
||||||
|
break;
|
||||||
|
case INT_PRESENCE_OFF:
|
||||||
|
/*
|
||||||
|
* Regardless of surprise capability, we need to
|
||||||
|
* definitely remove a card that has been pulled out!
|
||||||
|
*/
|
||||||
ctrl_dbg(ctrl, "Surprise Removal\n");
|
ctrl_dbg(ctrl, "Surprise Removal\n");
|
||||||
handle_surprise_event(p_slot);
|
handle_surprise_event(p_slot);
|
||||||
break;
|
break;
|
||||||
|
case INT_LINK_UP:
|
||||||
|
case INT_LINK_DOWN:
|
||||||
|
handle_link_event(p_slot, info->event_type);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -447,6 +559,9 @@ static void interrupt_event_handler(struct work_struct *work)
|
||||||
kfree(info);
|
kfree(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: This function must be called with slot->hotplug_lock held
|
||||||
|
*/
|
||||||
int pciehp_enable_slot(struct slot *p_slot)
|
int pciehp_enable_slot(struct slot *p_slot)
|
||||||
{
|
{
|
||||||
u8 getstatus = 0;
|
u8 getstatus = 0;
|
||||||
|
@ -479,13 +594,15 @@ int pciehp_enable_slot(struct slot *p_slot)
|
||||||
pciehp_get_latch_status(p_slot, &getstatus);
|
pciehp_get_latch_status(p_slot, &getstatus);
|
||||||
|
|
||||||
rc = board_added(p_slot);
|
rc = board_added(p_slot);
|
||||||
if (rc) {
|
if (rc)
|
||||||
pciehp_get_latch_status(p_slot, &getstatus);
|
pciehp_get_latch_status(p_slot, &getstatus);
|
||||||
}
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: This function must be called with slot->hotplug_lock held
|
||||||
|
*/
|
||||||
int pciehp_disable_slot(struct slot *p_slot)
|
int pciehp_disable_slot(struct slot *p_slot)
|
||||||
{
|
{
|
||||||
u8 getstatus = 0;
|
u8 getstatus = 0;
|
||||||
|
@ -494,24 +611,6 @@ int pciehp_disable_slot(struct slot *p_slot)
|
||||||
if (!p_slot->ctrl)
|
if (!p_slot->ctrl)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!HP_SUPR_RM(p_slot->ctrl)) {
|
|
||||||
pciehp_get_adapter_status(p_slot, &getstatus);
|
|
||||||
if (!getstatus) {
|
|
||||||
ctrl_info(ctrl, "No adapter on slot(%s)\n",
|
|
||||||
slot_name(p_slot));
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MRL_SENS(p_slot->ctrl)) {
|
|
||||||
pciehp_get_latch_status(p_slot, &getstatus);
|
|
||||||
if (getstatus) {
|
|
||||||
ctrl_info(ctrl, "Latch open on slot(%s)\n",
|
|
||||||
slot_name(p_slot));
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (POWER_CTRL(p_slot->ctrl)) {
|
if (POWER_CTRL(p_slot->ctrl)) {
|
||||||
pciehp_get_power_status(p_slot, &getstatus);
|
pciehp_get_power_status(p_slot, &getstatus);
|
||||||
if (!getstatus) {
|
if (!getstatus) {
|
||||||
|
@ -536,7 +635,9 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
|
||||||
case STATIC_STATE:
|
case STATIC_STATE:
|
||||||
p_slot->state = POWERON_STATE;
|
p_slot->state = POWERON_STATE;
|
||||||
mutex_unlock(&p_slot->lock);
|
mutex_unlock(&p_slot->lock);
|
||||||
|
mutex_lock(&p_slot->hotplug_lock);
|
||||||
retval = pciehp_enable_slot(p_slot);
|
retval = pciehp_enable_slot(p_slot);
|
||||||
|
mutex_unlock(&p_slot->hotplug_lock);
|
||||||
mutex_lock(&p_slot->lock);
|
mutex_lock(&p_slot->lock);
|
||||||
p_slot->state = STATIC_STATE;
|
p_slot->state = STATIC_STATE;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -206,7 +206,7 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
|
||||||
mutex_unlock(&ctrl->ctrl_lock);
|
mutex_unlock(&ctrl->ctrl_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_link_active(struct controller *ctrl)
|
bool pciehp_check_link_active(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
u16 lnk_status;
|
u16 lnk_status;
|
||||||
|
@ -225,12 +225,12 @@ static void __pcie_wait_link_active(struct controller *ctrl, bool active)
|
||||||
{
|
{
|
||||||
int timeout = 1000;
|
int timeout = 1000;
|
||||||
|
|
||||||
if (check_link_active(ctrl) == active)
|
if (pciehp_check_link_active(ctrl) == active)
|
||||||
return;
|
return;
|
||||||
while (timeout > 0) {
|
while (timeout > 0) {
|
||||||
msleep(10);
|
msleep(10);
|
||||||
timeout -= 10;
|
timeout -= 10;
|
||||||
if (check_link_active(ctrl) == active)
|
if (pciehp_check_link_active(ctrl) == active)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
|
ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
|
||||||
|
@ -242,11 +242,6 @@ static void pcie_wait_link_active(struct controller *ctrl)
|
||||||
__pcie_wait_link_active(ctrl, true);
|
__pcie_wait_link_active(ctrl, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pcie_wait_link_not_active(struct controller *ctrl)
|
|
||||||
{
|
|
||||||
__pcie_wait_link_active(ctrl, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
|
static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
|
||||||
{
|
{
|
||||||
u32 l;
|
u32 l;
|
||||||
|
@ -332,11 +327,6 @@ static int pciehp_link_enable(struct controller *ctrl)
|
||||||
return __pciehp_link_set(ctrl, true);
|
return __pciehp_link_set(ctrl, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pciehp_link_disable(struct controller *ctrl)
|
|
||||||
{
|
|
||||||
return __pciehp_link_set(ctrl, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -508,14 +498,6 @@ void pciehp_power_off_slot(struct slot * slot)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
/* Disable the link at first */
|
|
||||||
pciehp_link_disable(ctrl);
|
|
||||||
/* wait the link is down */
|
|
||||||
if (ctrl->link_active_reporting)
|
|
||||||
pcie_wait_link_not_active(ctrl);
|
|
||||||
else
|
|
||||||
msleep(1000);
|
|
||||||
|
|
||||||
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
|
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
|
||||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
|
||||||
|
@ -540,7 +522,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||||
|
|
||||||
detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
|
detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
|
||||||
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
|
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
|
||||||
PCI_EXP_SLTSTA_CC);
|
PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
|
||||||
detected &= ~intr_loc;
|
detected &= ~intr_loc;
|
||||||
intr_loc |= detected;
|
intr_loc |= detected;
|
||||||
if (!intr_loc)
|
if (!intr_loc)
|
||||||
|
@ -579,6 +561,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||||
ctrl->power_fault_detected = 1;
|
ctrl->power_fault_detected = 1;
|
||||||
pciehp_handle_power_fault(slot);
|
pciehp_handle_power_fault(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (intr_loc & PCI_EXP_SLTSTA_DLLSC)
|
||||||
|
pciehp_handle_linkstate_change(slot);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,9 +582,17 @@ void pcie_enable_notification(struct controller *ctrl)
|
||||||
* when it is cleared in the interrupt service routine, and
|
* when it is cleared in the interrupt service routine, and
|
||||||
* next power fault detected interrupt was notified again.
|
* next power fault detected interrupt was notified again.
|
||||||
*/
|
*/
|
||||||
cmd = PCI_EXP_SLTCTL_PDCE;
|
|
||||||
|
/*
|
||||||
|
* Always enable link events: thus link-up and link-down shall
|
||||||
|
* always be treated as hotplug and unplug respectively. Enable
|
||||||
|
* presence detect only if Attention Button is not present.
|
||||||
|
*/
|
||||||
|
cmd = PCI_EXP_SLTCTL_DLLSCE;
|
||||||
if (ATTN_BUTTN(ctrl))
|
if (ATTN_BUTTN(ctrl))
|
||||||
cmd |= PCI_EXP_SLTCTL_ABPE;
|
cmd |= PCI_EXP_SLTCTL_ABPE;
|
||||||
|
else
|
||||||
|
cmd |= PCI_EXP_SLTCTL_PDCE;
|
||||||
if (MRL_SENS(ctrl))
|
if (MRL_SENS(ctrl))
|
||||||
cmd |= PCI_EXP_SLTCTL_MRLSCE;
|
cmd |= PCI_EXP_SLTCTL_MRLSCE;
|
||||||
if (!pciehp_poll_mode)
|
if (!pciehp_poll_mode)
|
||||||
|
@ -606,7 +600,8 @@ void pcie_enable_notification(struct controller *ctrl)
|
||||||
|
|
||||||
mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
|
mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
|
||||||
PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
|
PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
|
||||||
PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE);
|
PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
|
||||||
|
PCI_EXP_SLTCTL_DLLSCE);
|
||||||
|
|
||||||
pcie_write_cmd(ctrl, cmd, mask);
|
pcie_write_cmd(ctrl, cmd, mask);
|
||||||
}
|
}
|
||||||
|
@ -624,33 +619,38 @@ static void pcie_disable_notification(struct controller *ctrl)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
|
* pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
|
||||||
* bus reset of the bridge, but if the slot supports surprise removal we need
|
* bus reset of the bridge, but at the same time we want to ensure that it is
|
||||||
* to disable presence detection around the bus reset and clear any spurious
|
* not seen as a hot-unplug, followed by the hot-plug of the device. Thus,
|
||||||
|
* disable link state notification and presence detection change notification
|
||||||
|
* momentarily, if we see that they could interfere. Also, clear any spurious
|
||||||
* events after.
|
* events after.
|
||||||
*/
|
*/
|
||||||
int pciehp_reset_slot(struct slot *slot, int probe)
|
int pciehp_reset_slot(struct slot *slot, int probe)
|
||||||
{
|
{
|
||||||
struct controller *ctrl = slot->ctrl;
|
struct controller *ctrl = slot->ctrl;
|
||||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||||
|
u16 stat_mask = 0, ctrl_mask = 0;
|
||||||
|
|
||||||
if (probe)
|
if (probe)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (HP_SUPR_RM(ctrl)) {
|
if (!ATTN_BUTTN(ctrl)) {
|
||||||
pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE);
|
ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
|
||||||
if (pciehp_poll_mode)
|
stat_mask |= PCI_EXP_SLTSTA_PDC;
|
||||||
del_timer_sync(&ctrl->poll_timer);
|
|
||||||
}
|
}
|
||||||
|
ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE;
|
||||||
|
stat_mask |= PCI_EXP_SLTSTA_DLLSC;
|
||||||
|
|
||||||
|
pcie_write_cmd(ctrl, 0, ctrl_mask);
|
||||||
|
if (pciehp_poll_mode)
|
||||||
|
del_timer_sync(&ctrl->poll_timer);
|
||||||
|
|
||||||
pci_reset_bridge_secondary_bus(ctrl->pcie->port);
|
pci_reset_bridge_secondary_bus(ctrl->pcie->port);
|
||||||
|
|
||||||
if (HP_SUPR_RM(ctrl)) {
|
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
|
||||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
|
||||||
PCI_EXP_SLTSTA_PDC);
|
if (pciehp_poll_mode)
|
||||||
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE);
|
int_poll_timeout(ctrl->poll_timer.data);
|
||||||
if (pciehp_poll_mode)
|
|
||||||
int_poll_timeout(ctrl->poll_timer.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -687,6 +687,7 @@ static int pcie_init_slot(struct controller *ctrl)
|
||||||
|
|
||||||
slot->ctrl = ctrl;
|
slot->ctrl = ctrl;
|
||||||
mutex_init(&slot->lock);
|
mutex_init(&slot->lock);
|
||||||
|
mutex_init(&slot->hotplug_lock);
|
||||||
INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
|
INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
|
||||||
ctrl->slot = slot;
|
ctrl->slot = slot;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -50,7 +50,7 @@ int pciehp_configure_device(struct slot *p_slot)
|
||||||
"at %04x:%02x:00, cannot hot-add\n", pci_name(dev),
|
"at %04x:%02x:00, cannot hot-add\n", pci_name(dev),
|
||||||
pci_domain_nr(parent), parent->number);
|
pci_domain_nr(parent), parent->number);
|
||||||
pci_dev_put(dev);
|
pci_dev_put(dev);
|
||||||
ret = -EINVAL;
|
ret = -EEXIST;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue