diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_hpc.c')
| -rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 680 | 
1 files changed, 288 insertions, 392 deletions
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 50a23da5d24..42914e04d11 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -41,34 +41,11 @@  #include "../pci.h"  #include "pciehp.h" -static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value) +static inline struct pci_dev *ctrl_dev(struct controller *ctrl)  { -	struct pci_dev *dev = ctrl->pcie->port; -	return pci_read_config_word(dev, pci_pcie_cap(dev) + reg, value); +	return ctrl->pcie->port;  } -static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value) -{ -	struct pci_dev *dev = ctrl->pcie->port; -	return pci_read_config_dword(dev, pci_pcie_cap(dev) + reg, value); -} - -static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value) -{ -	struct pci_dev *dev = ctrl->pcie->port; -	return pci_write_config_word(dev, pci_pcie_cap(dev) + reg, value); -} - -static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) -{ -	struct pci_dev *dev = ctrl->pcie->port; -	return pci_write_config_dword(dev, pci_pcie_cap(dev) + reg, value); -} - -/* Power Control Command */ -#define POWER_ON	0 -#define POWER_OFF	PCI_EXP_SLTCTL_PCC -  static irqreturn_t pcie_isr(int irq, void *dev_id);  static void start_int_poll_timer(struct controller *ctrl, int sec); @@ -92,7 +69,7 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)  {  	/* Clamp to sane value */  	if ((sec <= 0) || (sec > 60)) -        	sec = 2; +		sec = 2;  	ctrl->poll_timer.function = &int_poll_timeout;  	ctrl->poll_timer.data = (unsigned long)ctrl; @@ -129,20 +106,23 @@ static inline void pciehp_free_irq(struct controller *ctrl)  static int pcie_poll_cmd(struct controller *ctrl)  { +	struct pci_dev *pdev = ctrl_dev(ctrl);  	u16 slot_status; -	int err, timeout = 1000; +	int timeout = 1000; -	err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -	if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { -		pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); +	if (slot_status & PCI_EXP_SLTSTA_CC) { +		pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +					   PCI_EXP_SLTSTA_CC);  		return 1;  	}  	while (timeout > 0) {  		msleep(10);  		timeout -= 10; -		err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -		if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { -			pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); +		pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); +		if (slot_status & PCI_EXP_SLTSTA_CC) { +			pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +						   PCI_EXP_SLTSTA_CC);  			return 1;  		}  	} @@ -169,22 +149,18 @@ static void pcie_wait_cmd(struct controller *ctrl, int poll)   * @cmd:  command value written to slot control register   * @mask: bitmask of slot control register to be modified   */ -static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) +static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)  { -	int retval = 0; +	struct pci_dev *pdev = ctrl_dev(ctrl);  	u16 slot_status;  	u16 slot_ctrl;  	mutex_lock(&ctrl->ctrl_lock); -	retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", -			 __func__); -		goto out; -	} - +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);  	if (slot_status & PCI_EXP_SLTSTA_CC) { +		pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +					   PCI_EXP_SLTSTA_CC);  		if (!ctrl->no_cmd_complete) {  			/*  			 * After 1 sec and CMD_COMPLETED still not set, just @@ -194,37 +170,28 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)  			ctrl_dbg(ctrl, "CMD_COMPLETED not clear after 1 sec\n");  		} else if (!NO_CMD_CMPL(ctrl)) {  			/* -			 * This controller semms to notify of command completed +			 * This controller seems to notify of command completed  			 * event even though it supports none of power  			 * controller, attention led, power led and EMI.  			 */ -			ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Need to " -				 "wait for command completed event.\n"); +			ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Need to wait for command completed event\n");  			ctrl->no_cmd_complete = 0;  		} else { -			ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Maybe " -				 "the controller is broken.\n"); +			ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Maybe the controller is broken\n");  		}  	} -	retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); -		goto out; -	} - +	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);  	slot_ctrl &= ~mask;  	slot_ctrl |= (cmd & mask);  	ctrl->cmd_busy = 1;  	smp_mb(); -	retval = pciehp_writew(ctrl, PCI_EXP_SLTCTL, slot_ctrl); -	if (retval) -		ctrl_err(ctrl, "Cannot write to SLOTCTRL register\n"); +	pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl);  	/*  	 * Wait for command completion.  	 */ -	if (!retval && !ctrl->no_cmd_complete) { +	if (!ctrl->no_cmd_complete) {  		int poll = 0;  		/*  		 * if hotplug interrupt is not enabled or command @@ -234,328 +201,313 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)  		if (!(slot_ctrl & PCI_EXP_SLTCTL_HPIE) ||  		    !(slot_ctrl & PCI_EXP_SLTCTL_CCIE))  			poll = 1; -                pcie_wait_cmd(ctrl, poll); +		pcie_wait_cmd(ctrl, poll);  	} - out:  	mutex_unlock(&ctrl->ctrl_lock); -	return retval;  } -static inline int check_link_active(struct controller *ctrl) +bool pciehp_check_link_active(struct controller *ctrl)  { -	u16 link_status; +	struct pci_dev *pdev = ctrl_dev(ctrl); +	u16 lnk_status; +	bool ret; -	if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &link_status)) -		return 0; -	return !!(link_status & PCI_EXP_LNKSTA_DLLLA); +	pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); +	ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); + +	if (ret) +		ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); + +	return ret;  } -static void pcie_wait_link_active(struct controller *ctrl) +static void __pcie_wait_link_active(struct controller *ctrl, bool active)  {  	int timeout = 1000; -	if (check_link_active(ctrl)) +	if (pciehp_check_link_active(ctrl) == active)  		return;  	while (timeout > 0) {  		msleep(10);  		timeout -= 10; -		if (check_link_active(ctrl)) +		if (pciehp_check_link_active(ctrl) == active)  			return;  	} -	ctrl_dbg(ctrl, "Data Link Layer Link Active not set in 1000 msec\n"); +	ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", +			active ? "set" : "cleared"); +} + +static void pcie_wait_link_active(struct controller *ctrl) +{ +	__pcie_wait_link_active(ctrl, true); +} + +static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) +{ +	u32 l; +	int count = 0; +	int delay = 1000, step = 20; +	bool found = false; + +	do { +		found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0); +		count++; + +		if (found) +			break; + +		msleep(step); +		delay -= step; +	} while (delay > 0); + +	if (count > 1 && pciehp_debug) +		printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n", +			pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), +			PCI_FUNC(devfn), count, step, l); + +	return found;  }  int pciehp_check_link_status(struct controller *ctrl)  { +	struct pci_dev *pdev = ctrl_dev(ctrl); +	bool found;  	u16 lnk_status; -	int retval = 0; - -        /* -         * Data Link Layer Link Active Reporting must be capable for -         * hot-plug capable downstream port. But old controller might -         * not implement it. In this case, we wait for 1000 ms. -         */ -        if (ctrl->link_active_reporting){ -                /* Wait for Data Link Layer Link Active bit to be set */ -                pcie_wait_link_active(ctrl); -                /* -                 * We must wait for 100 ms after the Data Link Layer -                 * Link Active bit reads 1b before initiating a -                 * configuration access to the hot added device. -                 */ -                msleep(100); -        } else -                msleep(1000); - -	retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); -	if (retval) { -		ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); -		return retval; -	} +	/* +	 * Data Link Layer Link Active Reporting must be capable for +	 * hot-plug capable downstream port. But old controller might +	 * not implement it. In this case, we wait for 1000 ms. +	*/ +	if (ctrl->link_active_reporting) +		pcie_wait_link_active(ctrl); +	else +		msleep(1000); + +	/* wait 100ms before read pci conf, and try in 1s */ +	msleep(100); +	found = pci_bus_check_dev(ctrl->pcie->port->subordinate, +					PCI_DEVFN(0, 0)); + +	pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);  	ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);  	if ((lnk_status & PCI_EXP_LNKSTA_LT) ||  	    !(lnk_status & PCI_EXP_LNKSTA_NLW)) { -		ctrl_err(ctrl, "Link Training Error occurs \n"); -		retval = -1; -		return retval; +		ctrl_err(ctrl, "Link Training Error occurs\n"); +		return -1;  	} -	return retval; +	pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); + +	if (!found) +		return -1; + +	return 0; +} + +static int __pciehp_link_set(struct controller *ctrl, bool enable) +{ +	struct pci_dev *pdev = ctrl_dev(ctrl); +	u16 lnk_ctrl; + +	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); + +	if (enable) +		lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; +	else +		lnk_ctrl |= PCI_EXP_LNKCTL_LD; + +	pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); +	ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); +	return 0;  } -int pciehp_get_attention_status(struct slot *slot, u8 *status) +static int pciehp_link_enable(struct controller *ctrl) +{ +	return __pciehp_link_set(ctrl, true); +} + +void pciehp_get_attention_status(struct slot *slot, u8 *status)  {  	struct controller *ctrl = slot->ctrl; +	struct pci_dev *pdev = ctrl_dev(ctrl);  	u16 slot_ctrl; -	u8 atten_led_state; -	int retval = 0; - -	retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); -		return retval; -	} +	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,  		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); -	atten_led_state = (slot_ctrl & PCI_EXP_SLTCTL_AIC) >> 6; - -	switch (atten_led_state) { -	case 0: -		*status = 0xFF;	/* Reserved */ -		break; -	case 1: +	switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) { +	case PCI_EXP_SLTCTL_ATTN_IND_ON:  		*status = 1;	/* On */  		break; -	case 2: +	case PCI_EXP_SLTCTL_ATTN_IND_BLINK:  		*status = 2;	/* Blink */  		break; -	case 3: +	case PCI_EXP_SLTCTL_ATTN_IND_OFF:  		*status = 0;	/* Off */  		break;  	default:  		*status = 0xFF;  		break;  	} - -	return 0;  } -int pciehp_get_power_status(struct slot *slot, u8 *status) +void pciehp_get_power_status(struct slot *slot, u8 *status)  {  	struct controller *ctrl = slot->ctrl; +	struct pci_dev *pdev = ctrl_dev(ctrl);  	u16 slot_ctrl; -	u8 pwr_state; -	int	retval = 0; -	retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); -		return retval; -	} +	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__,  		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); -	pwr_state = (slot_ctrl & PCI_EXP_SLTCTL_PCC) >> 10; - -	switch (pwr_state) { -	case 0: -		*status = 1; +	switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) { +	case PCI_EXP_SLTCTL_PWR_ON: +		*status = 1;	/* On */  		break; -	case 1: -		*status = 0; +	case PCI_EXP_SLTCTL_PWR_OFF: +		*status = 0;	/* Off */  		break;  	default:  		*status = 0xFF;  		break;  	} - -	return retval;  } -int pciehp_get_latch_status(struct slot *slot, u8 *status) +void pciehp_get_latch_status(struct slot *slot, u8 *status)  { -	struct controller *ctrl = slot->ctrl; +	struct pci_dev *pdev = ctrl_dev(slot->ctrl);  	u16 slot_status; -	int retval; -	retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", -			 __func__); -		return retval; -	} +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);  	*status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); -	return 0;  } -int pciehp_get_adapter_status(struct slot *slot, u8 *status) +void pciehp_get_adapter_status(struct slot *slot, u8 *status)  { -	struct controller *ctrl = slot->ctrl; +	struct pci_dev *pdev = ctrl_dev(slot->ctrl);  	u16 slot_status; -	int retval; -	retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", -			 __func__); -		return retval; -	} +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);  	*status = !!(slot_status & PCI_EXP_SLTSTA_PDS); -	return 0;  }  int pciehp_query_power_fault(struct slot *slot)  { -	struct controller *ctrl = slot->ctrl; +	struct pci_dev *pdev = ctrl_dev(slot->ctrl);  	u16 slot_status; -	int retval; -	retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -	if (retval) { -		ctrl_err(ctrl, "Cannot check for power fault\n"); -		return retval; -	} +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);  	return !!(slot_status & PCI_EXP_SLTSTA_PFD);  } -int pciehp_set_attention_status(struct slot *slot, u8 value) +void pciehp_set_attention_status(struct slot *slot, u8 value)  {  	struct controller *ctrl = slot->ctrl;  	u16 slot_cmd; -	u16 cmd_mask; -	cmd_mask = PCI_EXP_SLTCTL_AIC; +	if (!ATTN_LED(ctrl)) +		return; +  	switch (value) { -	case 0 :	/* turn off */ -		slot_cmd = 0x00C0; +	case 0:		/* turn off */ +		slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF;  		break;  	case 1:		/* turn on */ -		slot_cmd = 0x0040; +		slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON;  		break;  	case 2:		/* turn blink */ -		slot_cmd = 0x0080; +		slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK;  		break;  	default: -		return -EINVAL; +		return;  	}  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,  		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); -	return pcie_write_cmd(ctrl, slot_cmd, cmd_mask); +	pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);  }  void pciehp_green_led_on(struct slot *slot)  {  	struct controller *ctrl = slot->ctrl; -	u16 slot_cmd; -	u16 cmd_mask; -	slot_cmd = 0x0100; -	cmd_mask = PCI_EXP_SLTCTL_PIC; -	pcie_write_cmd(ctrl, slot_cmd, cmd_mask); +	if (!PWR_LED(ctrl)) +		return; + +	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, -		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); +		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, +		 PCI_EXP_SLTCTL_PWR_IND_ON);  }  void pciehp_green_led_off(struct slot *slot)  {  	struct controller *ctrl = slot->ctrl; -	u16 slot_cmd; -	u16 cmd_mask; -	slot_cmd = 0x0300; -	cmd_mask = PCI_EXP_SLTCTL_PIC; -	pcie_write_cmd(ctrl, slot_cmd, cmd_mask); +	if (!PWR_LED(ctrl)) +		return; + +	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, -		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); +		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, +		 PCI_EXP_SLTCTL_PWR_IND_OFF);  }  void pciehp_green_led_blink(struct slot *slot)  {  	struct controller *ctrl = slot->ctrl; -	u16 slot_cmd; -	u16 cmd_mask; -	slot_cmd = 0x0200; -	cmd_mask = PCI_EXP_SLTCTL_PIC; -	pcie_write_cmd(ctrl, slot_cmd, cmd_mask); +	if (!PWR_LED(ctrl)) +		return; + +	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, -		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); +		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, +		 PCI_EXP_SLTCTL_PWR_IND_BLINK);  } -int pciehp_power_on_slot(struct slot * slot) +int pciehp_power_on_slot(struct slot *slot)  {  	struct controller *ctrl = slot->ctrl; -	u16 slot_cmd; -	u16 cmd_mask; +	struct pci_dev *pdev = ctrl_dev(ctrl);  	u16 slot_status; -	u16 lnk_status; -	int retval = 0; +	int retval;  	/* Clear sticky power-fault bit from previous power failures */ -	retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", -			 __func__); -		return retval; -	} -	slot_status &= PCI_EXP_SLTSTA_PFD; -	if (slot_status) { -		retval = pciehp_writew(ctrl, PCI_EXP_SLTSTA, slot_status); -		if (retval) { -			ctrl_err(ctrl, -				 "%s: Cannot write to SLOTSTATUS register\n", -				 __func__); -			return retval; -		} -	} +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); +	if (slot_status & PCI_EXP_SLTSTA_PFD) +		pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +					   PCI_EXP_SLTSTA_PFD);  	ctrl->power_fault_detected = 0; -	slot_cmd = POWER_ON; -	cmd_mask = PCI_EXP_SLTCTL_PCC; -	retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); -	if (retval) { -		ctrl_err(ctrl, "Write %x command failed!\n", slot_cmd); -		return retval; -	} +	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, -		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); +		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, +		 PCI_EXP_SLTCTL_PWR_ON); -	retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read LNKSTA register\n", -				__func__); -		return retval; -	} -	pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); +	retval = pciehp_link_enable(ctrl); +	if (retval) +		ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);  	return retval;  } -int pciehp_power_off_slot(struct slot * slot) +void pciehp_power_off_slot(struct slot *slot)  {  	struct controller *ctrl = slot->ctrl; -	u16 slot_cmd; -	u16 cmd_mask; -	int retval; -	slot_cmd = POWER_OFF; -	cmd_mask = PCI_EXP_SLTCTL_PCC; -	retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); -	if (retval) { -		ctrl_err(ctrl, "Write command failed!\n"); -		return retval; -	} +	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);  	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, -		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); -	return 0; +		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, +		 PCI_EXP_SLTCTL_PWR_OFF);  }  static irqreturn_t pcie_isr(int irq, void *dev_id)  {  	struct controller *ctrl = (struct controller *)dev_id; +	struct pci_dev *pdev = ctrl_dev(ctrl);  	struct slot *slot = ctrl->slot;  	u16 detected, intr_loc; @@ -566,24 +518,18 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)  	 */  	intr_loc = 0;  	do { -		if (pciehp_readw(ctrl, PCI_EXP_SLTSTA, &detected)) { -			ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS\n", -				 __func__); -			return IRQ_NONE; -		} +		pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected);  		detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |  			     PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | -			     PCI_EXP_SLTSTA_CC); +			     PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);  		detected &= ~intr_loc;  		intr_loc |= detected;  		if (!intr_loc)  			return IRQ_NONE; -		if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, intr_loc)) { -			ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", -				 __func__); -			return IRQ_NONE; -		} +		if (detected) +			pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +						   intr_loc);  	} while (detected);  	ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc); @@ -615,111 +561,14 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)  		ctrl->power_fault_detected = 1;  		pciehp_handle_power_fault(slot);  	} -	return IRQ_HANDLED; -} - -int pciehp_get_max_lnk_width(struct slot *slot, -				 enum pcie_link_width *value) -{ -	struct controller *ctrl = slot->ctrl; -	enum pcie_link_width lnk_wdth; -	u32	lnk_cap; -	int retval = 0; - -	retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); -		return retval; -	} - -	switch ((lnk_cap & PCI_EXP_LNKSTA_NLW) >> 4){ -	case 0: -		lnk_wdth = PCIE_LNK_WIDTH_RESRV; -		break; -	case 1: -		lnk_wdth = PCIE_LNK_X1; -		break; -	case 2: -		lnk_wdth = PCIE_LNK_X2; -		break; -	case 4: -		lnk_wdth = PCIE_LNK_X4; -		break; -	case 8: -		lnk_wdth = PCIE_LNK_X8; -		break; -	case 12: -		lnk_wdth = PCIE_LNK_X12; -		break; -	case 16: -		lnk_wdth = PCIE_LNK_X16; -		break; -	case 32: -		lnk_wdth = PCIE_LNK_X32; -		break; -	default: -		lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; -		break; -	} - -	*value = lnk_wdth; -	ctrl_dbg(ctrl, "Max link width = %d\n", lnk_wdth); - -	return retval; -} - -int pciehp_get_cur_lnk_width(struct slot *slot, -				 enum pcie_link_width *value) -{ -	struct controller *ctrl = slot->ctrl; -	enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; -	int retval = 0; -	u16 lnk_status; - -	retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); -	if (retval) { -		ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n", -			 __func__); -		return retval; -	} - -	switch ((lnk_status & PCI_EXP_LNKSTA_NLW) >> 4){ -	case 0: -		lnk_wdth = PCIE_LNK_WIDTH_RESRV; -		break; -	case 1: -		lnk_wdth = PCIE_LNK_X1; -		break; -	case 2: -		lnk_wdth = PCIE_LNK_X2; -		break; -	case 4: -		lnk_wdth = PCIE_LNK_X4; -		break; -	case 8: -		lnk_wdth = PCIE_LNK_X8; -		break; -	case 12: -		lnk_wdth = PCIE_LNK_X12; -		break; -	case 16: -		lnk_wdth = PCIE_LNK_X16; -		break; -	case 32: -		lnk_wdth = PCIE_LNK_X32; -		break; -	default: -		lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; -		break; -	} -	*value = lnk_wdth; -	ctrl_dbg(ctrl, "Current link width = %d\n", lnk_wdth); +	if (intr_loc & PCI_EXP_SLTSTA_DLLSC) +		pciehp_handle_linkstate_change(slot); -	return retval; +	return IRQ_HANDLED;  } -int pcie_enable_notification(struct controller *ctrl) +void pcie_enable_notification(struct controller *ctrl)  {  	u16 cmd, mask; @@ -733,9 +582,17 @@ int pcie_enable_notification(struct controller *ctrl)  	 * when it is cleared in the interrupt service routine, and  	 * 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))  		cmd |= PCI_EXP_SLTCTL_ABPE; +	else +		cmd |= PCI_EXP_SLTCTL_PDCE;  	if (MRL_SENS(ctrl))  		cmd |= PCI_EXP_SLTCTL_MRLSCE;  	if (!pciehp_poll_mode) @@ -743,34 +600,66 @@ int pcie_enable_notification(struct controller *ctrl)  	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |  		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); -	if (pcie_write_cmd(ctrl, cmd, mask)) { -		ctrl_err(ctrl, "Cannot enable software notification\n"); -		return -1; -	} -	return 0; +	pcie_write_cmd(ctrl, cmd, mask);  }  static void pcie_disable_notification(struct controller *ctrl)  {  	u16 mask; +  	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |  		PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |  		PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |  		PCI_EXP_SLTCTL_DLLSCE); -	if (pcie_write_cmd(ctrl, 0, mask)) -		ctrl_warn(ctrl, "Cannot disable software notification\n"); +	pcie_write_cmd(ctrl, 0, mask); +} + +/* + * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary + * bus reset of the bridge, but at the same time we want to ensure that it is + * 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. + */ +int pciehp_reset_slot(struct slot *slot, int probe) +{ +	struct controller *ctrl = slot->ctrl; +	struct pci_dev *pdev = ctrl_dev(ctrl); +	u16 stat_mask = 0, ctrl_mask = 0; + +	if (probe) +		return 0; + +	if (!ATTN_BUTTN(ctrl)) { +		ctrl_mask |= PCI_EXP_SLTCTL_PDCE; +		stat_mask |= PCI_EXP_SLTSTA_PDC; +	} +	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); + +	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask); +	pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask); +	if (pciehp_poll_mode) +		int_poll_timeout(ctrl->poll_timer.data); + +	return 0;  }  int pcie_init_notification(struct controller *ctrl)  {  	if (pciehp_request_irq(ctrl))  		return -1; -	if (pcie_enable_notification(ctrl)) { -		pciehp_free_irq(ctrl); -		return -1; -	} +	pcie_enable_notification(ctrl);  	ctrl->notification_enabled = 1;  	return 0;  } @@ -792,19 +681,26 @@ static int pcie_init_slot(struct controller *ctrl)  	if (!slot)  		return -ENOMEM; +	slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl)); +	if (!slot->wq) +		goto abort; +  	slot->ctrl = ctrl;  	mutex_init(&slot->lock); +	mutex_init(&slot->hotplug_lock);  	INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);  	ctrl->slot = slot;  	return 0; +abort: +	kfree(slot); +	return -ENOMEM;  }  static void pcie_cleanup_slot(struct controller *ctrl)  {  	struct slot *slot = ctrl->slot;  	cancel_delayed_work(&slot->work); -	flush_workqueue(pciehp_wq); -	flush_workqueue(pciehp_ordered_wq); +	destroy_workqueue(slot->wq);  	kfree(slot);  } @@ -852,12 +748,14 @@ static inline void dbg_ctrl(struct controller *ctrl)  		  EMI(ctrl)        ? "yes" : "no");  	ctrl_info(ctrl, "  Command Completed    : %3s\n",  		  NO_CMD_CMPL(ctrl) ? "no" : "yes"); -	pciehp_readw(ctrl, PCI_EXP_SLTSTA, ®16); +	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, ®16);  	ctrl_info(ctrl, "Slot Status            : 0x%04x\n", reg16); -	pciehp_readw(ctrl, PCI_EXP_SLTCTL, ®16); +	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, ®16);  	ctrl_info(ctrl, "Slot Control           : 0x%04x\n", reg16);  } +#define FLAG(x, y)	(((x) & (y)) ? '+' : '-') +  struct controller *pcie_init(struct pcie_device *dev)  {  	struct controller *ctrl; @@ -870,15 +768,7 @@ struct controller *pcie_init(struct pcie_device *dev)  		goto abort;  	}  	ctrl->pcie = dev; -	if (!pci_pcie_cap(pdev)) { -		ctrl_err(ctrl, "Cannot find PCI Express capability\n"); -		goto abort_ctrl; -	} -	if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) { -		ctrl_err(ctrl, "Cannot read SLOTCAP register\n"); -		goto abort_ctrl; -	} - +	pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);  	ctrl->slot_cap = slot_cap;  	mutex_init(&ctrl->ctrl_lock);  	init_waitqueue_head(&ctrl->queue); @@ -891,28 +781,34 @@ struct controller *pcie_init(struct pcie_device *dev)  	 */  	if (NO_CMD_CMPL(ctrl) ||  	    !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl))) -	    ctrl->no_cmd_complete = 1; - -        /* Check if Data Link Layer Link Active Reporting is implemented */ -        if (pciehp_readl(ctrl, PCI_EXP_LNKCAP, &link_cap)) { -                ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); -                goto abort_ctrl; -        } -        if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { -                ctrl_dbg(ctrl, "Link Active Reporting supported\n"); -                ctrl->link_active_reporting = 1; -        } +		ctrl->no_cmd_complete = 1; + +	/* Check if Data Link Layer Link Active Reporting is implemented */ +	pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); +	if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { +		ctrl_dbg(ctrl, "Link Active Reporting supported\n"); +		ctrl->link_active_reporting = 1; +	}  	/* Clear all remaining event bits in Slot Status register */ -	if (pciehp_writew(ctrl, PCI_EXP_SLTSTA, 0x1f)) -		goto abort_ctrl; +	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +		PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | +		PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | +		PCI_EXP_SLTSTA_CC); -	/* Disable sotfware notification */ +	/* Disable software notification */  	pcie_disable_notification(ctrl); -	ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", -		  pdev->vendor, pdev->device, pdev->subsystem_vendor, -		  pdev->subsystem_device); +	ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n", +		(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, +		FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), +		FLAG(slot_cap, PCI_EXP_SLTCAP_AIP), +		FLAG(slot_cap, PCI_EXP_SLTCAP_PIP), +		FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), +		FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP), +		FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), +		FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), +		FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC));  	if (pcie_init_slot(ctrl))  		goto abort_ctrl;  | 
