linux下pci总线的驱动模型
作者:互联网
linux下的pci驱动关心两个参数
vid(厂商ID) 和did(设备ID)
进一步通过lspci -n
详细的解释如下
zw@zw-pc:~$ lspci -nn |grep NVI
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)
输出值 | 含义 | 详细解释 |
---|---|---|
"01:00.0" 和 “01::00.1” | 以 ”bus:slot.func“ 格式来唯一标识一个 PCI 功能设备 | 唯一定位一个 PCI 设备的虚拟功能,可以是一个物理设备,也可以是一个多功能设备的功能设备,一个多功能设备可以最多有8个功能。 总线号(bus): 从系统中的256条总线中选择一条,0--255。 设备号(slot): 在一条给定的总线上选择32个设备中的一个。0--31。功能号(func): 选择多功能设备中的某一个功能,有八种功能,0--7。 PCI规范规定,功能0是必须实现的。 |
”0300“ | PCI 设备类型 | 指 PCI 设备的类型,来自不同厂商的同一类设备的类型码可以是相同的。 |
“10de” | 供应商识别字段(Vendor ID) | 该字段用一标明设备的制造者。一个有效的供应商标识由 PCI SIG 来分配,以保证它的唯一性。Intel 的 ID 为 0x8086,Nvidia 的 ID 为 0x10de |
“1c03” | 设备识别字段(Device ID) | 用以标明特定的设备,具体代码由供应商来分配。本例中表示的是 GPU 图形卡的设备 ID。 |
“a1” | 版本识别字段(Revision ID) | 用来指定一个设备特有的版本识别代码,其值由供应商提供 |
在PCIE配置空间里,0x10开始后面有6个32位的BAR寄存器,BAR寄存器中存储的数据是表示PCIE设备在PCIE地址空间中的基地址,注意这里不是表示PCIE设备内存在CPU内存中的映射地址,关于这两者的关系以及两者如何转换后面会有介绍。
其中Type0 Header最多有6个BAR,而Type1 Header最多有两个BAR。这就意味着,对于Endpoint来说,最多可以拥有6个不同的地址空间。但是实际应用中基本上不会用到6个,通常1~3个BAR比较常见。
主要注意的是,如果某个设备的BAR没有被全部使用,则对应的BAR应被硬件全被设置为0,并且告知软件这些BAR是不可以操作的。对于被使用的BAR来说,其部分低比特位是不可以被软件操作的,只有其高比特位才可以被软件操作。而这些不可操作的低比特决定了当前BAR支持的操作类型和可申请的地址空间的大小。
一旦BAR的值确定了(Have been programmed),其指定范围内的当前设备中的内部寄存器(或内部存储空间)就可以被访问了。当该设备确认某一个请求(Request)中的地址在自己的BAR的范围内,便会接受这请求。
驱动的框架模型
#define BAR_NUM 6
struct xxx_dev
{
struct pci_dev *pci_dev;
....
};
static int get_map_bars ( struct xxx_dev *priv, struct pci_dev *dev )
{
int i;
for ( i = 0; i < BAR_NUM; i++ )
{
unsigned long bar_start = pci_resource_start ( dev, i );
priv->bar_length[i] = pci_resource_len ( dev, i );
if ( !priv->bar_length[i] )
{
priv->bar[i] = NULL;
continue;
}
priv->bar[i] = pci_iomap ( dev, i, 0 );
if ( !priv->bar[i] )
{
dev_err ( &dev->dev, "could not map BAR[%d]", i );
return -1;
}
else
{
drv_printk ( "BAR[%d] mapped to 0x%p, length %lu \r\n", i, priv->bar[i], ( long unsigned int ) priv->bar_length[i] );
}
}
xxx_regs = priv->bar[0];//根据自己的设备来定
return 0;
}
static int pcie_scan_bars ( struct xxx_dev *priv, struct pci_dev *dev )
{
int i;
for ( i = 0; i < BAR_NUM; i++ )
{
unsigned long bar_start = pci_resource_start ( dev, i );
unsigned long bar_end = pci_resource_end ( dev, i );
unsigned long bar_flags = pci_resource_flags ( dev, i );
priv->bar_length[i] = pci_resource_len ( dev, i );
drv_printk ( "BAR[%d] 0x%08lx-0x%08lx flags 0x%08lx, length %d \r\n", i, bar_start, bar_end, bar_flags, ( int ) priv->bar_length[i] );
}
return 0;
}
static int pcie_xxx_probe ( struct pci_dev *dev, const struct pci_device_id *id )
{
struct xxx_dev *priv;
int ret;
printk ( "%s enter.\n", __func__ );
/* Allocate private data */
priv = devm_kzalloc ( &dev->dev, sizeof ( struct xxx_dev ), GFP_KERNEL );
if ( !priv )
{
dev_err ( &dev->dev, "Unable to allocate device private data\n" );
return -ENOMEM;
}
priv->pci_dev = pci_dev_get ( dev );
....
pci_set_drvdata ( dev, priv );
ret = pci_enable_device ( dev );
if ( ret )
{
goto out_deregister;
}
pci_set_master ( dev );
pci_read_config_byte ( dev, PCI_REVISION_ID, &priv->revision );
pci_read_config_byte ( dev, PCI_INTERRUPT_PIN, &priv->irq_pin );
pci_read_config_byte ( dev, PCI_INTERRUPT_LINE, &priv->irq_line );
drv_printk ( "irq pin: %d\n", priv->irq_pin );
drv_printk ( "irq line: %d\n", priv->irq_line );
drv_printk ( "revision: %d\n", priv->revision );
pcie_scan_bars ( priv, dev );
get_map_bars ( priv, dev );//获取基地址
....
return ret;
}
static int pcie_xxx_remove ( struct pci_dev *dev )
{
struct xxx_dev *priv = pci_get_drvdata ( dev );
printk ( "%s enter.\n", __func__ );
....
printk ( "pcie_fpga_remove success.\n" );
return 0;
}
static struct pci_device_id pcie_xxx_ids[] =
{
{ PCI_DEVICE ( xxx_VID, xxx_DID ) },
{ 0 }
};
static struct pci_driver pcie_xxx_driver =
{
.name = "fsl, pcie_xxx",
.id_table = pcie_xxx_ids,
.probe = pcie_xxx_probe,
.remove = pcie_xxx_remove,
};
static int __init pcie_xxx_init ( void )
{
int rc = 0;
rc = pci_register_driver ( &pcie_xxx_driver );
if ( rc )
{
printk ( " PCI driver registration failed\n" );
}
return rc;
}
static void __exit pcie_xxx_exit ( void )
{
pci_unregister_driver ( &pcie_xxx_driver );
}
module_init ( pcie_xxx_init );
module_exit ( pcie_xxx_exit );
MODULE_LICENSE ( "GPL" );
MODULE_AUTHOR ( "tccxy <xxx@xxx.com>" );
MODULE_DESCRIPTION ( "xxx driver" );
标签:BAR,xxx,总线,dev,pcie,pci,linux,priv 来源: https://www.cnblogs.com/tccxy/p/16194393.html