Есть плата с 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);
...
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
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 линии?
Код модуля:
CODE
/* 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;
}