Skip to content
Snippets Groups Projects
Commit 0b27a3f7 authored by Federico Vaga's avatar Federico Vaga
Browse files

import htvic 1.3.0


Signed-off-by: default avatarFederico Vaga <federico.vaga@cern.ch>
parent 62f22c5f
Branches
Tags
No related merge requests found
...@@ -7,6 +7,7 @@ REPO_PARENT ?= $(shell /bin/pwd)/.. ...@@ -7,6 +7,7 @@ REPO_PARENT ?= $(shell /bin/pwd)/..
DIRS := i2c-ocores DIRS := i2c-ocores
DIRS += spi-ocores DIRS += spi-ocores
DIRS += htvic
.PHONY: all clean modules install modules_install $(DIRS) .PHONY: all clean modules install modules_install $(DIRS)
......
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileCopyrightText: 2019 CERN
all: modules
install: modules_install
clean modules modules_install:
@$(MAKE) -C drivers/ $@
gtags:
git ls-tree -r --name-only HEAD | grep -E '.*\.(c|h)$$' | gtags -f -
.PHONY: modules modules_install clean all gtags
HT Vector Interrupt Controller
==============================
This is the driver for the Vector Interrupt Controller HDL component
developed in BE-CO-HT (CERN).
::
CARRIER
.------------------------------.
| FPGA |
| .----------------------. |
| | .--------. | |
| | .------| core-1 | | |
| | v '--------' | |
.-------------. | | .-----. .--------. | |
| motherboard |<-------------------------| vic |<--| core-2 | | |
'-------------' | | '-----' '--------' | |
| | ^ .--------. | |
| | '------| core-N | | |
| | '--------' | |
| '----------------------' |
'------------------------------'
Driver
------
The driver is a platform driver that registers an irq chip for the
correspondent IP core present on the FPGA (the vic). The driver for
the IRQ chip will be connected in cascade to the IRQ controller
assigned to the FPGA carrier by the operating system.
From the electrical point of view, when one of the cores rise an
interrupt line the VIC will keep track of it; the VIC rises
the carrier interrupt line that will, finally, arrive to the
motherboard. This will trigger the operating system, which will
try to handle the interrupt by calling the IRQ handler associated
to the carrier interrupt line. The carrier interrupt line has been
connected directly to the VIC, so the control passes to the VIC
driver which will call the IRQ handler associated to the risen
interrupt line.
The purpose of the VIC driver (IRQ controller) is to configure the VIC
IP core and, dispatch the control to IRQ handlers that belong to it and
to acknowledge the IRQ lines when the interrupt has been handled.
The driver uses the standard Linux interface, this means that any driver
can map an IRQ handler using ``request_irq``. In principle all the Linux IRQ
API should work.
The HTVIC uses the IRQ domain concept, so each HTVIC instance has its own
IRQ domain. Any driver who wants to retrieve the Linux IRQ number
associated to an HTVIC can use the IRQ domain.
::
irqdomain = irq_find_host((struct device_node *)irqdomain_name);
linux_irq_number = irq_find_mapping(irqdomain, HW_IRQ_NUMBER);
request_irq(linux_irq_number, ...);
A non trivial problem can be the detection of the correct domain name.
The source of this information can only be the FPGA carrier, or an user
space process but there is not an unique solution neither a standard one.
All the IRQs handled by the VIC IRQ controller will be visible as any
other IRQ from the operating system. By doing ``cat /proc/interrupts``,
among the other IRQ handlers, you will get the ones belonging to the VIC
IRQ controller. You can identify them because of the prefix "HT-VIC"
::
CPU0 CPU1
[...]
281: 0 0 HT-VIC fmc-tdc-svec.0
282: 0 0 HT-VIC fdelay-tdc-svec.0
283: 2 0 HT-VIC mock-turtle-svec.57005
284: 4 0 HT-VIC mock-turtle-svec.57005
313: 0 0 HT-VIC fmc-tdc-svec.1
314: 0 0 HT-VIC fdelay-tdc-svec.1
315: 2 0 HT-VIC mock-turtle-svec.48879
316: 4 0 HT-VIC mock-turtle-svec.48879
345: 5 0 HT-VIC adc-100m-svec.0
346: 0 0 HT-VIC adc-100m-svec.1
[...]
Debug
-----
This driver has a *debugfs* interface. This means that you need the debug
file-system mounted first::
mount -t debugfs none /sys/kernel/debug
The driver exports two read-only files:
info
It contains a YAML file with general information about the device instance
reg
It shows the VIC memory dump
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2019 CERN
ifdef CONFIG_SUPER_REPO
ifdef CONFIG_SUPER_REPO_VERSION
SUBMODULE_VERSIONS-y += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPER_REPO_VERSION)\");
endif
endif
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS-y)"
ccflags-y += -Werror
obj-m := htvic.o
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2019 CERN
-include Makefile.specific
REPO_PARENT ?= $(shell /bin/pwd)/../..
-include $(REPO_PARENT)/parent_common.mk
LINUX ?= /lib/modules/$(shell uname -r)/build
all: modules
clean modules help modules_install:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) $@
.PHONY: all modules clean help install modules_install
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 CERN
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Driver for the HT-VIC IRQ controller
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include "htvic.h"
static int htvic_dbg_info(struct seq_file *s, void *offset)
{
struct htvic_device *htvic = s->private;
int i;
seq_printf(s, "%s:\n",dev_name(&htvic->pdev->dev));
seq_printf(s, " redirect: %d\n", platform_get_irq(htvic->pdev, 0));
seq_printf(s, " irq-mapping:\n");
for (i = 0; i < VIC_MAX_VECTORS; ++i) {
seq_printf(s, " - hardware: %d\n", i);
seq_printf(s, " linux: %d\n",
irq_find_mapping(htvic->domain, i));
}
return 0;
}
static int htvic_dbg_info_open(struct inode *inode, struct file *file)
{
struct htvic_device *htvic = inode->i_private;
return single_open(file, htvic_dbg_info, htvic);
}
static const struct file_operations htvic_dbg_info_ops = {
.owner = THIS_MODULE,
.open = htvic_dbg_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int htvic_dbg_reg(struct seq_file *s, void *offset)
{
struct htvic_device *htvic = s->private;
uint32_t val;
void *addr;
int i;
#define VIC_REG_N 8
for (i = 0, addr = htvic->kernel_va; i < VIC_REG_N ; ++i, addr +=4) {
val = htvic_ioread(htvic, addr);
seq_printf(s, "%p = 0x%08x\n", addr, val);
}
for (i = 0, addr = htvic->kernel_va + VIC_IVT_RAM_BASE;
i < VIC_MAX_VECTORS; ++i, addr +=4) {
val = htvic_ioread(htvic, addr);
seq_printf(s, "%p = 0x%08x\n", addr, val);
}
return 0;
}
static int htvic_dbg_reg_open(struct inode *inode, struct file *file)
{
struct htvic_device *htvic = inode->i_private;
return single_open(file, htvic_dbg_reg, htvic);
}
static const struct file_operations htvic_dbg_reg_ops = {
.owner = THIS_MODULE,
.open = htvic_dbg_reg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t htvic_dbg_swirq_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
struct htvic_device *vic = file->private_data;
htvic_iowrite(vic, 1, vic->kernel_va + VIC_REG_SWIR);
return count;
}
static int htvic_dbg_swirq_open(struct inode *inode, struct file *file)
{
struct htvic_device *htvic = inode->i_private;
file->private_data = htvic;
return 0;
}
static const struct file_operations htvic_dbg_swirq_ops = {
.owner = THIS_MODULE,
.open = htvic_dbg_swirq_open,
.write = htvic_dbg_swirq_write,
};
/**
* It initializes the debugfs interface
* @htvic: IRQ controler instance
*
* Return: 0 on success, otherwise a negative error number
*/
static int htvic_debug_init(struct htvic_device *htvic)
{
htvic->dbg_dir = debugfs_create_dir(dev_name(&htvic->pdev->dev), NULL);
if (IS_ERR_OR_NULL(htvic->dbg_dir)) {
dev_err(&htvic->pdev->dev,
"Cannot create debugfs directory (%ld)\n",
PTR_ERR(htvic->dbg_dir));
return PTR_ERR(htvic->dbg_dir);
}
htvic->dbg_info = debugfs_create_file(HTVIC_DBG_INFO_NAME, 0444,
htvic->dbg_dir, htvic,
&htvic_dbg_info_ops);
if (IS_ERR_OR_NULL(htvic->dbg_info)) {
dev_err(&htvic->pdev->dev,
"Cannot create debugfs file \"%s\" (%ld)\n",
HTVIC_DBG_INFO_NAME, PTR_ERR(htvic->dbg_info));
return PTR_ERR(htvic->dbg_info);
}
htvic->dbg_reg = debugfs_create_file(HTVIC_DBG_REG_NAME, 0444,
htvic->dbg_dir, htvic,
&htvic_dbg_reg_ops);
if (IS_ERR_OR_NULL(htvic->dbg_reg)) {
dev_err(&htvic->pdev->dev,
"Cannot create debugfs file \"%s\" (%ld)\n",
HTVIC_DBG_REG_NAME, PTR_ERR(htvic->dbg_reg));
return PTR_ERR(htvic->dbg_reg);
}
htvic->dbg_swirq = debugfs_create_file(HTVIC_DBG_SWIRQ_NAME, 0200,
htvic->dbg_dir, htvic,
&htvic_dbg_swirq_ops);
if (IS_ERR_OR_NULL(htvic->dbg_swirq)) {
dev_err(&htvic->pdev->dev,
"Cannot create debugfs file \"%s\" (%ld)\n",
HTVIC_DBG_SWIRQ_NAME, PTR_ERR(htvic->dbg_swirq));
return PTR_ERR(htvic->dbg_reg);
}
return 0;
}
/**
* It removes the debugfs interface
* @htvic: IRQ controler instance
*/
static void htvic_debug_exit(struct htvic_device *htvic)
{
if (htvic->dbg_dir)
debugfs_remove_recursive(htvic->dbg_dir);
}
/**
* End of interrupt for the VIC. In case of level interrupt,
* there is no way to delegate this to the kernel
*/
static void htvic_eoi(struct irq_data *d)
{
struct htvic_device *vic = irq_data_get_irq_chip_data(d);
/*
* Any write operation acknowledges the pending interrupt.
* Then, VIC advances to another pending interrupt(s) or
* releases the master interrupt output.
*/
htvic_iowrite(vic, 1, vic->kernel_va + VIC_REG_EOIR);
}
static void htvic_mask_disable_reg(struct irq_data *d)
{
struct htvic_device *htvic = irq_data_get_irq_chip_data(d);
htvic_iowrite(htvic, 1 << d->hwirq,
htvic->kernel_va + VIC_REG_IDR);
}
static void htvic_unmask_enable_reg(struct irq_data *d)
{
struct htvic_device *htvic = irq_data_get_irq_chip_data(d);
htvic_iowrite(htvic, 1 << d->hwirq,
htvic->kernel_va + VIC_REG_IER);
}
static void htvic_irq_ack(struct irq_data *d)
{
}
/**
*
*/
static unsigned int htvic_irq_startup(struct irq_data *d)
{
struct htvic_device *vic = irq_data_get_irq_chip_data(d);
int ret;
ret = try_module_get(vic->pdev->dev.driver->owner);
if (ret == 0) { /* 0 fail, 1 success */
dev_err(&vic->pdev->dev,
"Cannot pin the \"%s\" driver. Something really wrong is going on\n",
vic->pdev->dev.driver->name);
return 1;
}
htvic_unmask_enable_reg(d);
return 0;
}
/**
* Executed when a driver does `free_irq()`.
*/
static void htvic_irq_shutdown(struct irq_data *d)
{
struct htvic_device *vic = irq_data_get_irq_chip_data(d);
htvic_mask_disable_reg(d);
module_put(vic->pdev->dev.driver->owner);
}
static int htvic_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct htvic_device *vic = irq_data_get_irq_chip_data(d);
/* We support only levels */
if (!(flow_type & IRQ_TYPE_LEVEL_MASK)) {
dev_err(&vic->pdev->dev,
"%s: unsopported type 0x%x\n", __func__, flow_type);
return -EINVAL;
}
return IRQ_SET_MASK_OK;
}
static struct irq_chip htvic_chip = {
.name = "HT-VIC",
.irq_startup = htvic_irq_startup,
.irq_shutdown = htvic_irq_shutdown,
.irq_ack = htvic_irq_ack,
.irq_eoi = htvic_eoi,
.irq_mask_ack = htvic_mask_disable_reg,
.irq_mask = htvic_mask_disable_reg,
.irq_unmask = htvic_unmask_enable_reg,
.irq_set_type = htvic_irq_set_type,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
static int htvic_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token)
{
struct htvic_device *htvic = d->host_data;
/*
* FIXME this should point to htvic->pdev->dev.parent. Today it is not
* a problem for CERN-like installations, so we leave it like this
* so that the WR-stating kit works.
*/
struct device *dev = &htvic->pdev->dev;
struct device *req_dev;
if(fwspec->param_count != 2)
return 0;
req_dev = (struct device *) ((((unsigned long) fwspec->param[0]) << 32) |
(((unsigned long) fwspec->param[1]) & 0xFFFFFFFF));
return (dev == req_dev);
}
#endif
/**
* Given the hardware IRQ and the Linux IRQ number (virtirq), configure the
* Linux IRQ number in order to handle properly the incoming interrupts
* on the hardware IRQ line.
*/
static int htvic_irq_domain_map(struct irq_domain *h,
unsigned int virtirq,
irq_hw_number_t hwirq)
{
struct htvic_device *htvic = h->host_data;
irq_set_chip_data(virtirq, htvic);
irq_set_chip(virtirq, &htvic_chip);
irq_set_handler(virtirq, handle_level_irq); /* not really used now */
/* all handlers are directly nested */
irq_set_nested_thread(virtirq, 1);
/*
* It MUST be no-thread because the VIC EOI must occur AFTER
* the device handler ack its signal. Any way the interrupt from
* the carrier is already threaded (most likely, if not we will
* see problems)
*/
return 0;
}
static struct irq_domain_ops htvic_irq_domain_ops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
.select = htvic_irq_domain_select,
#endif
.map = htvic_irq_domain_map,
};
/**
* Mapping of HTVIC irqs to Linux irqs using linear IRQ domain
*/
static int htvic_irq_mapping(struct htvic_device *htvic)
{
int i, irq;
htvic->domain = irq_domain_add_linear((void *)&htvic->pdev->dev,
VIC_MAX_VECTORS,
&htvic_irq_domain_ops, htvic);
if (!htvic->domain)
return -ENOMEM;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
htvic->domain->name = kasprintf(GFP_KERNEL, "%s",
dev_name(&htvic->pdev->dev));
#endif
/* Create the mapping between HW irq and virtual IRQ number */
for (i = 0; i < VIC_MAX_VECTORS; ++i) {
htvic->hwid[i] = htvic_ioread(htvic, htvic->kernel_va +
VIC_IVT_RAM_BASE + 4 * i);
htvic_iowrite(htvic, i,
htvic->kernel_va + VIC_IVT_RAM_BASE + 4 * i);
irq = irq_create_mapping(htvic->domain, i);
if (irq <= 0)
goto out;
}
return 0;
out:
irq_domain_remove(htvic->domain);
return -EPERM;
}
/**
* Check if the platform is providing all the necessary information
* for the HTVIC to work properly.
*
* The HTVIC needs the following informations:
* - a Linux IRQ number where it should attach itself
* - a virtual address where to find the component
*/
static int htvic_validation(struct platform_device *pdev)
{
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!r) {
dev_err(&pdev->dev, "Carrier IRQ number is missing\n");
return -EINVAL;
}
if (!(r->flags & (IORESOURCE_IRQ_HIGHEDGE |
IORESOURCE_IRQ_LOWEDGE |
IORESOURCE_IRQ_HIGHLEVEL |
IORESOURCE_IRQ_LOWLEVEL))) {
dev_err(&pdev->dev,
"Edge/Level High/Low information missing\n");
return -EINVAL;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, HTVIC_MEM_BASE);
if (!r) {
dev_err(&pdev->dev, "VIC base address is missing\n");
return -EINVAL;
}
return 0;
}
/**
* It acks any pending interrupt in order to avoid to bring the HTVIC
* to a stable status (possibly)
*/
static inline void htvic_ack_pending(struct htvic_device *htvic)
{
while (htvic_ioread(htvic, htvic->kernel_va + VIC_REG_RISR))
htvic_iowrite(htvic, 1, htvic->kernel_va + VIC_REG_EOIR);
}
/**
* This is the place to re-route interrupts to the proper handler
*/
static irqreturn_t htvic_handler(int irq, void *arg)
{
struct htvic_device *htvic = arg;
u32 risr;
risr = htvic_ioread(htvic, htvic->kernel_va + VIC_REG_RISR);
if (!risr) /* Nothing to do - not for us */
return IRQ_NONE;
do {
unsigned int cascade_irq;
uint32_t vect;
vect = htvic_ioread(htvic, htvic->kernel_va + VIC_REG_VAR) & 0xFF;
if (WARN(vect >= VIC_MAX_VECTORS,
"Invalid vector number %d\n", vect))
return IRQ_HANDLED;
cascade_irq = irq_find_mapping(htvic->domain, vect);
dev_dbg(&htvic->pdev->dev, "Raw: 0x%x Vect: 0x%x, IRQ: %d\n",
risr, vect, cascade_irq);
risr &= ~(1 << vect);
/*
* Ok, now we execute the handler for the given IRQ. Please
* note that this is not the action requested by the device driver
* but it is the handler defined during the IRQ mapping
*/
handle_nested_irq(cascade_irq);
/**
* ATTENTION here the ack is actually an EOI.The kernel
* does not export the handle_edge_eoi_irq() handler which
* is the one we need here. The kernel offers us the
* handle_edge_irq() which use only the ack() function.
* So what actually we need to to is to call the ack
* function but not the eoi function.
*/
htvic_eoi(irq_get_irq_data(cascade_irq));
/*
* Read the RISR register again (it could be any other
* register) to introduce a delay equivalent to the time
* necessary for the VIC to propagate the IRQ status line
* to the processor.
*/
htvic_ioread(htvic, htvic->kernel_va + VIC_REG_RISR);
} while(risr);
return IRQ_HANDLED;
}
/**
* Create a new instance for this driver.
*/
static int htvic_probe(struct platform_device *pdev)
{
struct htvic_device *htvic;
const struct resource *r;
unsigned long irq_flags = 0;
uint32_t ctl;
int ret;
ret = htvic_validation(pdev);
if (ret)
return ret;
htvic = kzalloc(sizeof(struct htvic_device), GFP_KERNEL);
if (!htvic)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, htvic);
htvic->pdev = pdev;
/*
* TODO theoretically speaking all the confguration should come
* from a platform_data structure. Since we do not have it yet,
* we proceed this way
*/
switch(pdev->id_entry->driver_data) {
case HTVIC_VER_SPEC:
case HTVIC_VER_WRSWI:
htvic->memop.read = __htvic_ioread32;
htvic->memop.write = __htvic_iowrite32;
break;
case HTVIC_VER_SVEC:
htvic->memop.read = __htvic_ioread32be;
htvic->memop.write = __htvic_iowrite32be;
break;
default:
dev_err(&pdev->dev, "Can't identify memory operations\n");
ret = -EINVAL;
goto out_memop;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, HTVIC_MEM_BASE);
htvic->kernel_va = ioremap(r->start, resource_size(r));
/* Disable the VIC during the configuration */
htvic_iowrite(htvic, 0, htvic->kernel_va + VIC_REG_CTL);
/* Disable also all interrupt lines */
htvic_iowrite(htvic, ~0, htvic->kernel_va + VIC_REG_IDR);
/* Ack any pending interrupt */
htvic_ack_pending(htvic);
ret = htvic_irq_mapping(htvic);
if (ret)
goto out_map;
/* VIC configuration */
ctl = 0;
ctl |= VIC_CTL_ENABLE;
irq_flags |= IRQF_SHARED;
switch (pdev->id_entry->driver_data) {
case HTVIC_VER_SPEC:
ctl |= VIC_CTL_POL;
ctl |= VIC_CTL_EMU_EDGE;
ctl |= VIC_CTL_EMU_LEN_W(250);
irq_flags |= IRQF_TRIGGER_HIGH;
break;
case HTVIC_VER_SVEC:
ctl |= VIC_CTL_POL;
irq_flags |= IRQF_TRIGGER_HIGH;
/* TODO what if we want and edge using the edge emulator? */
break;
case HTVIC_VER_WRSWI:
break;
default:
goto out_ctl;
}
/*
* It depends on the platform and on the IRQ on which we are connecting
* but most likely our interrupt handler will be a thread
*/
htvic->irq = platform_get_irq(htvic->pdev, 0);
ret = request_any_context_irq(htvic->irq,
htvic_handler, irq_flags,
dev_name(&pdev->dev),
htvic);
if (ret < 0) {
dev_err(&pdev->dev, "Can't request IRQ %d (%d)\n",
platform_get_irq(htvic->pdev, 0), ret);
goto out_req;
}
htvic_debug_init(htvic);
htvic_iowrite(htvic, ctl, htvic->kernel_va + VIC_REG_CTL);
return 0;
out_req:
out_ctl:
out_map:
out_memop:
dev_set_drvdata(&pdev->dev, NULL);
kfree(htvic);
return ret;
}
/**
* Unload the htvic driver from the platform
*/
static int htvic_remove(struct platform_device *pdev)
{
struct htvic_device *htvic = dev_get_drvdata(&pdev->dev);
/* struct irq_desc *desc = irq_to_desc(platform_get_irq(htvic->pdev, 0)); */
int i;
if (!htvic)
return 0;
htvic_debug_exit(htvic);
/*
* Disable all interrupts to prevent spurious interrupt
* Disable also the HTVIC component for the very same reason,
* but this way on next instance even if we enable the VIC
* no interrupt will come unless configured.
*/
htvic_iowrite(htvic, ~0, htvic->kernel_va + VIC_REG_IDR);
htvic_iowrite(htvic, 0, htvic->kernel_va + VIC_REG_CTL);
/*
* Restore HTVIC vector table with it's original content
* Release Linux IRQ number
*/
for (i = 0; i < VIC_MAX_VECTORS; i++) {
htvic_iowrite(htvic, htvic->hwid[i], htvic->kernel_va + VIC_IVT_RAM_BASE + 4 * i);
irq_dispose_mapping(irq_find_mapping(htvic->domain, i));
}
free_irq(htvic->irq, htvic);
/*
* Clear the memory and restore flags when needed
*/
irq_domain_remove(htvic->domain);
kfree(htvic);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
/**
* List of supported platform
*/
static const struct platform_device_id htvic_id_table[] = {
{ /* SPEC compatible */
.name = "htvic-spec",
.driver_data = HTVIC_VER_SPEC,
}, { /* SVEC compatible */
.name = "htvic-svec",
.driver_data = HTVIC_VER_SVEC,
}, {
.name = "htvic-wr-swi",
.driver_data = HTVIC_VER_WRSWI,
},
{},
};
static struct platform_driver htvic_driver = {
.driver = {
.name = "htvic",
.owner = THIS_MODULE,
},
.id_table = htvic_id_table,
.probe = htvic_probe,
.remove = htvic_remove,
};
module_platform_driver(htvic_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CERN BECOHT VHDL Vector Interrupt Controller - HTVIC");
MODULE_DEVICE_TABLE(platform, htvic_id_table);
ADDITIONAL_VERSIONS;
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2016 CERN
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __HTVIC_H__
#define __HTVIC_H__
#include <linux/debugfs.h>
#include "htvic_regs.h"
#define VIC_MAX_VECTORS 32
#define VIC_SDB_VENDOR 0xce42
#define VIC_SDB_DEVICE 0x0013
#define VIC_IRQ_BASE_NUMBER 0
enum htvic_versions {
HTVIC_VER_SPEC = 0,
HTVIC_VER_SVEC,
HTVIC_VER_WRSWI,
};
enum htvic_mem_resources {
HTVIC_MEM_BASE = 0,
};
struct htvic_data {
uint32_t is_edge; /* 1 edge, 0 level */
uint32_t is_raising; /* 1 raising, 0 falling */
uint32_t pulse_len;
};
struct memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
};
struct htvic_device {
struct platform_device *pdev;
struct irq_domain *domain;
unsigned int hwid[VIC_MAX_VECTORS]; /**> original ID from FPGA */
struct htvic_data *data;
void __iomem *kernel_va;
struct memory_ops memop;
int irq;
irq_flow_handler_t platform_handle_irq;
void *platform_handler_data;
struct dentry *dbg_dir;
#define HTVIC_DBG_INFO_NAME "info"
struct dentry *dbg_info;
#define HTVIC_DBG_REG_NAME "reg"
struct dentry *dbg_reg;
#define HTVIC_DBG_SWIRQ_NAME "swirq"
struct dentry *dbg_swirq;
};
static inline u32 htvic_ioread(struct htvic_device *htvic, void __iomem *addr)
{
return htvic->memop.read(addr);
}
static inline void htvic_iowrite(struct htvic_device *htvic,
u32 value, void __iomem *addr)
{
return htvic->memop.write(value, addr);
}
static inline u32 __htvic_ioread32(void *addr)
{
return ioread32(addr);
}
static inline u32 __htvic_ioread32be(void *addr)
{
return ioread32be(addr);
}
static inline void __htvic_iowrite32(u32 value,void __iomem *addr)
{
iowrite32(value, addr);
}
static inline void __htvic_iowrite32be(u32 value, void __iomem *addr)
{
iowrite32be(value, addr);
}
#endif
/*
Register definitions for slave core: Vectored Interrupt Controller (VIC)
* File : here.h
* Author : auto-generated by wbgen2 from wb_slave_vic.wb
* Created : Thu Jul 14 15:43:13 2016
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE wb_slave_vic.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_WB_SLAVE_VIC_WB
#define __WBGEN2_REGDEFS_WB_SLAVE_VIC_WB
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <inttypes.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: VIC Control Register */
/* definitions for field: VIC Enable in reg: VIC Control Register */
#define VIC_CTL_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: VIC output polarity in reg: VIC Control Register */
#define VIC_CTL_POL WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Emulate Edge sensitive output in reg: VIC Control Register */
#define VIC_CTL_EMU_EDGE WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Emulated Edge pulse timer in reg: VIC Control Register */
#define VIC_CTL_EMU_LEN_MASK WBGEN2_GEN_MASK(3, 16)
#define VIC_CTL_EMU_LEN_SHIFT 3
#define VIC_CTL_EMU_LEN_W(value) WBGEN2_GEN_WRITE(value, 3, 16)
#define VIC_CTL_EMU_LEN_R(reg) WBGEN2_GEN_READ(reg, 3, 16)
/* definitions for register: Raw Interrupt Status Register */
/* definitions for register: Interrupt Enable Register */
/* definitions for register: Interrupt Disable Register */
/* definitions for register: Interrupt Mask Register */
/* definitions for register: Vector Address Register */
/* definitions for register: Software Interrupt Register */
/* definitions for register: End Of Interrupt Acknowledge Register */
/* definitions for RAM: Interrupt Vector Table */
#define VIC_IVT_RAM_BASE 0x00000080 /* base address */
#define VIC_IVT_RAM_BYTES 0x00000080 /* size in bytes */
#define VIC_IVT_RAM_WORDS 0x00000020 /* size in 32-bit words, 32-bit aligned */
/* [0x0]: REG VIC Control Register */
#define VIC_REG_CTL 0x00000000
/* [0x4]: REG Raw Interrupt Status Register */
#define VIC_REG_RISR 0x00000004
/* [0x8]: REG Interrupt Enable Register */
#define VIC_REG_IER 0x00000008
/* [0xc]: REG Interrupt Disable Register */
#define VIC_REG_IDR 0x0000000c
/* [0x10]: REG Interrupt Mask Register */
#define VIC_REG_IMR 0x00000010
/* [0x14]: REG Vector Address Register */
#define VIC_REG_VAR 0x00000014
/* [0x18]: REG Software Interrupt Register */
#define VIC_REG_SWIR 0x00000018
/* [0x1c]: REG End Of Interrupt Acknowledge Register */
#define VIC_REG_EOIR 0x0000001c
#endif
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment