[Controlador de Linux] Introducción al controlador pcie

El bus pcie es compatible con versiones anteriores del bus PCI, y el PCI en el texto es igual a pcie por defecto

topología pcie

Inserte la descripción de la imagen aquí
La topología pcie se compone principalmente de un bus, un puente y una tarjeta de dispositivo. El puente conecta el bus principal y el bus secundario para formar una estructura de árbol. Los puentes se dividen principalmente en las siguientes tres categorías:

  • Puente Host / PCI: se utiliza para conectar la CPU y el bus raíz PCI. En la PC, el controlador de memoria también suele estar integrado en el chip del dispositivo de puente Host / PCI. El puente Host / PCI generalmente se denomina "Conjunto de chips North Bridge" .
  • Puente PCI / ISA: se utiliza para conectar el bus ISA antiguo. El puente PCI / ISA también se conoce como el "conjunto de chips South Bridge".
  • Puente de PCI a PCI: se utiliza para conectar el bus primario PCI y el bus secundario.
    Inserte la descripción de la imagen aquí
    El número de bus PCI adopta el orden transversal de profundidad primero (DFS)
    Inserte la descripción de la imagen aquí

espacio de direcciones pcie

pcie consta de tres espacios de direcciones, a saber: PCI配置空间, PCI/IO空间yPCI内存地址空间

PCI配置空间 Puede entenderse como el registro de configuración de la tarjeta del dispositivo pcie. Este registro de configuración tiene una determinada especificación. El tamaño del protocolo PCI es de 256 bytes, y el protocolo PCI se puede extender a 4K. Los primeros 64 bytes se solidifican en el dispositivo y no se pueden leer ni escribir, y las otras partes se pueden leer y escribir. El método para acceder al registro de configuración es usar el puente norte, que usa la dirección de 0xCF8 ~ 0xCFF para la configuración de la tarjeta pci. Entre ellos, escriba la identificación de la tarjeta PCI a la que se accederá en 0xCF8 ~ 0xCFC. La identificación se compone de número de bus de imagen + número de dispositivo + número de función + número de registro. El formato específico es como se muestra en la figura siguiente. Después de seleccionar la tarjeta y registrarse en el paso anterior, lea y escriba 0xCFC ~ 0xCFF, y North Bridge enviará una transacción de configuración para completar la configuración del registro.
Inserte la descripción de la imagen aquí

PCI内存地址空间 Puede entenderse como el registro de datos del dispositivo pci, que se determina cuando se diseña el dispositivo. Este espacio se registra en la PCI配置空间primera información de 64k. El sistema operativo solo necesita asignar esta dirección a la memoria para acceder a ella.

PCI/IO空间 Puede entenderse como el espacio direccionable del dispositivo pcie. El espacio direccionable del bus PCI con 32 bits es de 4 GB, por lo PCI内存地址空间que no puede ser superior a 4 GB

espacio de configuración pcie

Mire el formato del registro de configuración de pcie en detalle como se muestra a continuación:
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí

  1. Id. De dispositivo e Id. De Verdor. Asignados por el fabricante, solo lectura.
  2. ID de revisión. Registra el número de versión del dispositivo PCI, solo lectura
  3. Código de clase: registra la clasificación de dispositivos PCI, de solo lectura. Se divide en tres campos: código de clase base (divide el dispositivo en tarjeta gráfica, tarjeta de red, puente PCI, etc.), código de subclase (subdivide) e interfaz (define la interfaz de programación). El software del sistema puede utilizar este registro para identificar la clasificación actual del dispositivo PCI. La escritura de controladores por tipo depende de esta distinción.
  4. Tipo de encabezado: indica el tipo de espacio de configuración actual, solo lectura.
  5. Tamaño de la línea de caché La longitud de la línea de caché utilizada por el procesador HOST la establece el software del sistema. (No significativo para PCIe
  6. ID del subsistema e ID del proveedor del subsistema. Cuando no se pueden distinguir el ID del dispositivo y el ID del proveedor
  7. Dirección base de la ROM de expansión Registra la dirección base del programa ROM. Algunos dispositivos PCI necesitan completar la configuración básica de inicialización antes de que el procesador ejecute el sistema operativo.Los dispositivos PCI proporcionan un programa ROM y el procesador ejecutará este programa ROM durante el proceso de inicialización para inicializar estos dispositivos PCI.
  8. Puntero de capacidades En los dispositivos PCI, los registros son opcionales y deben ser compatibles con los dispositivos PCI-X y PCIe.
  9. Línea de interrupción. Se escribe cuando el software del sistema configura el dispositivo PCI y registra el número de vector de interrupción utilizado por el dispositivo PCI actual. Si el controlador de interrupción 8259A no es aplicable, este registro no tiene significado
  10. Pin de interrupción. Utilice ese pin como una interrupción. PCI tiene 4 pines de interrupción, que pueden ser cualquiera
  11. Registro de dirección base 0 ~ 5. Guarde la dirección base del espacio de direcciones utilizado por el dispositivo PCI y guarde la dirección del dispositivo en el dominio del bus PCI.

De hecho, existen tres tipos de registros de configuración:

  • Espacio de configuración utilizado por el agente PCI
  • Espacio de configuración utilizado por el puente PCI
  • El espacio de configuración utilizado por el puente Cardbus
    es el registro del agente. Echemos un vistazo al puente siguiente: el
    Inserte la descripción de la imagen aquí
    puente PCI tiene dos conjuntos de registros BAR. Si el puente PCI en sí no tiene registros privados, entonces los registros BAR no pueden ser utilizado (puente transparente) e inicializado a 0.

En comparación con el espacio de configuración de PCI Agent, el espacio de configuración de PCI Bridge tiene más registros de número de bus

  1. El registro de número de bus subordinado almacena el número de bus PCI con el número más alto en el subárbol PCI actual
  2. El número de bus secundario almacena el número de bus utilizado por el bus secundario del puente PCI actual, y también es el número de bus con el número más pequeño en el subárbol.
  3. Primary Bus Number almacena el número de bus PCI aguas arriba del puente PCI

API del kernel de Linux

Leer y escribir el registro de configuración del dispositivo pci

pci_read_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value);
pci_write_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value);
  • 1
  • 2

Obtenga la dirección inicial de la dirección base en la lista de registros de configuración, donde bar es la dirección, hay seis en total

pci_resource_start(dev, bar) /*Bar值的范围为0-5*/
  • 1

Obtenga la dirección final de la dirección base en la lista de registro de configuración

pci_resource_end(dev, bar) /*Bar值的范围为0-5*/
  • 1

Obtenga el estado de la dirección base en la lista de registro de configuración, todavía hay muchos estados, consulte ioport.h para obtener más detalles.

pci_resource_flags(dev, bar) /*Bar值的范围为0-5*/
  • 1

Obtenga la longitud de la dirección de la dirección base en la lista de registros de configuración, que también es igual apci_resource_end((dev), (bar))-pci_resource_start((dev), (bar)) + 1)

pci_resource_len(dev,bar) /*Bar值的范围为0-5*/
  • 1

Solicite o libere recursos

int pci_request_regions(struct pci_dev *pdev, const char *res_name);
void pci_release_regions(struct pci_dev *pdev);
  • 1
  • 2

Habilitar o deshabilitar el dispositivo

int pci_enable_device(struct pci_dev *pdev);
int pci_disable_device(struct pci_dev *pdev);
  • 1
  • 2

Establecer como dispositivo maestro

void pci_set_master(struct pci_dev *pdev);
  • 1

Encontrar dispositivo

struct pci_dev *pci_find_slot (unsigned int bus,unsigned int devfn);
  • 1

Establecer estado de energía

int pci_set_power_state(struct pci_dev *pdev, pci_power_t state);
  • 1

Función de búsqueda

int pci_find_capability(struct pci_dev *pdev, int cap);
  • 1

Habilitar inhabilitar inhabilitar escritura

int pci_set_mwi(struct pci_dev *pdev);
void pci_clear_mwi(struct pci_dev *pdev);
  • 1
  • 2

Hola Mundo

El marco de un dispositivo pci es similar al de un bus, y también es inseparable. Consiste aproximadamente en los siguientes pasos:

1. Registro de conductor:

  • Compruebe si el hardware admite la imagen

2. Adaptación de la unidad:

  • Encienda el dispositivo pci
  • Registro de configuración
  • Leer la dirección asignada
  • Inicialice dispositivos de caracteres, dispositivos de red, USB, etc.

3. Leer y escribir del dispositivo pci

pci_devLa estructura se corresponde básicamente con el contenido del registro de configuración.

struct pci_dev {
    struct list_head global_list;
    struct list_head bus_list;
    struct pci_bus  *bus;
    struct pci_bus  *subordinate;
    void        *sysdata;
    struct proc_dir_entry *procent;
    unsigned int    devfn;
    unsigned short  vendor;
    unsigned short  device;
    unsigned short  subsystem_vendor;
    unsigned short  subsystem_device;
    unsigned int    class;
    u8      hdr_type;
    u8      rom_base_reg;
    struct pci_driver *driver;
    void        *driver_data;
    u64     dma_mask;
    u32             current_state;
    unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
    unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
    unsigned int    irq;
    struct resource resource[DEVICE_COUNT_RESOURCE];
    struct resource dma_resource[DEVICE_COUNT_DMA];
    struct resource irq_resource[DEVICE_COUNT_IRQ];
    char        name[80];
    char        slot_name[8];
    int     active;
    int     ro;
    unsigned short  regs;
    int (*prepare)(struct pci_dev *dev);
    int (*activate)(struct pci_dev *dev);
    int (*deactivate)(struct pci_dev *dev);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • dieciséis
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

Presentación general

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");


/* 对特定PCI设备进行描述的数据结构 */
struct hello_card {
    unsigned int magic;
    /* 使用链表保存所有同类的PCI设备 */
    struct hello_card *next;
}

static int hello_ioctl(struct inode *inode, struct file *file,
      unsigned int cmd, unsigned long arg)
{
    switch(cmd) {
        case hello_RDATA:
            /* 从I/O端口读取4字节的数据 */
            val = inl(card->iobae + 0x10);
/* 将读取的数据传输到用户空间 */
    }
    return 0;
}

static int hello_open(struct inode *inode, struct file *file)
{
    /* 申请中断,注册中断处理程序 */
    request_irq(card->irq, &hello_interrupt, SA_SHIRQ,
        card_names[pci_id->driver_data], card)) {
    /* 检查读写模式 */
    if(file->f_mode & FMODE_READ) {
        /* ... */
    }
    if(file->f_mode & FMODE_WRITE) {
       /* ... */
    }
     
    /* 申请对设备的控制权 */
    down(&card->open_sem);
    while(card->open_mode & file->f_mode) {
        if (file->f_flags & O_NONBLOCK) {
            /* NONBLOCK模式,返回-EBUSY */
            up(&card->open_sem);
            return -EBUSY;
        } else {
            /* 等待调度,获得控制权 */
            card->open_mode |= f_mode & (FMODE_READ | FMODE_WRITE);
            up(&card->open_sem);
            /* 设备打开计数增1 */
            MOD_INC_USE_COUNT;
            /* ... */
        }
    }
}

static void hello_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    struct hello_card *card = (struct hello_card *)dev_id;
    u32 status;
    spin_lock(&card->lock);
    /* 识别中断 */
    status = inl(card->iobase + GLOB_STA);
    if(!(status & INT_MASK)) 
    {
        spin_unlock(&card->lock);
        return;  /* not for us */
    }
    /* 告诉设备已经收到中断 */
    outl(status & INT_MASK, card->iobase + GLOB_STA);
    spin_unlock(&card->lock);
     
    /* 其它进一步的处理,如更新DMA缓冲区指针等 */
}

static int hello_release(struct inode *inode, struct file *file)
{
    /* 释放对设备的控制权 */
    card->open_mode &= (FMODE_READ | FMODE_WRITE);
     
    /* 唤醒其它等待获取控制权的进程 */
    wake_up(&card->open_wait);
    up(&card->open_sem);
     
    /* 释放中断 */
    free_irq(card->irq, card);
     
    /* 设备打开计数增1 */
    MOD_DEC_USE_COUNT;
}

static struct file_operations hello_fops = {
    owner:      THIS_MODULE,
    read:       hello_read,
    write:      hello_write,
    ioctl:      hello_ioctl,
    open:       hello_open,
    release:    hello_release,
};


static int __init hello_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
    struct hello_card *card;
    /* 启动PCI设备 */
    if (pci_enable_device(pci_dev))
        return -EIO;
    /* 设备DMA标识 */
    if (pci_set_dma_mask(pci_dev, hello_DMA_MASK)) {
        return -ENODEV;
    }
    /* 在内核空间中动态申请内存 */
    if ((card = kmalloc(sizeof(struct hello_card), GFP_KERNEL)) == NULL) {
        printk(KERN_ERR "pci_hello: out of memory\n");
        return -ENOMEM;
    }
    memset(card, 0, sizeof(*card));
    /* 读取PCI配置信息 */
    card->iobase = pci_resource_start(pci_dev, 1);
    card->pci_dev = pci_dev;
    card->pci_id = pci_id->device;
    card->irq = pci_dev->irq;
    card->next = devs;
    card->magic = hello_CARD_MAGIC;
    /* 设置成总线主DMA模式 */
    pci_set_master(pci_dev);
    /* 申请I/O资源 */
    request_region(card->iobase, 64, card_names[pci_id->driver_data]);
    return 0;
}

static struct pci_device_id hello_pci_tbl [] __initdata = {
    {PCI_VENDOR_ID_hello, PCI_DEVICE_ID_hello,
     PCI_ANY_ID, PCI_ANY_ID, 0, 0, hello},
    {0,}
};

static struct pci_driver hello_pci_driver = {
    name:       hello_MODULE_NAME,
    id_table:   hello_pci_tbl,
    probe:      hello_probe,
    remove:     hello_remove,
};

static int __init hello_init_module (void)
{
    //检查系统是否支持PCI总线
    if (!pci_present())
        return -ENODEV;

    if (!pci_register_driver(&hello_pci_driver)) {
        pci_unregister_driver(&hello_pci_driver);
        return -ENODEV;
    }
    return 0;
}

static void __exit hello_cleanup_module (void)
{
    pci_unregister_driver(&hello_pci_driver);
}

module_init(hello_init_module);
module_exit(hello_cleanup_module)

Supongo que te gusta

Origin blog.csdn.net/star871016/article/details/112990144
Recomendado
Clasificación