Jump to content

    
Sign in to follow this  
doom13

PCIe Driver

Recommended Posts

Приветствую.

Есть плата с Virtex 7, надо забросить данные в ПК по PCIe. Ранее такого не делал и есь трудности.

В FPGA собрана система: GPIO, Timer, PCIe Bridge (регистры GPIO и таймера смапированы на адресное пространство BAR0). Разбираюсь в написании модулей ядра Linux (использую Ubuntu 15.04).

Пока реализовал управление GPIO при загрузке/выгрузке модуля в Linux. Хочу добавить прерывание. В FPGA линия interrupt от таймера заводится на PCIe-мост, он должен запихнуть его в систему.

Для настройки прерывания в драйвере использую:

irq_handler_t timer_isr(unsigned int irq, void *dev);
struct pci_dev *pdev;
char irq;
...
pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq ); // для моей платы получаю irq = 11
request_irq(irq_num, timer_isr, IRQF_TRIGGER_RISING, "PCIe INT", (void *)pdev);
...

 

Функция pci_read_config_byte() установит irq = 11.

 

Далее настраиваю таймер, чтобы генерировал прерывание и стартую его. В syslog вижу следующее сообщение

Jul 29 10:57:49 user-pc kernel: [ 797.188154] irq 16: nobody cared (try booting with the "irqpoll" option)

Jul 29 10:57:49 user-pc kernel: [ 797.188158] CPU: 7 PID: 0 Comm: swapper/7 Tainted: G OE 3.19.0-25-generic #26-Ubuntu

Jul 29 10:57:49 user-pc kernel: [ 797.188159] Hardware name: Gigabyte Technology Co., Ltd. To be filled by O.E.M./Z77P-D3, BIOS F7 08/24/2012

Jul 29 10:57:49 user-pc kernel: [ 797.188160] ffff880408dc92a4 ffff88041edc3e28 ffffffff817c4518 0000000000040400

Jul 29 10:57:49 user-pc kernel: [ 797.188162] ffff880408dc9200 ffff88041edc3e58 ffffffff810d0e86 ffff88041edc3e88

Jul 29 10:57:49 user-pc kernel: [ 797.188164] ffff880408dc9200 0000000000000000 0000000000000010 ffff88041edc3e98

Jul 29 10:57:49 user-pc kernel: [ 797.188165] Call Trace:

Jul 29 10:57:49 user-pc kernel: [ 797.188166] <IRQ> [<ffffffff817c4518>] dump_stack+0x45/0x57

Jul 29 10:57:49 user-pc kernel: [ 797.188174] [<ffffffff810d0e86>] __report_bad_irq+0x36/0xd0

Jul 29 10:57:49 user-pc kernel: [ 797.188175] [<ffffffff810d1237>] note_interrupt+0x267/0x2b0

Jul 29 10:57:49 user-pc kernel: [ 797.188177] [<ffffffff810ce8c3>] handle_irq_event_percpu+0x133/0x1a0

Jul 29 10:57:49 user-pc kernel: [ 797.188179] [<ffffffff810ce971>] handle_irq_event+0x41/0x70

Jul 29 10:57:49 user-pc kernel: [ 797.188181] [<ffffffff810d1666>] handle_fasteoi_irq+0x86/0x140

Jul 29 10:57:49 user-pc kernel: [ 797.188182] [<ffffffff81017772>] handle_irq+0x22/0x40

Jul 29 10:57:49 user-pc kernel: [ 797.188184] [<ffffffff817ce55f>] do_IRQ+0x4f/0xf0

Jul 29 10:57:49 user-pc kernel: [ 797.188186] [<ffffffff817cc36d>] common_interrupt+0x6d/0x6d

Jul 29 10:57:49 user-pc kernel: [ 797.188186] <EOI> [<ffffffff816663b5>] ? cpuidle_enter_state+0x65/0x160

Jul 29 10:57:49 user-pc kernel: [ 797.188190] [<ffffffff816663a1>] ? cpuidle_enter_state+0x51/0x160

Jul 29 10:57:49 user-pc kernel: [ 797.188192] [<ffffffff81666597>] cpuidle_enter+0x17/0x20

Jul 29 10:57:49 user-pc kernel: [ 797.188195] [<ffffffff810b7ce1>] cpu_startup_entry+0x311/0x3b0

Jul 29 10:57:49 user-pc kernel: [ 797.188198] [<ffffffff81049107>] start_secondary+0x197/0x1c0

Jul 29 10:57:49 user-pc kernel: [ 797.188199] handlers:

Jul 29 10:57:49 user-pc kernel: [ 797.188202] [<ffffffff815bb130>] usb_hcd_irq

Jul 29 10:57:49 user-pc kernel: [ 797.188204] Disabling IRQ #16

 

Получается PCIe мост при наличии прерывания от таймера выдаёт его на 16 линию. Для просмотра оборудования в системе установлен System Profiler, для моего устройства он показывает IRQ = 16 (рисунок).

Если пытаюсь использовать 16 линию прерывания в функции request_irq, она выдаёт ошибку.

request_irq(16, timer_isr, IRQF_TRIGGER_RISING, "PCIe INT", (void *)pdev);

Вопрос, что я делаю не так? Почему в конфигурационной области моего устройства записано 11 (Interrupt Line), а прерывание срабатывает на 16 линии?

 

Код модуля:

/* Necessary includes for device drivers */
#include <linux/init.h>
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>		/* everything... */
#include <linux/errno.h>	/* error codes */
#include <linux/types.h>	/* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>	/* O_ACCMODE */
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/interrupt.h>
//#include <asm/system.h>		/* cli(), *_flags */
#include <asm/uaccess.h>	/* copy_from/to_user */

#include "hardware.h"
#include "timer.h"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Andrei Hres");
MODULE_DESCRIPTION("Driver for /dev/virtex7_pcie");
MODULE_SUPPORTED_DEVICE("virtex7_pcie");

#define __DEBUG_MODE
#define DRV_NAME					"virtex7_pcie"

#define BAR0						0
#define BAR1						1
#define BAR2						2
#define BAR3						3
#define BAR4						4
#define BAR5						5

int virtex7_pcie_init(void);
void virtex7_pcie_exit(void);
int virtex7_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
void virtex7_pcie_remove(struct pci_dev *pdev);

irq_handler_t timer_isr(unsigned int irq, void *dev);
int irq_status;
char irq_num;

char *bar0_base;
unsigned long *pio_base;

static struct pci_device_id virtex7_pcie_ids[] = { {PCI_DEVICE(0x10EE, 0x7014)}, {0,} };

MODULE_DEVICE_TABLE(pci, virtex7_pcie_ids);

static struct pci_driver virtex7_pcie_driver = {
.name           = DRV_NAME,
.probe          = virtex7_pcie_probe,
.remove         = virtex7_pcie_remove,
.id_table       = virtex7_pcie_ids,
//#ifdef CONFIG_PM
//        .suspend        = pcie_suspend,
//        .resume         = pcie_resume,
//#endif /* CONFIG_PM */
};

//static int __init virtex7_pcie_init(void)
int virtex7_pcie_init(void)
{
printk(KERN_ALERT "*****************************************\n");
printk(KERN_ALERT "Inserting PCIe module\n");
return pci_register_driver(&virtex7_pcie_driver);
}

//static void __exit virtex7_pcie_exit(void)
void virtex7_pcie_exit(void)
{
printk(KERN_ALERT "Removing PCIe module\n");
pci_unregister_driver(&virtex7_pcie_driver);
printk(KERN_ALERT "*****************************************\n");
}

int virtex7_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int status;
int st;
unsigned long data;
char byte2;
unsigned long bar0_start;
unsigned long bar0_end;
unsigned long *pio_reg;

printk(KERN_ALERT "Entering PCIe probe() function\n");

status = pci_enable_device(pdev);

if(status == 0)
{
	printk(KERN_ALERT "PCIe device is succesfully enabled\n");
	printk(KERN_ALERT "vendor: 0x%.4X\n", pdev->vendor);
	printk(KERN_ALERT "device: 0x%.4X\n", pdev->device);

	pci_read_config_dword(pdev, 0, &data);
	printk(KERN_ALERT "Data from config space: 0x%.8X\n", data);
	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_num);
	printk(KERN_ALERT " PCI_INTERRUPT_LINE=%d\n", irq_num);

	//irq_num = 16;

	pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &byte2);
	printk(KERN_ALERT " PCI_INTERRUPT_PIN=%d\n", byte2);

	bar0_start = pci_resource_start(pdev, BAR0);
	bar0_end = pci_resource_end(pdev, BAR0);
	printk(KERN_ALERT "BAR0 start address: 0x%.8X\n", bar0_start);
	printk(KERN_ALERT "BAR0 end address: 0x%.8X\n", bar0_end);
	printk(KERN_ALERT "BAR0 size: %u bytes = %u kB\n", bar0_end-bar0_start, (bar0_end-bar0_start+1)/1024);

	//pio_reg = (unsigned long *) (bar0_start + 0x8000);
	//*pio_reg = 0xFFFFFFFF;

	//----------------------------------------------------------------

	st = pci_request_region(pdev, BAR0, "virtex7_pcie_bar0");
	if(st == 0)
	{
		printk(KERN_ALERT "Request region success\n");
		bar0_base = pci_iomap(pdev, BAR0, 0);
		printk(KERN_ALERT "bar0_base: 0x%.16X\n", (unsigned int) bar0_base);

		pio_base = (unsigned long *) (bar0_base + 32768);
		printk(KERN_ALERT "pio_base: 0x%.16X\n", (unsigned int) pio_base);

		//*(unsigned long *)(bar0_base + 32768) = 0x1;
		*pio_base = 0x1;

		Timer_Init(bar0_base + TIMER_0_OFFSET_BYTES);
		Timer_IntClear(bar0_base + TIMER_0_OFFSET_BYTES);
		data = Timer_DbgRdCSR(bar0_base + TIMER_0_OFFSET_BYTES);
		printk(KERN_ALERT "Timer CSR: 0x%.8X\n", (unsigned int) data);

//			free_irq(irq_num, (void *)pdev);
		irq_status = request_irq(irq_num, timer_isr, IRQF_TRIGGER_RISING/* | IRQF_SHARED*/, "PCIe INT", (void *)pdev);
		if(irq_status)
			printk(KERN_ALERT "request_irq() error\n");
		else
		{
			printk(KERN_ALERT "request_irq() success\n");
			Timer_Start(bar0_base + TIMER_0_OFFSET_BYTES);
		}

		Timer_IntClear(bar0_base + TIMER_0_OFFSET_BYTES);
		data = Timer_DbgRdCSR(bar0_base + TIMER_0_OFFSET_BYTES);
		printk(KERN_ALERT "Timer CSR: 0x%.8X\n", (unsigned int) data);
	}

	//pio_base = (unsigned char *) (addr_bar0 + 0x8000);
	//----------------------------------------------------------------
}

return status;
}

void virtex7_pcie_remove(struct pci_dev *pdev)
{
*pio_base = 0;


printk(KERN_ALERT "Disable PCIe device\n");
pci_disable_device(pdev);
pci_release_region(pdev, BAR0);

if(irq_status == 0)
{
	free_irq(irq_num, (void *)pdev);
	Timer_Stop(bar0_base + TIMER_0_OFFSET_BYTES);
	printk(KERN_ALERT "Free PCI interrupt line #%d\n", irq_num);
}
}

module_init(virtex7_pcie_init);
module_exit(virtex7_pcie_exit);

irq_handler_t timer_isr(unsigned int irq, void *dev)
{
static int int_counter = 0;

if(Timer_IntStatus(bar0_base + TIMER_0_OFFSET_BYTES))
{
	Timer_IntClear(bar0_base + TIMER_0_OFFSET_BYTES);
}

printk(KERN_ALERT "Interrupt counter %d\n", int_counter);
int_counter++;

return IRQ_NONE;
}

post-63539-1438155328_thumb.png

Share this post


Link to post
Share on other sites

Прерывание стало работать со следующими изменениями:

request_irq(pdev->irq, timer_isr, IRQF_SHARED, "PCIe INT", (void *)pdev); // pdev->irq = 16

, ранее использовался неверный флаг IRQF_TRIGGER_RISING и значение pdev->irq = 16.

Остался вопрос по номеру линии прерывания. В конфигурационной области PCIe моего устройства записано 11 (Interrupt Line), а прерывание срабатывает для линии №16.

Нашёл, что при старте системы плате назначена линия прерывания №11, через некоторое время линию прерывания №11 занимает другое устройство (SMBus Controller). Плате назначается линия прерывания №16, которую она делит с USB Controller-ом, но номер линии прерывания (Interrupt Line) в конфигурационной области PCIe не меняется (может и меняется но функция pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq )устанавливает переменную irq = 11)?

 

В struct pci_dev *pdev значение pdev->irq = 16, а если использовать pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq) получим irq = 11???

Share this post


Link to post
Share on other sites

Предположил, что раз система видит устройство как PCI Mouse Controller, то она и вешает его на туже линию прерывания, что и USB Controller.

Поменял настройки для PCIe Bridge (теперь класс устройства стал Base system peripherals), но результат одинаков (при старте системы повесило на 11 линию прерывания, после запуска модуля перекинуло на 16).

Как сделать так, чтоб моему устройству назначалась отдельная линия прерывания (в системе есть много свободных)?

 

Для мультимедиа-девайс всё аналогично.

Share this post


Link to post
Share on other sites

Насколько я понимаю вопрос. PCI имеет механизмы обработки прерываний даже если несколько устройств пользуется одним и тем же проводом для вызова прерывания. Много устройств прекрасно ладят используя общий провод для вызова прерываний.

 

Далее пойдут общие соображения и я не поручусь, что они точны, поскольку с PCI мало работал.

Прерывание физически присоединено к проводу и задется при изготовлении устройства. Можно изготовить устройство так, что есть возможность выбора. Надо найти документацию на устройство и узнать если есть такая возможность.

Share this post


Link to post
Share on other sites
Насколько я понимаю вопрос. PCI имеет механизмы обработки прерываний даже если несколько устройств пользуется одним и тем же проводом для вызова прерывания. Много устройств прекрасно ладят используя общий провод для вызова прерываний.

Да, так и есть - моё PCIe устройство делит Interrupt Line #16 c USB контроллером (при определении обработчика прерывания используется флаг IRQF_SHARED).

Но в системе куча свободных линий и хотелось бы повесить его на отдельную.

 

Далее пойдут общие соображения и я не поручусь, что они точны, поскольку с PCI мало работал.

Прерывание физически присоединено к проводу и задется при изготовлении устройства. Можно изготовить устройство так, что есть возможность выбора. Надо найти документацию на устройство и узнать если есть такая возможность.

А как тогда объяснить что при старте система назначает моему устройству Interrupt Line #11, а потом (загружаю свой модуль в ядро) перебрасывает его на Interrupt Line #16? Как я пока понял прерывания для PCIe работают на уровне Transaction Layer, т.е. устройство PCIe передаёт сигнал прерывания посредством сообщений.

Не совсем понимаю разницу между Legacy Interrupt, MSI Interrupt и MSI-X Interrupt, но вроде как речь везде идёт о транзакциях.

Посмотрю, что там может быть ещё железного.

Спасибо.

 

 

Раньше драйвер немного неправильно конфигурировал таймер - прерывание срабатывало очень часто. Мышь, клава и всё остальное, что висело на USB контроллере с Interrupt Line #16, жутко тормозили. Счас подправил функции настройки таймера и он отлично делит 16 линию прерывания с USB контроллером.

Вопрос, как происходит назначение устройству линии прерывания, остаётся актуальным. Буду благодарен, если кто подскажет.

Share this post


Link to post
Share on other sites

Попробовал поменять местами видеокарту и мою плату с Virtex 7, номера линий прерываний назначаемые устройствам сохранились (у видеокарты было и осталось Interrupt Line 30, для моей платы - 16)???

Share this post


Link to post
Share on other sites

Как правильно со стороны ядра выделить память, чтобы моё устройство могло писать в неё?

 

Пока сделал так:

pc_buffer = kmalloc(pc_buffer_size, GFP_KERNEL); // выделяю буфер
pc_buffer_phys = virt_to_phys(pc_buffer); // получаю физический адрес выделенной памяти

 

Полученный физический адрес памяти (32 бита) записываю в регистр PCIe моста в Virtex 7 (старшие 32 бита записываю 0 т.к. возвращаемый адрес 32 бита). Пытаюсь из Xilinx SDK получить доступ к выделенному адресному пространству (через мост). Иногда всё проходит нормально, вижу память выделенную драйвером Linux (предварительно установил все байты буфера в определённое значение). Могу изменить из Xilinx SDK данные в выделенном буфере (в прерывании драйвер мониторит содержимое участка памяти буфера, и могу видеть, что данные меняются). Иногда всё работает как-то криво, либо вообще не могу видеть буфер памяти со стороны FPGA, либо оно подключилось к какому-то другому участку памяти (через мост пишу/читаю данные, т.е. какую-то память он видит, но даннае в буфере драйвера не меняются).

Правильно ли я определяю физический адрес выделенной памяти?

Share this post


Link to post
Share on other sites

Последний вопрос снят. Посмотрел старую инфу, где

unsigned long virt_to_phys( volatile void *address );

В новых исходниках

#ifdef CONFIG_PHYS_ADDR_T_64BIT
typedef u64 phys_addr_t;
#else
typedef u32 phys_addr_t;
#endif

static inline phys_addr_t virt_to_phys(volatile void *address)
{
    return __pa(address);
}

, как итог загонял в мост неверный адрес.

 

Вопрос про прерывания остаётся актуальным, пока так и не понял, как система раздаёт линии прерываний. Добавил только разрешение MSI и система при загрузке модуля стала выделять отдельную линию для моего устройства.

Share this post


Link to post
Share on other sites

Система не может взять и выделить PCI-устройству любую линию прерывания, какую посчитает нужным, так как эта линия может схемотехнически тупо не проложена до процессора и аппаратных средств довести эту линию от PCI-устройства до процессора у данной модели материнской платы нет.

Как разложены линии прерывания по плате знает только разработчик платы и сообщает эту информацию разработчику BIOS, который в свою очередь строит в BIOS таблицы роутинга для всех слотов PCI. В процессе старта и энумерации всех подключенных PCI-устройств BIOS раздаёт прерывания и прокладывает их до процессора известным ему аппаратным способом, после чего номер выделенного прерывания записывается в соответствующий конфиг-регистр. Этот регистр просто ячейка памяти для драйвера, который будет работать с устройством, то бишь произвольная перезапись номера прерывания не меняет выделенной линии и просто ведет к сбою логики работы драйвера.

Операционная система может оставить роутинг PCI-прерываний, как его создал BIOS при загрузке, а может и перестроить по-своему, но опираясь на таблицы роутинга, размещённые в BIOS-e.

По практике, поскольку PCI-прерывания работают по уровню и легко разделяются между устройствами, разработчики плат выделяют на PCI 2-3 линии IRQ и всё. Поэтому на уровне физики номер прерывания тот, который записан в регистре устройства и брать для подключения обработчика нужно именно его. Всё остальное, что показывает система - это виртуальные вектора, созданные самой системой в процессе старта драйверов, чтобы как-то разобраться в кипе устройств и отправить пришедшее прерывание IRQ 11 на нужный из 5 подключенных обработчиков.

Share this post


Link to post
Share on other sites

На рисунке Configuration Space для устройства PCIe есть поле Interrupt Line. Оно, как понимаю, доступно только для чтения.

Можете пояснить, что это такое и за что оно отвечает?

Если повесить обработчик прерывания на 11 линию (записана в поле Interrupt Line), то работать не будет.

При загрузке модуля указатель на структуру struct pci_dev *pdev содержит номер линии прерывания pdev->irq отличный от того, что хранится в конфигурационной области PCIe (всегда = 11). Это будет либо 16 (MSI запрещены), либо 31 (разрешены MSI). Если обработчик привязан к этому номеру - работает.

Откуда взялся номер 11 в конфигурационной области? Если его пишет система (или BIOS), то почему после переопределения номера линии прерывания он не переписывается на правильный?

 

Драйвер virtex7.

MSI запрещены

cat /proc/interrupts

          CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       
 0:         17          0          0          0          0          0          0          0   IO-APIC-edge      timer
 1:          2          0          0          0          0          0          0          0   IO-APIC-edge      i8042
 8:          1          0          0          0          0          0          0          0   IO-APIC-edge      rtc0
 9:          0          0          0          0          0          0          0          0   IO-APIC-fasteoi   acpi
12:          2          0          0          0          0          1          0          1   IO-APIC-edge      i8042
16:      11080        968      13363     494207      41182       4940      73084    4109705   IO-APIC  16-fasteoi   ehci_hcd:usb3, virtex7
17:         66        176          1          2         11        897          3         17   IO-APIC  17-fasteoi   snd_hda_intel
19:      10129      11616       4327       9923      15263      31670      14741      30747   IO-APIC  19-fasteoi   ata_piix, ata_piix
23:         27          0          0          7          1          0          0          0   IO-APIC  23-fasteoi   ehci_hcd:usb4
25:          0          0          0          0          0          0          0          0   PCI-MSI-edge      xhci_hcd
26:         17      87949          1        470          4          0          0          9   PCI-MSI-edge      eth0
27:      28196          0          1         42          2          2          0          1   PCI-MSI-edge      eth1
28:         12          0          0          0          3          0          0          0   PCI-MSI-edge      mei_me
29:         27        103          1          1         11        313         20          7   PCI-MSI-edge      snd_hda_intel
30:    1954797     699670     527638     508968     858144     423732     314299     277036   PCI-MSI-edge      nouveau
NMI:         68         57         54         49         66         51         38         69   Non-maskable interrupts
LOC:    1477693    1742544    1765876    1910964     513009     525984     531958    2214784   Local timer interrupts
SPU:          0          0          0          0          0          0          0          0   Spurious interrupts
PMI:         68         57         54         49         66         51         38         69   Performance monitoring interrupts
IWI:       4371        467        310        533        505        357        275        360   IRQ work interrupts
RTR:          6          0          0          0          0          0          0          0   APIC ICR read retries
RES:     134654     144649     116848      92117      45669      39876      36313      47002   Rescheduling interrupts
CAL:      52294      87270      93843      89098      75381      80948      91265      91551   Function call interrupts
TLB:      26135      26093      25448      23842      20976      23850      25294      17155   TLB shootdowns
TRM:          0          0          0          0          0          0          0          0   Thermal event interrupts
THR:          0          0          0          0          0          0          0          0   Threshold APIC interrupts
MCE:          0          0          0          0          0          0          0          0   Machine check exceptions
MCP:         50         50         50         50         50         50         50         50   Machine check polls
HYP:          0          0          0          0          0          0          0          0   Hypervisor callback interrupts
ERR:          0
MIS:          0

MSI разрешены

cat /proc/interrupts

      CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       
 0:         17          0          0          0          0          0          0          0   IO-APIC-edge      timer
 1:          2          0          0          0          0          0          0          0   IO-APIC-edge      i8042
 8:          1          0          0          0          0          0          0          0   IO-APIC-edge      rtc0
 9:          0          0          0          0          0          0          0          0   IO-APIC-fasteoi   acpi
12:          2          0          0          0          0          1          0          1   IO-APIC-edge      i8042
16:      11080        968      13363     535687      41182       4940      73084    4587454   IO-APIC  16-fasteoi   ehci_hcd:usb3
17:         66        176          1          2         11        897          3         17   IO-APIC  17-fasteoi   snd_hda_intel
19:      10321      11791       4942      10065      16048      32307      16633      31262   IO-APIC  19-fasteoi   ata_piix, ata_piix
23:         27          0          0          7          1          0          0          0   IO-APIC  23-fasteoi   ehci_hcd:usb4
25:          0          0          0          0          0          0          0          0   PCI-MSI-edge      xhci_hcd
26:         17      88916          1        470          4          0          0          9   PCI-MSI-edge      eth0
27:      31025          0          1         42          2          2          0          1   PCI-MSI-edge      eth1
28:         12          0          0          0          3          0          0          0   PCI-MSI-edge      mei_me
29:         27        103          1          1         11        313         20          7   PCI-MSI-edge      snd_hda_intel
30:    2145752     747852     565542     547596     947147     459822     339967     308636   PCI-MSI-edge      nouveau
31:         10          0          0          0          0          1          0          0   PCI-MSI-edge      virtex7
NMI:         74         61         58         53         68         53         40         73   Non-maskable interrupts
LOC:    1630591    1926949    1962790    2112890     550816     568671     579103    2443575   Local timer interrupts
SPU:          0          0          0          0          0          0          0          0   Spurious interrupts
PMI:         74         61         58         53         68         53         40         73   Performance monitoring interrupts
IWI:       4389        469        314        604        508        361        279       1490   IRQ work interrupts
RTR:          6          0          0          0          0          0          0          0   APIC ICR read retries
RES:     140695     148620     119993      95453      48181      41309      37573      50718   Rescheduling interrupts
CAL:      54810      89545      96134      90298      77653      83242      93564      92749   Function call interrupts
TLB:      27526      27963      26448      26049      22528      24252      25863      18098   TLB shootdowns
TRM:          0          0          0          0          0          0          0          0   Thermal event interrupts
THR:          0          0          0          0          0          0          0          0   Threshold APIC interrupts
MCE:          0          0          0          0          0          0          0          0   Machine check exceptions
MCP:         54         54         54         54         54         54         54         54   Machine check polls
HYP:          0          0          0          0          0          0          0          0   Hypervisor callback interrupts
ERR:          0
MIS:          0

post-63539-1438857054_thumb.jpg

Share this post


Link to post
Share on other sites

Хочу ещё спросить по поводу MSI.

Добавил в ядро PCIe поддержку 4 векторов MSI. Функция pci_msi_vec_count(pdev) возвращает значение 4, т.е. всё правильно.

А вот функция pci_enable_msi_range(pdev, 1, 4) возвращает 1. Что это означает, что система может выделить только одну линию под данное устройство?

А как тогда должен работать драйвер, должен предполагать возможность обеих ситуаций?

Share this post


Link to post
Share on other sites
На рисунке Configuration Space для устройства PCIe есть поле Interrupt Line. Оно, как понимаю, доступно только для чтения.

Можете пояснить, что это такое и за что оно отвечает?

Насколько я помню, только для чтения поле Interrupt Pin, оно просто определяет, на какую из 4 линий INTA#-INTD# выведена линия прерывания данной платы. Исторически сложилось так, что при конструировании материнских плат линии запросов прерываний от слота к слоту идут со сдвигом, то есть в 1-м слоте PCI INTA подключается к INTA, INTB->INTB, INTC->INTC, INTD->INTD, во 2-м слоте уже INTA->INTB, INTB->INTC, INTC->INTD, INTD->INTA и т. д. Поэтому, если все слоты будут заполнены платами PCI, у которых прерывание выведено на INTA - они равномерно распределятся по всем линиям INTA-INTD. Если какая-то плата окажется с линией INTB - ничего страшного не произойдет, она разделит с другой платой одну линию прерывания.

Поэтому конструкторы периферийных плат PCI тоже не мудруствуют лукаво и в 99% случаев используют INTA.

Все 4 линии идут в мост, и уже BIOS (или ОС) решает, выделить каждой линии INTA#-INTD# по своему IRQ или всех повесить на одно IRQ. Задача BIOS - изолировать прерывания PCI-устройств от других (ISA, Legacy), которые не могут быть разделены между несколькими устройствами.

 

Interrupt Line регистр - это просто байтовая ячейка памяти, в которую после энумерации энумератор (BIOS или ОС) записывает выделенную для устройства и проложенную до процессора линию IRQ, чтобы драйвер мог разобраться, к чему цеплять обработчик прерывания. Никакого физического смысла, кроме ячейки памяти, поле Interrupt Line не несет.

 

С MSI история примерно та же, только вместо линий INTA#-INTD# используются сообщения с соответствующим номером 1-4 для совместимости, ну и с другими номерами для доп. возможностей.

Share this post


Link to post
Share on other sites
Interrupt Line регистр - это просто байтовая ячейка памяти, в которую после энумерации энумератор (BIOS или ОС) записывает выделенную для устройства и проложенную до процессора линию IRQ, чтобы драйвер мог разобраться, к чему цеплять обработчик прерывания. Никакого физического смысла, кроме ячейки памяти, поле Interrupt Line не несет.

Каким тогда образом, если в Interrupt Line записано 11 (при выполнении функции pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_num) получаю irq_num = 11), прерывание работает на линии № 16 (если MSI запрещены), либо № 31 (если MSI разрешены)?

 

Share this post


Link to post
Share on other sites
Каким тогда образом, если в Interrupt Line записано 11 (при выполнении функции pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_num) получаю irq_num = 11), прерывание работает на линии № 16 (если MSI запрещены), либо № 31 (если MSI разрешены)?

Возможно, это "происки" операционной системы, то есть номера 16 и 31 являются виртуальными номерами прерываний. Так как на одном физическом номере IRQ может висеть несколько PCI-устройств - ОС должна как-то различать, какой драйвер к какому устройству подключается. Для этого ОС "придумывает" свои номера устройствам, а сама "втихаря" подвешивает свой обработчик на IRQ 11. Как только произойдёт IRQ 11 - система передает управление всем подключенным обработчикам (возможно, с приоритетом), пока кто-то из обработчиков не сообщит, что, мол, да, это моё устройство сгенерировало прерывание, и я его обработал. Так система поймет, какое именно устройство сгенерировало прерывание IRQ 11.

Share this post


Link to post
Share on other sites

В драйвере есть операции работы с файлом драйвера (virtex7board_ioctl добавил недавно):

static const struct file_operations virtex7board_fops= {
    .owner                = THIS_MODULE,
    .open                = virtex7board_open,
    .release            = virtex7board_release,
    .read                = virtex7board_read,
    .write                = virtex7board_write,
    .unlocked_ioctl        = virtex7board_ioctl,
};

Для работы с файлом драйвера из user space использовал fopen, fwrite, fread, fclose и всё было гуд. Теперь добавил в модуль драйвера поддержку ioctl. Тут появились какие-то ошибки. Функция fread как-то неправильно работает, она вызывает virtex7board_ioctl и virtex7board_read и передаёт неправильный размер данных для чтения в virtex7board_read (4096 байт вместо 16).

Попробовал использовать open, read, write, close - тут всё нормально.

???

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this