⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pciehp_hpc.c

📁 audio driver for hotplug pci on linux 2.6.27
💻 C
📖 第 1 页 / 共 2 页
字号:
	u16 cmd_mask;	slot_cmd = 0x0200;	cmd_mask = PWR_LED_CTRL;	pcie_write_cmd(ctrl, slot_cmd, cmd_mask);	dbg("%s: SLOTCTRL %x write cmd %x\n",	    __func__, ctrl->cap_base + SLOTCTRL, slot_cmd);}static int hpc_power_on_slot(struct slot * slot){	struct controller *ctrl = slot->ctrl;	u16 slot_cmd;	u16 cmd_mask;	u16 slot_status;	int retval = 0;	dbg("%s: slot->hp_slot %x\n", __func__, slot->hp_slot);	/* Clear sticky power-fault bit from previous power failures */	retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);	if (retval) {		err("%s: Cannot read SLOTSTATUS register\n", __func__);		return retval;	}	slot_status &= PWR_FAULT_DETECTED;	if (slot_status) {		retval = pciehp_writew(ctrl, SLOTSTATUS, slot_status);		if (retval) {			err("%s: Cannot write to SLOTSTATUS register\n",			    __func__);			return retval;		}	}	slot_cmd = POWER_ON;	cmd_mask = PWR_CTRL;	/* Enable detection that we turned off at slot power-off time */	if (!pciehp_poll_mode) {		slot_cmd |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |			     PRSN_DETECT_ENABLE);		cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |			     PRSN_DETECT_ENABLE);	}	retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);	if (retval) {		err("%s: Write %x command failed!\n", __func__, slot_cmd);		return -1;	}	dbg("%s: SLOTCTRL %x write cmd %x\n",	    __func__, ctrl->cap_base + SLOTCTRL, slot_cmd);	return retval;}static inline int pcie_mask_bad_dllp(struct controller *ctrl){	struct pci_dev *dev = ctrl->pci_dev;	int pos;	u32 reg;	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);	if (!pos)		return 0;	pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);	if (reg & PCI_ERR_COR_BAD_DLLP)		return 0;	reg |= PCI_ERR_COR_BAD_DLLP;	pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);	return 1;}static inline void pcie_unmask_bad_dllp(struct controller *ctrl){	struct pci_dev *dev = ctrl->pci_dev;	u32 reg;	int pos;	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);	if (!pos)		return;	pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);	if (!(reg & PCI_ERR_COR_BAD_DLLP))		return;	reg &= ~PCI_ERR_COR_BAD_DLLP;	pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);}static int hpc_power_off_slot(struct slot * slot){	struct controller *ctrl = slot->ctrl;	u16 slot_cmd;	u16 cmd_mask;	int retval = 0;	int changed;	dbg("%s: slot->hp_slot %x\n", __func__, slot->hp_slot);	/*	 * Set Bad DLLP Mask bit in Correctable Error Mask	 * Register. This is the workaround against Bad DLLP error	 * that sometimes happens during turning power off the slot	 * which conforms to PCI Express 1.0a spec.	 */	changed = pcie_mask_bad_dllp(ctrl);	slot_cmd = POWER_OFF;	cmd_mask = PWR_CTRL;	/*	 * If we get MRL or presence detect interrupts now, the isr	 * will notice the sticky power-fault bit too and issue power	 * indicator change commands. This will lead to an endless loop	 * of command completions, since the power-fault bit remains on	 * till the slot is powered on again.	 */	if (!pciehp_poll_mode) {		slot_cmd &= ~(PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |			      PRSN_DETECT_ENABLE);		cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |			     PRSN_DETECT_ENABLE);	}	retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);	if (retval) {		err("%s: Write command failed!\n", __func__);		retval = -1;		goto out;	}	dbg("%s: SLOTCTRL %x write cmd %x\n",	    __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); out:	if (changed)		pcie_unmask_bad_dllp(ctrl);	return retval;}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	 * serviced, we need to re-inspect Slot Status register after	 * clearing what is presumed to be the last pending interrupt.	 */	intr_loc = 0;	do {		if (pciehp_readw(ctrl, SLOTSTATUS, &detected)) {			err("%s: Cannot read SLOTSTATUS\n", __func__);			return IRQ_NONE;		}		detected &= (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED |			     MRL_SENS_CHANGED | PRSN_DETECT_CHANGED |			     CMD_COMPLETED);		intr_loc |= detected;		if (!intr_loc)			return IRQ_NONE;		if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) {			err("%s: Cannot write to SLOTSTATUS\n", __func__);			return IRQ_NONE;		}	} while (detected);	dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc);	/* Check Command Complete Interrupt Pending */	if (intr_loc & CMD_COMPLETED) {		ctrl->cmd_busy = 0;		smp_mb();		wake_up(&ctrl->queue);	}	if (!(intr_loc & ~CMD_COMPLETED))		return IRQ_HANDLED;	p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);	/* Check MRL Sensor Changed */	if (intr_loc & MRL_SENS_CHANGED)		pciehp_handle_switch_change(p_slot);	/* Check Attention Button Pressed */	if (intr_loc & ATTN_BUTTN_PRESSED)		pciehp_handle_attention_button(p_slot);	/* Check Presence Detect Changed */	if (intr_loc & PRSN_DETECT_CHANGED)		pciehp_handle_presence_change(p_slot);	/* Check Power Fault Detected */	if (intr_loc & PWR_FAULT_DETECTED)		pciehp_handle_power_fault(p_slot);	return IRQ_HANDLED;}static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value){	struct controller *ctrl = slot->ctrl;	enum pcie_link_speed lnk_speed;	u32	lnk_cap;	int retval = 0;	retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap);	if (retval) {		err("%s: Cannot read LNKCAP register\n", __func__);		return retval;	}	switch (lnk_cap & 0x000F) {	case 1:		lnk_speed = PCIE_2PT5GB;		break;	default:		lnk_speed = PCIE_LNK_SPEED_UNKNOWN;		break;	}	*value = lnk_speed;	dbg("Max link speed = %d\n", lnk_speed);	return retval;}static int hpc_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, LNKCAP, &lnk_cap);	if (retval) {		err("%s: Cannot read LNKCAP register\n", __func__);		return retval;	}	switch ((lnk_cap & 0x03F0) >> 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;	dbg("Max link width = %d\n", lnk_wdth);	return retval;}static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value){	struct controller *ctrl = slot->ctrl;	enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN;	int retval = 0;	u16 lnk_status;	retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status);	if (retval) {		err("%s: Cannot read LNKSTATUS register\n", __func__);		return retval;	}	switch (lnk_status & 0x0F) {	case 1:		lnk_speed = PCIE_2PT5GB;		break;	default:		lnk_speed = PCIE_LNK_SPEED_UNKNOWN;		break;	}	*value = lnk_speed;	dbg("Current link speed = %d\n", lnk_speed);	return retval;}static int hpc_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, LNKSTATUS, &lnk_status);	if (retval) {		err("%s: Cannot read LNKSTATUS register\n", __func__);		return retval;	}	switch ((lnk_status & 0x03F0) >> 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;	dbg("Current link width = %d\n", lnk_wdth);	return retval;}static void pcie_release_ctrl(struct controller *ctrl);static struct hpc_ops pciehp_hpc_ops = {	.power_on_slot			= hpc_power_on_slot,	.power_off_slot			= hpc_power_off_slot,	.set_attention_status		= hpc_set_attention_status,	.get_power_status		= hpc_get_power_status,	.get_attention_status		= hpc_get_attention_status,	.get_latch_status		= hpc_get_latch_status,	.get_adapter_status		= hpc_get_adapter_status,	.get_emi_status			= hpc_get_emi_status,	.toggle_emi			= hpc_toggle_emi,	.get_max_bus_speed		= hpc_get_max_lnk_speed,	.get_cur_bus_speed		= hpc_get_cur_lnk_speed,	.get_max_lnk_width		= hpc_get_max_lnk_width,	.get_cur_lnk_width		= hpc_get_cur_lnk_width,	.query_power_fault		= hpc_query_power_fault,	.green_led_on			= hpc_set_green_led_on,	.green_led_off			= hpc_set_green_led_off,	.green_led_blink		= hpc_set_green_led_blink,	.release_ctlr			= pcie_release_ctrl,	.check_lnk_status		= hpc_check_lnk_status,};int pcie_enable_notification(struct controller *ctrl){	u16 cmd, mask;	cmd = PRSN_DETECT_ENABLE;	if (ATTN_BUTTN(ctrl))		cmd |= ATTN_BUTTN_ENABLE;	if (POWER_CTRL(ctrl))		cmd |= PWR_FAULT_DETECT_ENABLE;	if (MRL_SENS(ctrl))		cmd |= MRL_DETECT_ENABLE;	if (!pciehp_poll_mode)		cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;	mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |	       PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;	if (pcie_write_cmd(ctrl, cmd, mask)) {		err("%s: Cannot enable software notification\n", __func__);		return -1;	}	return 0;}static void pcie_disable_notification(struct controller *ctrl){	u16 mask;	mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |	       PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;	if (pcie_write_cmd(ctrl, 0, mask))		warn("%s: Cannot disable software notification\n", __func__);}static 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;	}	return 0;}static void pcie_shutdown_notification(struct controller *ctrl){	pcie_disable_notification(ctrl);	pciehp_free_irq(ctrl);}static int pcie_init_slot(struct controller *ctrl){	struct slot *slot;	slot = kzalloc(sizeof(*slot), GFP_KERNEL);	if (!slot)		return -ENOMEM;	slot->hp_slot = 0;	slot->ctrl = ctrl;	slot->bus = ctrl->pci_dev->subordinate->number;	slot->device = ctrl->slot_device_offset + slot->hp_slot;	slot->hpc_ops = ctrl->hpc_ops;	slot->number = ctrl->first_slot;	mutex_init(&slot->lock);	INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);	list_add(&slot->slot_list, &ctrl->slot_list);	return 0;}static void pcie_cleanup_slot(struct controller *ctrl){	struct slot *slot;	slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list);	list_del(&slot->slot_list);	cancel_delayed_work(&slot->work);	flush_scheduled_work();	flush_workqueue(pciehp_wq);	kfree(slot);}static inline void dbg_ctrl(struct controller *ctrl){	int i;	u16 reg16;	struct pci_dev *pdev = ctrl->pci_dev;	if (!pciehp_debug)		return;	dbg("Hotplug Controller:\n");	dbg("  Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n", pci_name(pdev), pdev->irq);	dbg("  Vendor ID            : 0x%04x\n", pdev->vendor);	dbg("  Device ID            : 0x%04x\n", pdev->device);	dbg("  Subsystem ID         : 0x%04x\n", pdev->subsystem_device);	dbg("  Subsystem Vendor ID  : 0x%04x\n", pdev->subsystem_vendor);	dbg("  PCIe Cap offset      : 0x%02x\n", ctrl->cap_base);	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {		if (!pci_resource_len(pdev, i))			continue;		dbg("  PCI resource [%d]     : 0x%llx@0x%llx\n", i,		    (unsigned long long)pci_resource_len(pdev, i),		    (unsigned long long)pci_resource_start(pdev, i));	}	dbg("Slot Capabilities      : 0x%08x\n", ctrl->slot_cap);	dbg("  Physical Slot Number : %d\n", ctrl->first_slot);	dbg("  Attention Button     : %3s\n", ATTN_BUTTN(ctrl) ? "yes" : "no");	dbg("  Power Controller     : %3s\n", POWER_CTRL(ctrl) ? "yes" : "no");	dbg("  MRL Sensor           : %3s\n", MRL_SENS(ctrl)   ? "yes" : "no");	dbg("  Attention Indicator  : %3s\n", ATTN_LED(ctrl)   ? "yes" : "no");	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("  Command Completed    : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes");	pciehp_readw(ctrl, SLOTSTATUS, &reg16);	dbg("Slot Status            : 0x%04x\n", reg16);	pciehp_readw(ctrl, SLOTCTRL, &reg16);	dbg("Slot Control           : 0x%04x\n", reg16);}struct controller *pcie_init(struct pcie_device *dev){	struct controller *ctrl;	u32 slot_cap;	struct pci_dev *pdev = dev->port;	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);	if (!ctrl) {		err("%s : out of memory\n", __func__);		goto abort;	}	INIT_LIST_HEAD(&ctrl->slot_list);	ctrl->pci_dev = pdev;	ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP);	if (!ctrl->cap_base) {		err("%s: Cannot find PCI Express capability\n", __func__);		goto abort;	}	if (pciehp_readl(ctrl, SLOTCAP, &slot_cap)) {		err("%s: Cannot read SLOTCAP register\n", __func__);		goto abort;	}	ctrl->slot_cap = slot_cap;	ctrl->first_slot = slot_cap >> 19;	ctrl->slot_device_offset = 0;	ctrl->num_slots = 1;	ctrl->hpc_ops = &pciehp_hpc_ops;	mutex_init(&ctrl->crit_sect);	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;	/* Clear all remaining event bits in Slot Status register */	if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f))		goto abort_ctrl;	/* Disable sotfware notification */	pcie_disable_notification(ctrl);	/*	 * If this is the first controller to be initialized,	 * initialize the pciehp work queue	 */	if (atomic_add_return(1, &pciehp_num_controllers) == 1) {		pciehp_wq = create_singlethread_workqueue("pciehpd");		if (!pciehp_wq)			goto abort_ctrl;	}	info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",	     pdev->vendor, pdev->device,	     pdev->subsystem_vendor, pdev->subsystem_device);	if (pcie_init_slot(ctrl))		goto abort_ctrl;	if (pcie_init_notification(ctrl))		goto abort_slot;	return ctrl;abort_slot:	pcie_cleanup_slot(ctrl);abort_ctrl:	kfree(ctrl);abort:	return NULL;}void pcie_release_ctrl(struct controller *ctrl){	pcie_shutdown_notification(ctrl);	pcie_cleanup_slot(ctrl);	/*	 * If this is the last controller to be released, destroy the	 * pciehp work queue	 */	if (atomic_dec_and_test(&pciehp_num_controllers))		destroy_workqueue(pciehp_wq);	kfree(ctrl);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -