PCIe学习笔记(1)Hot-Plug机制

Hot-Plug Init

PCIe hot-plug是一种支持在不关机情况下从支持的插槽添加或删除设备的功能,PCIe架构定义了一些寄存器以支持原生热插拔。相关寄存器主要分布在Device Capabilities, Slot Capabilities, Slot Control, Slot Status和Slot Capabilities 2。Hot-Plug相关支持寄存器位置如下。
在这里插入图片描述
一个Downstream Port支持以下热插拔事件。

  • Slot Events:
    • Attention Button Pressed
    • Power Fault Detected
    • MRL Sensor Changed
    • Presence Detect Changed
  • Command Completed Events
  • Data Link Layer State Changed Events

主要事件都可以通过寄存器控制使能,需要在初始化时配置启用。如果要启用INTx message,需配置如下:

  • Command register内的Interrupt Disable位需要设为0
  • Slot Control register内的Hot-Plug Interrupt Enable位需要设为1
  • 至少使能一个hot-plug event控制寄存器
    如果要启用MSI/MSI-X,则需取消屏蔽关联向量。PME和Hot-Plug事件中断共享同一向量,由PCIE Capabilities的Interrupt Message Number域段控制。
    在这里插入图片描述

Hot-Plug事件产生需要启用PME_En位。
在这里插入图片描述

Hot Add Flow

主要处理Presence Detect Changed, Command Completed events(需要在No Command Completed Support为0时,否则硬件支持无通知式自动完成)。
pciehp_handle_presence_or_link_change函数为Presence Detect Changed事件中断服务函数。

void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
{
    int present, link_active;

    /*
	 * If the slot is on and presence or link has changed, turn it off.
	 * Even if it's occupied again, we cannot assume the card is the same.
	 */
    mutex_lock(&ctrl->state_lock);
    switch (ctrl->state) {
        case BLINKINGOFF_STATE:
            cancel_delayed_work(&ctrl->button_work);
            fallthrough;
        case ON_STATE:
            ctrl->state = POWEROFF_STATE;
            mutex_unlock(&ctrl->state_lock);
            if (events & PCI_EXP_SLTSTA_DLLSC)
                ctrl_info(ctrl, "Slot(%s): Link Down\n",
                      slot_name(ctrl));
            if (events & PCI_EXP_SLTSTA_PDC)
                ctrl_info(ctrl, "Slot(%s): Card not present\n",
                      slot_name(ctrl));
            pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);  //call remove_board
            break;
        default:
            mutex_unlock(&ctrl->state_lock);
            break;
    }

    /* Turn the slot on if it's occupied or link is up */
    mutex_lock(&ctrl->state_lock);
    present = pciehp_card_present(ctrl);
    link_active = pciehp_check_link_active(ctrl);
    if (present <= 0 && link_active <= 0) {
        if (ctrl->state == BLINKINGON_STATE) {
            ctrl->state = OFF_STATE;
            cancel_delayed_work(&ctrl->button_work);
            pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
                                  INDICATOR_NOOP);
            ctrl_info(ctrl, "Slot(%s): Card not present\n",
                      slot_name(ctrl));
        }
        mutex_unlock(&ctrl->state_lock);
        return;
    }

    switch (ctrl->state) {
        case BLINKINGON_STATE:
            cancel_delayed_work(&ctrl->button_work);
            fallthrough;
        case OFF_STATE:
            ctrl->state = POWERON_STATE;
            mutex_unlock(&ctrl->state_lock);
            if (present)
                ctrl_info(ctrl, "Slot(%s): Card present\n",
                      slot_name(ctrl));
            if (link_active)
                ctrl_info(ctrl, "Slot(%s): Link Up\n",
                      slot_name(ctrl));
            ctrl->request_result = pciehp_enable_slot(ctrl);  //call board_added()
            break;
        default:
            mutex_unlock(&ctrl->state_lock);
            break;
    }
}
pciehp_enable_slot会调用board_added(通过__pciehp_enable_slot)和pciehp_set_indicators
static int pciehp_enable_slot(struct controller *ctrl)
{
    int ret;

    pm_runtime_get_sync(&ctrl->pcie->port->dev);
    ret = __pciehp_enable_slot(ctrl);
    if (ret && ATTN_BUTTN(ctrl))
        /* may be blinking */
        pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
                              INDICATOR_NOOP);
    pm_runtime_put(&ctrl->pcie->port->dev);

    mutex_lock(&ctrl->state_lock);
    ctrl->state = ret ? OFF_STATE : ON_STATE;
    mutex_unlock(&ctrl->state_lock);

    return ret;
}

board_added完成了pciehp_power_on_slot,pciehp_set_indicators,pciehp_configure_device (Off -> Blink -> On)

static int board_added(struct controller *ctrl)
{
	int retval = 0;
	struct pci_bus *parent = ctrl->pcie->port->subordinate;

	if (POWER_CTRL(ctrl)) {
		/* Power on slot */
		retval = pciehp_power_on_slot(ctrl);
		if (retval)
			return retval;
	}

	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
			      INDICATOR_NOOP);

	/* Check link training status */
	retval = pciehp_check_link_status(ctrl);
	if (retval)
		goto err_exit;

	/* Check for a power fault */
	if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
		ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
		retval = -EIO;
		goto err_exit;
	}

	retval = pciehp_configure_device(ctrl);
	if (retval) {
		if (retval != -EEXIST) {
			ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
				 pci_domain_nr(parent), parent->number);
			goto err_exit;
		}
	}

	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
			      PCI_EXP_SLTCTL_ATTN_IND_OFF);
	return 0;

err_exit:
	set_slot_off(ctrl);
	return retval;
}
int pciehp_power_on_slot(struct controller *ctrl)
{
	struct pci_dev *pdev = ctrl_dev(ctrl);
	u16 slot_status;
	int retval;

	/* Clear power-fault bit from previous power failures */
	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;

	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,
		 PCI_EXP_SLTCTL_PWR_ON);

	retval = pciehp_link_enable(ctrl);
	if (retval)
		ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);

	return retval;
}

Surprise Remove Flow

需要处理AER和DPC events,然后处理hot-plug events。中断触发pciehp_handle_presence_or_link_change,然后pciehp_disable_slot会调用remove_board(通过__pciehp_disable_slot),remove_board主要完成pciehp_power_off_slot和pciehp_set_indicators (On -> Off)。Presense detected需要等待DPC先触发。

static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
{
	int ret;

	pm_runtime_get_sync(&ctrl->pcie->port->dev);
	ret = __pciehp_disable_slot(ctrl, safe_removal);
	pm_runtime_put(&ctrl->pcie->port->dev);

	mutex_lock(&ctrl->state_lock);
	ctrl->state = OFF_STATE;
	mutex_unlock(&ctrl->state_lock);

	return ret;
}
static void remove_board(struct controller *ctrl, bool safe_removal)
{
	pciehp_unconfigure_device(ctrl, safe_removal);

	if (POWER_CTRL(ctrl)) {
		pciehp_power_off_slot(ctrl);

		/*
		 * 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);

		/* Ignore link or presence changes caused by power off */
		atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
			   &ctrl->pending_events);
	}

	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
			      INDICATOR_NOOP);
}
void pciehp_power_off_slot(struct controller *ctrl)
{
	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,
		 PCI_EXP_SLTCTL_PWR_OFF);
}

NPEM Flow

NPEM(Native PCIe Enclosure Management) 是PCIe标准为NVMe设备定义的扩展LED控制功能,热插拔的LED控制机制由NPEM完成。可以实现在DP内或UP内,用于控制和更新LED状态。软件通过写入NPEM控制寄存器来发出NPEM指令,NPEM控制器根据指令更新LED状态,并使用completed机制指示软件完成。
在这里插入图片描述
NPEM支持实现OK/Locate/Fail/Rebuild等可选LED状态。
在这里插入图片描述
Linux Reference:
drivers/pci/hotplug /pciehp_hpc.c
drivers/pci/hotplug/pciehp_ctrl.c

PCIE-DMA是一种基于PCIe接口的直接内存访问技术,能够实现高速数据传输。在我的学习笔记中,我详细记录了与PCIE-DMA相关的知识和学习心得。 首先,我了解到PCIE-DMA技术的基本原理和作用。PCIE-DMA可以通过PCIe总线直接访问系统内存中的数据,而不需要过多的CPU干预,提高数据传输的速度和效率。这种技术在需要大量数据传输的场景下非常有效,比如高性能计算、数据采集等。 其次,我深入学习了PCIE-DMA的工作原理。PCIE-DMA的核心是DMA控制器,它负责管理和控制数据传输的流程。当设备需要读写内存中的数据时,它通过DMA控制器发送请求,然后DMA控制器生成一个事务,将数据直接传输到或从内存中。这样就大大减少了CPU的参与,提高了数据传输的效率。 另外,我还学习了PCIE-DMA的配置和编程方法。PCIE-DMA的配置主要包括硬件配置和软件配置两个部分。硬件配置通常涉及到DMA控制器和PCIe接口的初始化和配置,软件配置则需要编写驱动程序来驱动DMA控制器和处理数据传输过程中的事件和异常。这部分内容对于我来说还比较新颖,需要更多的实践和实践。 最后,我总结了PCIE-DMA的应用场景和发展前景。PCIE-DMA在高性能计算、数据采集等领域具有广阔的应用前景。随着数据量的不断增加和传输速度的要求越来越高,PCIE-DMA技术的需求也将越来越大。因此,对于我来说,学习掌握PCIE-DMA技术非常有价值。 通过学习和记录PCIE-DMA的相关知识和经验,我对这项技术有了更深入的理解和掌握。希望将来能通过应用PCIE-DMA技术解决实际问题,为科研和工程项目的顺利进行做出贡献。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小破同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值