linux下 usb 和pci设备的reset
1. 什么是设备的reset
设备的reset是为了让设备的寄存器恢复初始状态
设备的寄存器
设备的寄存器分为两种,一种是不可改变的,静态寄存器;一种是可改变的,动态寄存器。
静态寄存器的恢复初始状态只能通过完全断电来完成
动态寄存器的恢复初始状态可以通过向reset寄存器写值,来完成动态寄存器的恢复。
上面两种reset 分别叫做cold reset 和 hot reset。
设备的结构体
reset和设备的新发现是完全不一样的,其本质在于reset会保持为这个设备分配的资源不变,包含为这个设备生成的一系列结构体和分配的内存空间等,只是重置动态寄存器。
usb device reset
开门见山, usb 设备的reset是通过对usb设备所连接的hub 的port 重新init来完成的,重新枚举了一次usb设备。
函数调用顺序:usb_reset_device->usb_reset_and_verify_device->hub_port_init->hub_port_reset
pci device reset
pci 设备的reset 比较复杂,主要是因为pci device 有很多不同层次的reset,它会一个个尝试,程度从浅到深,哪个层次的reset成功就直接返回,不会执行更深层次的reset。
pci 设备的function, slot, bus
pci设备可以是个复合设备,比如usb2.0 主机控制器,就是一个usb1.1 主机控制器和一个usb2.0 主机控制器集成在一起,成了一个复合设备。每个主控是一个function,合在一起成了一个device,对与linux内核来说,就是两个设备。
因此对pci设备的复位,其实对pci function的复位。对整个device reset,就需要复位这个device所在pci插槽进行复位,即对slot进行复位。
如果一个pci设备是一个pci bridge,那么对这个设备的复位,必然会导致对连在这个bridge(bus)上所有设备reset。
pci function reset
函数的调用堆栈是
pci_reset_function->__pci_reset_function_locked
__pci_reset_function_locked 是重点
int __pci_reset_function_locked(struct pci_dev *dev)
{
int rc;
might_sleep();
/*
* A reset method returns -ENOTTY if it doesn't support this device
* and we should try the next method.
*
* If it returns 0 (success), we're finished. If it returns any
* other error, we're also finished: this indicates that further
* reset mechanisms might be broken on the device.
*/
rc = pci_dev_specific_reset(dev, 0);
if (rc != -ENOTTY)
return rc;
if (pcie_has_flr(dev)) {
rc = pcie_flr(dev);
if (rc != -ENOTTY)
return rc;
}
rc = pci_af_flr(dev, 0);
if (rc != -ENOTTY)
return rc;
rc = pci_pm_reset(dev, 0);
if (rc != -ENOTTY)
return rc;
rc = pci_dev_reset_slot_function(dev, 0);
if (rc != -ENOTTY)
return rc;
return pci_parent_bus_reset(dev, 0);
}
EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
pci_dev_specific_reset
pcie_flr
pci_af_flr
pci_pm_reset
pci_dev_reset_slot_function
pci_parent_bus_reset
上面的都是function的重置函数,只要满足一个就可以直接返回,其程度逐渐加深
pci_dev_specific_reset 是驱动工程师为这款设备,写的特定的reset方式
pcie_has_flr 设备是否与FLR 能力,就是读取Function Level Reset Capability
pci_pm_reset 让设备进入D3 hot 再返回D0
pci_dev_reset_slot_function 调用pci_reset_hotplug_slot,重置slot,该函数会重置整个pci device,而不只pci function
pci_parent_bus_reset 调用 pci_bridge_secondary_bus_reset,会修改该pci device 所连接的pci briage的配置空间,让他reset下游的pci device。