From 7bae8f4926816662b8989b0e9872b3c1c6229a3f Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Fri, 1 Mar 2024 10:15:09 +0100 Subject: [PATCH 01/12] add: ccat types from fc1121 --- module.c | 16 ++++++++++++++++ module.h | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/module.c b/module.c index c218ab1..87e4ff1 100644 --- a/module.c +++ b/module.c @@ -18,6 +18,14 @@ MODULE_LICENSE("GPL and additional rights"); MODULE_VERSION(DRV_VERSION); static struct ccat_cell ccat_cells[] = { + { + .type = CCATINFO_INFO, + .cell = {.name = "ccat_info"}, + }, + { + .type = CCATINFO_ETHERCAT_SLAVE, + .cell = {.name = "ccat_esc"}, + }, { .type = CCATINFO_ETHERCAT_NODMA, .cell = {.name = "ccat_eth_eim"}, @@ -42,6 +50,14 @@ static struct ccat_cell ccat_cells[] = { .type = CCATINFO_SYSTEMTIME, .cell = {.name = "ccat_systemtime"}, }, + { + .type = CCATINFO_IRQ, + .cell = {.name = "ccat_irq"}, + }, + { + .type = CCATINFO_EEPROM, + .cell = {.name = "ccat_eeprom"}, + }, }; static int __init ccat_class_init(struct ccat_class *base) diff --git a/module.h b/module.h index 9aee94b..32967c5 100644 --- a/module.h +++ b/module.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + +// vim: noexpandtab #ifndef _CCAT_H_ #define _CCAT_H_ @@ -40,10 +42,14 @@ */ enum ccat_info_t { CCATINFO_NOTUSED = 0, + CCATINFO_INFO = 0x1, + CCATINFO_ETHERCAT_SLAVE = 0x2, CCATINFO_ETHERCAT_NODMA = 0x3, CCATINFO_GPIO = 0xd, CCATINFO_EPCS_PROM = 0xf, CCATINFO_SYSTEMTIME = 0x10, + CCATINFO_IRQ = 0x11, + CCATINFO_EEPROM = 0x12, CCATINFO_ETHERCAT_MASTER_DMA = 0x14, CCATINFO_SRAM = 0x16, }; From a5f0e913f4095872725eefefc92cb6d80a6b501b Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Fri, 1 Mar 2024 14:25:24 +0100 Subject: [PATCH 02/12] add: esc and irq driver --- Makefile | 5 +- esc.c | 115 +++++++++++++++++++++++++ irq.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ module.c | 6 +- module.h | 8 +- sram.c | 2 +- update.c | 2 +- 7 files changed, 389 insertions(+), 5 deletions(-) create mode 100644 esc.c create mode 100644 irq.c diff --git a/Makefile b/Makefile index efa7019..8a9b398 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ KDIR ?= /lib/modules/$(shell uname -r)/build -obj-m += ccat.o ccat_netdev.o ccat_gpio.o ccat_sram.o ccat_systemtime.o ccat_update.o +obj-m += ccat.o ccat_netdev.o ccat_gpio.o ccat_sram.o ccat_systemtime.o ccat_update.o ccat_esc.o ccat_irq.o ccat-y := module.o ccat_netdev-y := netdev.o ccat_gpio-y := gpio.o ccat_sram-y := sram.o ccat_systemtime-y := systemtime.o ccat_update-y := update.o +ccat_esc-y := esc.o +ccat_irq-y := irq.o #ccflags-y := -DDEBUG ccflags-y += -D__CHECK_ENDIAN__ @@ -23,6 +25,7 @@ install: - rmmod ccat make -C $(KDIR) M=$(CURDIR) modules_install modprobe ccat + modprobe ccat_esc modprobe ccat_netdev modprobe ccat_gpio modprobe ccat_sram diff --git a/esc.c b/esc.c new file mode 100644 index 0000000..573d01b --- /dev/null +++ b/esc.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +/** + ESC Driver for Beckhoff CCAT FPGA ESCs + Copyright (C) 2024 DLR e.V. + Author: Robert Burger +*/ + +// vim: set noexpandtab + +#include "module.h" +#include +#include +#include +#include + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR("Robert Burger "); +MODULE_LICENSE("GPL and additional rights"); +MODULE_VERSION(DRV_VERSION); + +#define CCAT_ESC_DEVICES_MAX 4 + +static ssize_t ccat_esc_read(struct file *const f, char __user * buf, + size_t len, loff_t * off) +{ + struct cdev_buffer *buffer = f->private_data; + const size_t iosize = buffer->ccdev->iosize; + + if (*off >= iosize) { + return 0; + } + + len = min(len, (size_t) (iosize - *off)); + + memcpy_fromio(buffer->data, buffer->ccdev->ioaddr + *off, len); + if (copy_to_user(buf, buffer->data, len)) + return -EFAULT; + + *off += len; + return len; +} + +static ssize_t ccat_esc_write(struct file *const f, const char __user * buf, + size_t len, loff_t * off) +{ + struct cdev_buffer *const buffer = f->private_data; + + if (*off + len > buffer->ccdev->iosize) { + return 0; + } + + if (copy_from_user(buffer->data, buf, len)) { + return -EFAULT; + } + + memcpy_toio(buffer->ccdev->ioaddr + *off, buffer->data, len); + + *off += len; + return len; +} + +static int ccat_esc_mmap(struct file *f, struct vm_area_struct *vma) +{ + struct cdev_buffer *const buffer = f->private_data; + struct pci_dev *pdev = (struct pci_dev *)(buffer->ccdev->func->ccat->pdev); + + if (vma->vm_pgoff == 0) { + vma->vm_pgoff = (pci_resource_start(pdev, 0) + buffer->ccdev->func->info.addr) >> PAGE_SHIFT; + } else { + vma->vm_pgoff = (pci_resource_start(pdev, 0) >> PAGE_SHIFT); + } + + pr_info("pgoff %lX\n", vma->vm_pgoff); + return remap_pfn_range( + vma, + vma->vm_start, + vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static struct ccat_cdev dev_table[CCAT_ESC_DEVICES_MAX]; +static struct ccat_class cdev_class = { + .instances = {0}, + .count = CCAT_ESC_DEVICES_MAX, + .devices = dev_table, + .name = "ccat_esc", + .fops = { + .owner = THIS_MODULE, + .llseek = ccat_cdev_llseek, + .open = ccat_cdev_open, + .release = ccat_cdev_release, + .read = ccat_esc_read, + .write = ccat_esc_write, + .mmap = ccat_esc_mmap, + }, +}; + +static int ccat_esc_probe(struct platform_device *pdev) +{ + struct ccat_function *const func = pdev->dev.platform_data; + + pr_info("%s: 0x%04x rev: 0x%04x, addr: 0x%X, size: 0x%X\n", __FUNCTION__, + func->info.type, func->info.rev, func->info.addr, func->info.size); + + return ccat_cdev_probe(func, &cdev_class, func->info.size, NULL); +} + +static struct platform_driver esc_driver = { + .driver = {.name = "ccat_esc"}, + .probe = ccat_esc_probe, + .remove = ccat_cdev_remove, +}; + +module_platform_driver(esc_driver); + diff --git a/irq.c b/irq.c new file mode 100644 index 0000000..1583733 --- /dev/null +++ b/irq.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: MIT +/** + IRQ Driver for Beckhoff CCAT FPGA irq + Copyright (C) 2024 DLR e.V. + Author: Robert Burger +*/ + +// vim: set noexpandtab + +#include "module.h" +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR("Robert Burger "); +MODULE_LICENSE("GPL and additional rights"); +MODULE_VERSION(DRV_VERSION); + +struct ccat_irq { + char name[16]; + int irq_num; + wait_queue_head_t ir_queue; // Interrupt wait queue. +}; + +#define CCAT_IRQ_FUNCTION_INFO ((uint16_t)0x0001u) +#define CCAT_IRQ_FUNCTION_ESC ((uint16_t)0x0002u) // slot 1 +#define CCAT_IRQ_FUNCTION_SYSTEM_TIME ((uint16_t)0x0010u) // slot 6 +#define CCAT_IRQ_FUNCTION_IRQ ((uint16_t)0x0011u) // slot 8 +#define CCAT_IRQ_FUNCTION_EPSC_PROM ((uint16_t)0x000Fu) // slot 10 +#define CCAT_IRQ_FUNCTION_EEPROM ((uint16_t)0x0012u) // slot 11 +#define CCAT_IRQ_FUNCTION_SRAM ((uint16_t)0x0016u) // slot 13 + +#define CCAT_IRQ_FUNCTION_IRQ__SLOT_N(n) ((uint16_t)1u << n) +#define CCAT_IRQ_FUNCTION_IRQ__SLOT (CCAT_IRQ_FUNCTION_IRQ__SLOT_N(1)) + +#define CCAT_IRQ_FUNCTION_IRQ__STATUS_REG ((uint32_t)0x0u) // 2 byte / 1 word +#define CCAT_IRQ_FUNCTION_IRQ__CONTROL_REG ((uint32_t)0x8u) // 2 byte / 1 word + +#define CCAT_IRQ_GLOBAL_IRQ_STATUS_REG ((uint32_t)0x40u) // 1 byte +#define CCAT_IRQ_GLOBAL_IRQ_ENABLE_REG ((uint32_t)0x50u) // 1 byte + +#define CCAT_IRQ_GLOBAL_IRQ_ENABLE ((uint32_t)0x80u) + +#define CCAT_IRQ_DEVICES_MAX 4 + +static uint16_t ccat_irq_get_slot_irq_stat(struct ccat_function *func) +{ + return ioread16(func->ccat->bar_0 + func->info.addr + CCAT_IRQ_FUNCTION_IRQ__STATUS_REG); +} + +static void ccat_irq_set_slot_irq_ctrl(struct ccat_function *func, uint16_t ctrl) +{ + iowrite16(ctrl, func->ccat->bar_0 + func->info.addr + CCAT_IRQ_FUNCTION_IRQ__CONTROL_REG); +} + +static uint8_t ccat_irq_get_global_irq_stat(struct ccat_function *func) +{ + return ioread8(func->ccat->bar_2 + CCAT_IRQ_GLOBAL_IRQ_STATUS_REG); +} + +static void ccat_irq_set_global_irq_ctrl(struct ccat_function *func, uint8_t ctrl) +{ + iowrite8(ctrl, func->ccat->bar_2 + CCAT_IRQ_GLOBAL_IRQ_ENABLE_REG); +} + +int ccat_irq_use_msi = 0; +module_param(ccat_irq_use_msi, int, 0); +MODULE_PARM_DESC(ccat_irq_use_msi, "Use MSI interrupts instead of legacy PCI"); + +static irqreturn_t ccat_irq_int_handler(int int_no, void *arg) { + struct cdev_buffer *buffer = (struct cdev_buffer *)arg; + struct ccat_irq *irq = (struct ccat_irq *)(buffer->ccdev->user); + irqreturn_t ret = IRQ_NONE; + uint32_t global_state = ccat_irq_get_global_irq_stat(buffer->ccdev->func); + + if (ccat_irq_use_msi != 0) { + // no need to check here, it's 100% sure it is ours + + // disable here until interrupt source is processed. + // will be re-enable in poll + ccat_irq_set_slot_irq_ctrl(buffer->ccdev->func, 0); + wake_up(&irq->ir_queue); + + ret = IRQ_HANDLED; + } else { + if (global_state & 0x80) { + if (ccat_irq_get_slot_irq_stat(buffer->ccdev->func) & CCAT_IRQ_FUNCTION_IRQ__SLOT) { + // disable here until interrupt source is processed. + // will be re-enable in poll + ccat_irq_set_slot_irq_ctrl(buffer->ccdev->func, 0); + wake_up(&irq->ir_queue); + + ret = IRQ_HANDLED; + } + } + } + + return ret; +} + +static int ccat_irq_open(struct inode *const i, struct file *const f) +{ + struct ccat_cdev *ccdev = + container_of(i->i_cdev, struct ccat_cdev, cdev); + struct ccat_irq *irq = (struct ccat_irq *)(ccdev->user); + struct cdev_buffer *buf; + + if (!atomic_dec_and_test(&ccdev->in_use)) { + atomic_inc(&ccdev->in_use); + return -EBUSY; + } + + buf = kzalloc(sizeof(*buf) + ccdev->iosize, GFP_KERNEL); + if (!buf) { + atomic_inc(&ccdev->in_use); + return -ENOMEM; + } + + buf->ccdev = ccdev; + f->private_data = buf; + + if (ccat_irq_use_msi == 1) { + int num_vecs = pci_alloc_irq_vectors(ccdev->func->ccat->pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (num_vecs < 0 ) { + pr_err("Allocating IRQ vectors failed\n"); + } else { + int r, irq_success = 1; + pr_info("Got %d IRQ vectors\n", num_vecs); + + for (r = 0; r < num_vecs; ++r) { + irq->irq_num = pci_irq_vector(ccdev->func->ccat->pdev, r); + pr_info("Interrupt %d has been reserved, using irq name %s\n", irq->irq_num, &irq->name[0]); + + if (request_irq(irq->irq_num, ccat_irq_int_handler, IRQF_NO_THREAD, &irq->name[0], buf)) { + pr_err("Interrupt %d reqeust failed!\n", irq->irq_num); + irq_success = 0; + } + } + + if (irq_success == 0) { + pci_disable_device(ccdev->func->ccat->pdev); + + return -EBUSY; + } else { + // disable all Interrupt slots + ccat_irq_set_slot_irq_ctrl(ccdev->func, 0); + ccat_irq_set_global_irq_ctrl(ccdev->func, CCAT_IRQ_GLOBAL_IRQ_ENABLE); + } + } + } else { + irq->irq_num = ((struct pci_dev *)(ccdev->func->ccat->pdev))->irq; + + pr_info("Interrupt %d has been reserved, using irq name %s\n", irq->irq_num, &irq->name[0]); + + if (request_irq(irq->irq_num, ccat_irq_int_handler, IRQF_SHARED, &irq->name[0], buf)) { + pci_disable_device(ccdev->func->ccat->pdev); + + pr_err("Interrupt %d isn't free\n", irq->irq_num); + return -EBUSY; + } else { + // disable all Interrupt slots + ccat_irq_set_slot_irq_ctrl(ccdev->func, 0); + ccat_irq_set_global_irq_ctrl(ccdev->func, CCAT_IRQ_GLOBAL_IRQ_ENABLE); + } + } + + return 0; +} + +static int ccat_irq_release(struct inode *const i, struct file *const f) +{ + struct cdev_buffer *const buf = f->private_data; + struct ccat_cdev *const ccdev = buf->ccdev; + struct ccat_irq *irq = (struct ccat_irq *)(ccdev->user); + + ccat_irq_set_global_irq_ctrl(ccdev->func, 0); + + if (ccat_irq_use_msi == 1) { + // disable Interrupt (see FC1121 Application Notes 3.2.1) + free_irq(irq->irq_num, buf); + pci_free_irq_vectors(ccdev->func->ccat->pdev); + } else { + free_irq(irq->irq_num, buf); + } + + kfree(f->private_data); + atomic_inc(&ccdev->in_use); + return 0; +} + +static unsigned int ccat_irq_poll(struct file *f, struct poll_table_struct *poll_table) +{ + struct cdev_buffer *buffer = f->private_data; + struct ccat_irq *irq = (struct ccat_irq *)(buffer->ccdev->user); + + // status of slot 1 (see FC1121 Application Notes 3.2.1) + if (ccat_irq_get_slot_irq_stat(buffer->ccdev->func) & CCAT_IRQ_FUNCTION_IRQ__SLOT) { + return DEFAULT_POLLMASK; + } + + ccat_irq_set_slot_irq_ctrl(buffer->ccdev->func, CCAT_IRQ_FUNCTION_IRQ__SLOT); + poll_wait(f, &irq->ir_queue, poll_table); + return 0; +} + +static struct ccat_cdev dev_table[CCAT_IRQ_DEVICES_MAX]; +static struct ccat_class cdev_class = { + .instances = {0}, + .count = CCAT_IRQ_DEVICES_MAX, + .devices = dev_table, + .name = "ccat_irq", + .fops = { + .owner = THIS_MODULE, + .open = ccat_irq_open, + .release = ccat_irq_release, + .poll = ccat_irq_poll, + }, +}; + +static int ccat_irq_probe(struct platform_device *pdev) +{ + struct ccat_function *const func = pdev->dev.platform_data; + struct ccat_irq *const irq = kzalloc(sizeof(*irq), GFP_KERNEL); + int ret = 0; + + if (!irq) + return -ENOMEM; + + // init wait queue + init_waitqueue_head(&irq->ir_queue); + + pr_info("%s: 0x%04x rev: 0x%04x, addr: 0x%X, size: 0x%X\n", __FUNCTION__, + func->info.type, func->info.rev, func->info.addr, func->info.size); + + ret = ccat_cdev_probe(func, &cdev_class, func->info.size, irq); + + if (ret == 0) { + struct ccat_cdev *ccdev = (struct ccat_cdev *)func->private_data; + snprintf(&irq->name[0], 16, "esc%d", MINOR(ccdev->dev)); + } + + return ret; +} + +static struct platform_driver irq_driver = { + .driver = {.name = "ccat_irq"}, + .probe = ccat_irq_probe, + .remove = ccat_cdev_remove, // TODO release ir_queue +}; + +module_platform_driver(irq_driver); + + diff --git a/module.c b/module.c index 87e4ff1..015de2b 100644 --- a/module.c +++ b/module.c @@ -5,6 +5,8 @@ Author: Patrick Bruenn */ +// vim: noexpandtab + #include #include #include @@ -168,7 +170,7 @@ int ccat_cdev_open(struct inode *const i, struct file *const f) EXPORT_SYMBOL(ccat_cdev_open); int ccat_cdev_probe(struct ccat_function *func, struct ccat_class *cdev_class, - size_t iosize) + size_t iosize, void *user) { struct ccat_cdev *const ccdev = alloc_ccat_cdev(cdev_class); if (!ccdev) { @@ -177,6 +179,8 @@ int ccat_cdev_probe(struct ccat_function *func, struct ccat_class *cdev_class, ccdev->ioaddr = func->ccat->bar_0 + func->info.addr; ccdev->iosize = iosize; + ccdev->func = func; + ccdev->user = user; atomic_set(&ccdev->in_use, 1); if (ccat_cdev_init diff --git a/module.h b/module.h index 32967c5..1174de7 100644 --- a/module.h +++ b/module.h @@ -71,6 +71,8 @@ struct ccat_cdev { dev_t dev; struct cdev cdev; struct ccat_class *class; + struct ccat_function *func; + void *user; }; /** @@ -125,6 +127,10 @@ struct ccat_info_block { u8 sram_size; u16 reserved; }; + struct { + u16 revision; + u16 parameter; + }; }; u32 addr; u32 size; @@ -148,6 +154,6 @@ struct ccat_class { extern int ccat_cdev_remove(struct platform_device *pdev); extern int ccat_cdev_probe(struct ccat_function *func, - struct ccat_class *cdev_class, size_t iosize); + struct ccat_class *cdev_class, size_t iosize, void *user); #endif /* #ifndef _CCAT_H_ */ diff --git a/sram.c b/sram.c index 6934641..4024233 100644 --- a/sram.c +++ b/sram.c @@ -91,7 +91,7 @@ static int ccat_sram_probe(struct platform_device *pdev) if (type == NO_SRAM_CONNECTED) { return -ENODEV; } - return ccat_cdev_probe(func, &cdev_class, iosize); + return ccat_cdev_probe(func, &cdev_class, iosize, NULL); } static struct platform_driver sram_driver = { diff --git a/update.c b/update.c index 4ddcf66..0f3c12a 100644 --- a/update.c +++ b/update.c @@ -333,7 +333,7 @@ static int ccat_update_probe(struct platform_device *pdev) pr_warn("CCAT Update rev. %d not supported\n", func->info.rev); return -ENODEV; } - return ccat_cdev_probe(func, &cdev_class, CCAT_FLASH_SIZE); + return ccat_cdev_probe(func, &cdev_class, CCAT_FLASH_SIZE, NULL); } static struct platform_driver update_driver = { From cb677233fb8c153372ddafc08262ebfbe91f73bb Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Fri, 1 Mar 2024 13:38:47 +0100 Subject: [PATCH 03/12] indent fix --- esc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esc.c b/esc.c index 573d01b..c43ff4d 100644 --- a/esc.c +++ b/esc.c @@ -91,7 +91,7 @@ static struct ccat_class cdev_class = { .release = ccat_cdev_release, .read = ccat_esc_read, .write = ccat_esc_write, - .mmap = ccat_esc_mmap, + .mmap = ccat_esc_mmap, }, }; From 8bc6b54acd9eb9e08685d6afe54a4edcd58d93fa Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Fri, 1 Mar 2024 15:42:29 +0100 Subject: [PATCH 04/12] indent fix --- esc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esc.c b/esc.c index c43ff4d..bcee51e 100644 --- a/esc.c +++ b/esc.c @@ -62,7 +62,7 @@ static ssize_t ccat_esc_write(struct file *const f, const char __user * buf, static int ccat_esc_mmap(struct file *f, struct vm_area_struct *vma) { struct cdev_buffer *const buffer = f->private_data; - struct pci_dev *pdev = (struct pci_dev *)(buffer->ccdev->func->ccat->pdev); + struct pci_dev *pdev = (struct pci_dev *)(buffer->ccdev->func->ccat->pdev); if (vma->vm_pgoff == 0) { vma->vm_pgoff = (pci_resource_start(pdev, 0) + buffer->ccdev->func->info.addr) >> PAGE_SHIFT; @@ -102,7 +102,7 @@ static int ccat_esc_probe(struct platform_device *pdev) pr_info("%s: 0x%04x rev: 0x%04x, addr: 0x%X, size: 0x%X\n", __FUNCTION__, func->info.type, func->info.rev, func->info.addr, func->info.size); - return ccat_cdev_probe(func, &cdev_class, func->info.size, NULL); + return ccat_cdev_probe(func, &cdev_class, func->info.size, NULL); } static struct platform_driver esc_driver = { From 5222e7f61249219e4c90fee0c011bb45bf101bfb Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Fri, 1 Mar 2024 18:01:09 +0100 Subject: [PATCH 05/12] fix: loading ccat_irq module on install rule --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 8a9b398..567f977 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ install: make -C $(KDIR) M=$(CURDIR) modules_install modprobe ccat modprobe ccat_esc + modprobe ccat_irq modprobe ccat_netdev modprobe ccat_gpio modprobe ccat_sram From 6fe7f6c48b6b4849ff2ba22c4f9e577dd0fb32c3 Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Sat, 2 Mar 2024 09:02:35 +0100 Subject: [PATCH 06/12] chore: final cleanup --- esc.c | 3 +-- irq.c | 24 ++++++++---------------- module.c | 2 +- module.h | 2 +- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/esc.c b/esc.c index bcee51e..9a26720 100644 --- a/esc.c +++ b/esc.c @@ -5,7 +5,7 @@ Author: Robert Burger */ -// vim: set noexpandtab +// vi: set noexpandtab: #include "module.h" #include @@ -70,7 +70,6 @@ static int ccat_esc_mmap(struct file *f, struct vm_area_struct *vma) vma->vm_pgoff = (pci_resource_start(pdev, 0) >> PAGE_SHIFT); } - pr_info("pgoff %lX\n", vma->vm_pgoff); return remap_pfn_range( vma, vma->vm_start, diff --git a/irq.c b/irq.c index 1583733..fb51b5d 100644 --- a/irq.c +++ b/irq.c @@ -5,7 +5,7 @@ Author: Robert Burger */ -// vim: set noexpandtab +// vi: set noexpandtab: #include "module.h" #include @@ -25,24 +25,16 @@ struct ccat_irq { wait_queue_head_t ir_queue; // Interrupt wait queue. }; -#define CCAT_IRQ_FUNCTION_INFO ((uint16_t)0x0001u) -#define CCAT_IRQ_FUNCTION_ESC ((uint16_t)0x0002u) // slot 1 -#define CCAT_IRQ_FUNCTION_SYSTEM_TIME ((uint16_t)0x0010u) // slot 6 -#define CCAT_IRQ_FUNCTION_IRQ ((uint16_t)0x0011u) // slot 8 -#define CCAT_IRQ_FUNCTION_EPSC_PROM ((uint16_t)0x000Fu) // slot 10 -#define CCAT_IRQ_FUNCTION_EEPROM ((uint16_t)0x0012u) // slot 11 -#define CCAT_IRQ_FUNCTION_SRAM ((uint16_t)0x0016u) // slot 13 +#define CCAT_IRQ_FUNCTION_IRQ__SLOT_N(n) ((uint16_t)1u << n) +#define CCAT_IRQ_FUNCTION_IRQ__SLOT (CCAT_IRQ_FUNCTION_IRQ__SLOT_N(1)) -#define CCAT_IRQ_FUNCTION_IRQ__SLOT_N(n) ((uint16_t)1u << n) -#define CCAT_IRQ_FUNCTION_IRQ__SLOT (CCAT_IRQ_FUNCTION_IRQ__SLOT_N(1)) +#define CCAT_IRQ_FUNCTION_IRQ__STATUS_REG ((uint32_t)0x0u) // 2 byte / 1 word +#define CCAT_IRQ_FUNCTION_IRQ__CONTROL_REG ((uint32_t)0x8u) // 2 byte / 1 word -#define CCAT_IRQ_FUNCTION_IRQ__STATUS_REG ((uint32_t)0x0u) // 2 byte / 1 word -#define CCAT_IRQ_FUNCTION_IRQ__CONTROL_REG ((uint32_t)0x8u) // 2 byte / 1 word +#define CCAT_IRQ_GLOBAL_IRQ_STATUS_REG ((uint32_t)0x40u) // 1 byte +#define CCAT_IRQ_GLOBAL_IRQ_ENABLE_REG ((uint32_t)0x50u) // 1 byte -#define CCAT_IRQ_GLOBAL_IRQ_STATUS_REG ((uint32_t)0x40u) // 1 byte -#define CCAT_IRQ_GLOBAL_IRQ_ENABLE_REG ((uint32_t)0x50u) // 1 byte - -#define CCAT_IRQ_GLOBAL_IRQ_ENABLE ((uint32_t)0x80u) +#define CCAT_IRQ_GLOBAL_IRQ_ENABLE ((uint32_t)0x80u) #define CCAT_IRQ_DEVICES_MAX 4 diff --git a/module.c b/module.c index 015de2b..e912436 100644 --- a/module.c +++ b/module.c @@ -5,7 +5,7 @@ Author: Patrick Bruenn */ -// vim: noexpandtab +// vi: noexpandtab: #include #include diff --git a/module.h b/module.h index 1174de7..8101c88 100644 --- a/module.h +++ b/module.h @@ -18,7 +18,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -// vim: noexpandtab +// vi: noexpandtab: #ifndef _CCAT_H_ #define _CCAT_H_ From 33041c9685d0fc8d047f0f916e4793048881c8d6 Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Sat, 2 Mar 2024 09:07:47 +0100 Subject: [PATCH 07/12] chore: updated readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 257845e..64e0460 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,15 @@ https://infosys.beckhoff.com/english.php?content=../content/1033/ethercatsystem/ - GPIO - SRAM - FPGA update +- ESC (EtherCAT@ Slave) +- IRQ ### Supported devices - [CX50xx](https://www.beckhoff.com/CX5000/) - [CX51xx](https://www.beckhoff.com/CX5100/) - [CX20xx](https://www.beckhoff.com/CX2000/) +- [FC1121](https://www.beckhoff.com/en-en/products/ipc/pcs/accessories/fc1121.html) ### How to build and install the kernel modules: From 67b10ddb055c546f9d85a8d0199e9c1340be3c5b Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Sat, 2 Mar 2024 09:22:08 +0100 Subject: [PATCH 08/12] change: reusing sram read and write funcs in esc --- Makefile | 1 + esc.c | 44 +++----------------------------------------- sram.c | 13 ++++++++++--- sram.h | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 44 deletions(-) create mode 100644 sram.h diff --git a/Makefile b/Makefile index 567f977..9b57b7a 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ install: - rmmod ccat_netdev - rmmod ccat make -C $(KDIR) M=$(CURDIR) modules_install + depmod -a modprobe ccat modprobe ccat_esc modprobe ccat_irq diff --git a/esc.c b/esc.c index 9a26720..962d79e 100644 --- a/esc.c +++ b/esc.c @@ -8,6 +8,7 @@ // vi: set noexpandtab: #include "module.h" +#include "sram.h" #include #include #include @@ -20,45 +21,6 @@ MODULE_VERSION(DRV_VERSION); #define CCAT_ESC_DEVICES_MAX 4 -static ssize_t ccat_esc_read(struct file *const f, char __user * buf, - size_t len, loff_t * off) -{ - struct cdev_buffer *buffer = f->private_data; - const size_t iosize = buffer->ccdev->iosize; - - if (*off >= iosize) { - return 0; - } - - len = min(len, (size_t) (iosize - *off)); - - memcpy_fromio(buffer->data, buffer->ccdev->ioaddr + *off, len); - if (copy_to_user(buf, buffer->data, len)) - return -EFAULT; - - *off += len; - return len; -} - -static ssize_t ccat_esc_write(struct file *const f, const char __user * buf, - size_t len, loff_t * off) -{ - struct cdev_buffer *const buffer = f->private_data; - - if (*off + len > buffer->ccdev->iosize) { - return 0; - } - - if (copy_from_user(buffer->data, buf, len)) { - return -EFAULT; - } - - memcpy_toio(buffer->ccdev->ioaddr + *off, buffer->data, len); - - *off += len; - return len; -} - static int ccat_esc_mmap(struct file *f, struct vm_area_struct *vma) { struct cdev_buffer *const buffer = f->private_data; @@ -88,8 +50,8 @@ static struct ccat_class cdev_class = { .llseek = ccat_cdev_llseek, .open = ccat_cdev_open, .release = ccat_cdev_release, - .read = ccat_esc_read, - .write = ccat_esc_write, + .read = ccat_sram_read, + .write = ccat_sram_write, .mmap = ccat_esc_mmap, }, }; diff --git a/sram.c b/sram.c index 4024233..37ebd3b 100644 --- a/sram.c +++ b/sram.c @@ -5,7 +5,10 @@ Author: Patrick Bruenn */ +// vi: set noexpandtab: + #include "module.h" +#include "sram.h" #include #include #include @@ -29,7 +32,7 @@ static ssize_t __sram_read(struct cdev_buffer *buffer, char __user * buf, return len; } -static ssize_t ccat_sram_read(struct file *const f, char __user * buf, +ssize_t ccat_sram_read(struct file *const f, char __user * buf, size_t len, loff_t * off) { struct cdev_buffer *buffer = f->private_data; @@ -42,9 +45,11 @@ static ssize_t ccat_sram_read(struct file *const f, char __user * buf, len = min(len, (size_t) (iosize - *off)); return __sram_read(buffer, buf, len, off); -} +} -static ssize_t ccat_sram_write(struct file *const f, const char __user * buf, +EXPORT_SYMBOL(ccat_sram_read); + +ssize_t ccat_sram_write(struct file *const f, const char __user * buf, size_t len, loff_t * off) { struct cdev_buffer *const buffer = f->private_data; @@ -63,6 +68,8 @@ static ssize_t ccat_sram_write(struct file *const f, const char __user * buf, return len; } +EXPORT_SYMBOL(ccat_sram_write); + static struct ccat_cdev dev_table[CCAT_SRAM_DEVICES_MAX]; static struct ccat_class cdev_class = { .instances = {0}, diff --git a/sram.h b/sram.h new file mode 100644 index 0000000..71b3bc8 --- /dev/null +++ b/sram.h @@ -0,0 +1,35 @@ +/** + Network Driver for Beckhoff CCAT communication controller + Copyright (C) 2014 Beckhoff Automation GmbH + Author: Patrick Bruenn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +// vi: set noexpandtab: + +#ifndef _CCAT_SRAM_H_ +#define _CCAT_SRAM_H_ + +#include + +extern ssize_t ccat_sram_read(struct file *const f, char __user * buf, + size_t len, loff_t * off); + +extern ssize_t ccat_sram_write(struct file *const f, const char __user * buf, + size_t len, loff_t * off); + +#endif /* #ifndef _CCAT_SRAM_H_ */ + From 44fd523bacf09ceb9d9f51ce9a7cbded2d176393 Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Sat, 2 Mar 2024 09:50:32 +0100 Subject: [PATCH 09/12] add: rules for modprobe and udev modprobe rule to automatically load other ccat_* modules after ccat was loaded automatically by pci id. udev rule to set permissions for users on /dev/ccat_* device files. --- Makefile | 2 ++ etc/modprobe.d/ccat.conf | 1 + etc/udev/rules.d/991-ccat.rules | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 etc/modprobe.d/ccat.conf create mode 100644 etc/udev/rules.d/991-ccat.rules diff --git a/Makefile b/Makefile index 9b57b7a..53194b2 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,8 @@ install: - rmmod ccat_netdev - rmmod ccat make -C $(KDIR) M=$(CURDIR) modules_install + cp etc/modprobe.d/ccat.conf /etc/modprobe.d/ + cp etc/udev/rules.d/991-ccat.rules /etc/udev/rules.d/ depmod -a modprobe ccat modprobe ccat_esc diff --git a/etc/modprobe.d/ccat.conf b/etc/modprobe.d/ccat.conf new file mode 100644 index 0000000..2ca1eac --- /dev/null +++ b/etc/modprobe.d/ccat.conf @@ -0,0 +1 @@ +softdep ccat post: ccat_esc ccat_gpio ccat_irq ccat_netdev ccat_sram ccat_systemtime ccat_update diff --git a/etc/udev/rules.d/991-ccat.rules b/etc/udev/rules.d/991-ccat.rules new file mode 100644 index 0000000..2ea0968 --- /dev/null +++ b/etc/udev/rules.d/991-ccat.rules @@ -0,0 +1,4 @@ +# udev rules for CCAT devices: +SUBSYSTEM=="ccat_esc", KERNEL=="ccat_esc?", MODE="0666" +SUBSYSTEM=="ccat_irq", KERNEL=="ccat_irq?", MODE="0666" +SUBSYSTEM=="ccat_update", KERNEL=="ccat_update?", MODE="0666" From fed2faae88a76b53ef5f5ed0183ca10140af97df Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Mon, 4 Mar 2024 10:28:03 +0100 Subject: [PATCH 10/12] cleanup: only map ESC memory area to user level --- esc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/esc.c b/esc.c index 962d79e..d50d702 100644 --- a/esc.c +++ b/esc.c @@ -26,11 +26,7 @@ static int ccat_esc_mmap(struct file *f, struct vm_area_struct *vma) struct cdev_buffer *const buffer = f->private_data; struct pci_dev *pdev = (struct pci_dev *)(buffer->ccdev->func->ccat->pdev); - if (vma->vm_pgoff == 0) { - vma->vm_pgoff = (pci_resource_start(pdev, 0) + buffer->ccdev->func->info.addr) >> PAGE_SHIFT; - } else { - vma->vm_pgoff = (pci_resource_start(pdev, 0) >> PAGE_SHIFT); - } + vma->vm_pgoff = (pci_resource_start(pdev, 0) + buffer->ccdev->func->info.addr) >> PAGE_SHIFT; return remap_pfn_range( vma, From f902ad864dca2c50dc26cca6587d254120a4067e Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Wed, 6 Mar 2024 08:09:57 +0100 Subject: [PATCH 11/12] change: adding flage and page protection to mmaped memory --- esc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esc.c b/esc.c index d50d702..1cdaf06 100644 --- a/esc.c +++ b/esc.c @@ -27,6 +27,8 @@ static int ccat_esc_mmap(struct file *f, struct vm_area_struct *vma) struct pci_dev *pdev = (struct pci_dev *)(buffer->ccdev->func->ccat->pdev); vma->vm_pgoff = (pci_resource_start(pdev, 0) + buffer->ccdev->func->info.addr) >> PAGE_SHIFT; + vma->vm_flags |= VM_LOCKED | VM_DONTCOPY | VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); return remap_pfn_range( vma, From 925d473e5e9fa5f0d690289e09a600653405584c Mon Sep 17 00:00:00 2001 From: Robert Burger Date: Wed, 6 Mar 2024 08:10:37 +0100 Subject: [PATCH 12/12] fix: legacy interrupt working - It seems to be there is a mistake in the documentation of the FC1121 card. In chapter 3.2.1 Interrupt it is said that the global interrupt enable/status is bit 7 but instead it is bit 0 !!! - The FC1121 card is also configured to map DC Sycn0/1 interrupts to PDI interrupt for only 100 ns. With this configuration DC interrupts cannot be used when using standard OS like Linux or Windows. For processing inside an IRQ handler this is usually too short to determine the INT source. --- irq.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/irq.c b/irq.c index fb51b5d..8e22780 100644 --- a/irq.c +++ b/irq.c @@ -34,7 +34,10 @@ struct ccat_irq { #define CCAT_IRQ_GLOBAL_IRQ_STATUS_REG ((uint32_t)0x40u) // 1 byte #define CCAT_IRQ_GLOBAL_IRQ_ENABLE_REG ((uint32_t)0x50u) // 1 byte -#define CCAT_IRQ_GLOBAL_IRQ_ENABLE ((uint32_t)0x80u) +#define CCAT_IRQ_GLOBAL_IRQ_ENABLE ((uint32_t)0x01u) // It seems to be there is a mistake in the documentation of + // the FC1121 card. In chapter 3.2.1 Interrupt it is said that + // the global interrupt enable/status is bit 7 but instead it + // is bit 0 !!! #define CCAT_IRQ_DEVICES_MAX 4 @@ -48,6 +51,11 @@ static void ccat_irq_set_slot_irq_ctrl(struct ccat_function *func, uint16_t ctrl iowrite16(ctrl, func->ccat->bar_0 + func->info.addr + CCAT_IRQ_FUNCTION_IRQ__CONTROL_REG); } +static void ccat_irq_set_slot_irq_stat(struct ccat_function *func, uint16_t ctrl) +{ + iowrite16(ctrl, func->ccat->bar_0 + func->info.addr + CCAT_IRQ_FUNCTION_IRQ__STATUS_REG); +} + static uint8_t ccat_irq_get_global_irq_stat(struct ccat_function *func) { return ioread8(func->ccat->bar_2 + CCAT_IRQ_GLOBAL_IRQ_STATUS_REG); @@ -78,13 +86,16 @@ static irqreturn_t ccat_irq_int_handler(int int_no, void *arg) { ret = IRQ_HANDLED; } else { - if (global_state & 0x80) { + if (global_state & CCAT_IRQ_GLOBAL_IRQ_ENABLE) { if (ccat_irq_get_slot_irq_stat(buffer->ccdev->func) & CCAT_IRQ_FUNCTION_IRQ__SLOT) { // disable here until interrupt source is processed. // will be re-enable in poll ccat_irq_set_slot_irq_ctrl(buffer->ccdev->func, 0); wake_up(&irq->ir_queue); + // clear interrupt + ccat_irq_set_slot_irq_stat(buffer->ccdev->func, 0); + ret = IRQ_HANDLED; } } @@ -126,7 +137,7 @@ static int ccat_irq_open(struct inode *const i, struct file *const f) irq->irq_num = pci_irq_vector(ccdev->func->ccat->pdev, r); pr_info("Interrupt %d has been reserved, using irq name %s\n", irq->irq_num, &irq->name[0]); - if (request_irq(irq->irq_num, ccat_irq_int_handler, IRQF_NO_THREAD, &irq->name[0], buf)) { + if (request_irq(irq->irq_num, ccat_irq_int_handler, IRQF_NO_THREAD | IRQF_NOBALANCING, &irq->name[0], buf)) { pr_err("Interrupt %d reqeust failed!\n", irq->irq_num); irq_success = 0; } @@ -147,7 +158,7 @@ static int ccat_irq_open(struct inode *const i, struct file *const f) pr_info("Interrupt %d has been reserved, using irq name %s\n", irq->irq_num, &irq->name[0]); - if (request_irq(irq->irq_num, ccat_irq_int_handler, IRQF_SHARED, &irq->name[0], buf)) { + if (request_irq(irq->irq_num, ccat_irq_int_handler, IRQF_SHARED | IRQF_NOBALANCING, &irq->name[0], buf)) { pci_disable_device(ccdev->func->ccat->pdev); pr_err("Interrupt %d isn't free\n", irq->irq_num);