Commit eed4e16d authored by Federico Vaga's avatar Federico Vaga

kernel: remove vic and add GN IRQ controller

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 3af5e8af
...@@ -37,6 +37,7 @@ spec-y += spec-vic.o ...@@ -37,6 +37,7 @@ spec-y += spec-vic.o
spec-y += loader-ll.o spec-y += loader-ll.o
spec-y += spec-gpio-no.o spec-y += spec-gpio-no.o
spec-$(CONFIG_GPIOLIB) += spec-gpio.o spec-$(CONFIG_GPIOLIB) += spec-gpio.o
spec-y += spec-irq.o
wr-nic-y = wr-nic-core.o wr-nic-y = wr-nic-core.o
wr-nic-y += wr-nic-eth.o wr-nic-y += wr-nic-eth.o
......
...@@ -15,6 +15,7 @@ export GIT_VERSION ...@@ -15,6 +15,7 @@ export GIT_VERSION
FMC_BUS_VERSION ?= $(shell cd $(FMC_BUS_ABS); git describe --always --dirty --long --tags) FMC_BUS_VERSION ?= $(shell cd $(FMC_BUS_ABS); git describe --always --dirty --long --tags)
export FMC_BUS_VERSION export FMC_BUS_VERSION
spec-y += spec-irq.o
all modules: all modules:
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* This work is part of the White Rabbit project, a research effort led * This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research. * by CERN, the European Institute for Nuclear Research.
*/ */
#include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fmc.h> #include <linux/fmc.h>
...@@ -22,6 +23,7 @@ module_param_named(test_irq, spec_test_irq, int, 0444); ...@@ -22,6 +23,7 @@ module_param_named(test_irq, spec_test_irq, int, 0444);
static int spec_show_sdb; static int spec_show_sdb;
module_param_named(show_sdb, spec_show_sdb, int, 0444); module_param_named(show_sdb, spec_show_sdb, int, 0444);
/* The main role of this file is offering the fmc_operations for the spec */ /* The main role of this file is offering the fmc_operations for the spec */
static int spec_validate(struct fmc_device *fmc, struct fmc_driver *drv) static int spec_validate(struct fmc_device *fmc, struct fmc_driver *drv)
...@@ -110,170 +112,6 @@ out: ...@@ -110,170 +112,6 @@ out:
return ret; return ret;
} }
/* Low-level IRQ request function: set up handler, with or without the VIC */
static int spec_shared_irq_request(struct fmc_device *fmc,
irq_handler_t handler, char *name, int flags)
{
struct spec_dev *spec = fmc->carrier_data;
int ret;
u32 value;
ret = request_irq(spec->pdev->irq, handler, flags, name, fmc);
if (ret)
return ret;
if (spec_use_msi) {
/* A check and a hack, but doesn't work on all computers */
value = gennum_readl(spec, GNPPCI_MSI_CONTROL);
if ((value & 0x810000) != 0x810000)
dev_err(&spec->pdev->dev, "invalid msi control: "
"0x%04x\n", value >> 16);
value = 0xa50000 | (value & 0xffff);
gennum_writel(spec, value, GNPPCI_MSI_CONTROL);
}
/* Interrupts are enabled by the driver, with gpio_config() */
return 0;
}
static void spec_shared_irq_ack(struct fmc_device *fmc);
static irqreturn_t spec_vic_irq_handler(int id, void *data)
{
struct fmc_device *fmc = (struct fmc_device *)data;
struct spec_dev *spec = (struct spec_dev *)fmc->carrier_data;
irqreturn_t rv;
/*
* Lock to avoid to remove the VIC or the handler itself while
* it's running
*/
spin_lock(&spec->irq_lock);
rv = spec_vic_irq_dispatch(spec);
/*
* We ack the GN4124 **before** the VIC. The other way around makes the
* interrupt handling timing dependent. For instance. When the VIC
* VIC_CTL_EMU_LEN_W is too short it may happens that the VIC asserts
* the interrupt **before** the GN4124 is acked; then, so do not receive
* interrupts anymore from the GN4124 because it misses the VIC edge.
*/
spec_shared_irq_ack(fmc);
spec_vic_irq_ack(spec, 0);
spin_unlock(&spec->irq_lock);
return IRQ_HANDLED;
}
static struct fmc_gpio spec_vic_gpio_cfg[] = {
{
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
},
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
char *name, int flags)
{
struct spec_dev *spec = fmc->carrier_data;
int rv, first_time;
/* VIC mode interrupt */
if (!(flags & IRQF_SHARED)) {
/*
* Lock to serialize request/free and have a consistent status
* during these operations
*/
spin_lock(&spec->irq_lock);
first_time = !spec->vic;
/* configure the VIC */
rv = spec_vic_irq_request(spec, fmc, fmc->irq, handler);
spin_unlock(&spec->irq_lock);
if (rv)
return rv;
/* on first IRQ, configure VIC "master" handler and GPIO too */
if (first_time) {
rv = spec_shared_irq_request(fmc, spec_vic_irq_handler,
"spec-vic", IRQF_SHARED);
if (rv)
return rv;
fmc->op->gpio_config(fmc, spec_vic_gpio_cfg,
ARRAY_SIZE(spec_vic_gpio_cfg));
}
} else {
rv = spec_shared_irq_request(fmc, handler, name, flags);
pr_debug("Requesting irq '%s' in shared mode (rv %d)\n", name,
rv);
}
if (!rv)
spec->flags |= SPEC_FLAG_IRQS_REQUESTED;
return rv;
}
static void spec_shared_irq_ack(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
/*
* Note: we only support gpio interrupts here, i.e. the
* 0x814 (INT_STAT) register is expected to only have bit 15 set.
* We also accept software-generated irq, but they need no ack.
*/
gennum_readl(spec, GNGPIO_INT_STATUS);
}
static void spec_irq_ack(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
if (!spec->vic)
spec_shared_irq_ack(fmc);
/* Nothing for VIC here, all irqs are acked by master VIC handler */
}
static void spec_shared_irq_free(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
gennum_writel(spec, 0xffff, GNGPIO_INT_MASK_SET); /* disable */
free_irq(spec->pdev->irq, fmc);
}
static int spec_irq_free(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
/*
* Lock to serialize request/free and have a consistent status
* during these operations. And, to avoid to run VIC handler while
* it is going to desappear
*/
spin_lock(&spec->irq_lock);
if (spec->vic)
spec_vic_irq_free(spec, fmc->irq);
/*
* If we were not using the VIC, or we released all the VIC handler, then
* release the PCI IRQ handler
*/
if (!spec->vic)
spec_shared_irq_free(fmc);
spin_unlock(&spec->irq_lock);
return 0;
}
/* This is the mapping from virtual GPIO pin numbers to raw gpio numbers */ /* This is the mapping from virtual GPIO pin numbers to raw gpio numbers */
struct { struct {
...@@ -408,53 +246,11 @@ static struct fmc_operations spec_fmc_operations = { ...@@ -408,53 +246,11 @@ static struct fmc_operations spec_fmc_operations = {
.validate = spec_validate, .validate = spec_validate,
.reprogram_raw = spec_reprogram_raw, .reprogram_raw = spec_reprogram_raw,
.reprogram = spec_reprogram, .reprogram = spec_reprogram,
.irq_request = spec_irq_request,
.irq_ack = spec_irq_ack,
.irq_free = spec_irq_free,
.gpio_config = spec_gpio_config, .gpio_config = spec_gpio_config,
.read_ee = spec_read_ee, .read_ee = spec_read_ee,
.write_ee = spec_write_ee, .write_ee = spec_write_ee,
}; };
/*
* Since interrupts are a hairy thing with the gennum, make a test run
* of interrupt handling using its own internal "software interrupt"
*/
static irqreturn_t spec_test_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
struct spec_dev *spec = fmc->carrier_data;
dev_info(fmc->hwdev, "received interrupt %i\n", irq);
spec->irq_count++;
complete(&spec->compl);
fmc->op->irq_ack(fmc);
return IRQ_HANDLED;
}
static int spec_irq_test(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
spec->irq_count = 0;
init_completion(&spec->compl);
fmc->op->irq_request(fmc, spec_test_handler, "spec-test", IRQF_SHARED);
gennum_writel(spec, 8, GNINT_STAT);
gennum_writel(spec, 0, GNINT_STAT);
wait_for_completion_timeout(&spec->compl, msecs_to_jiffies(50));
fmc->op->irq_free(fmc);
if (!spec->irq_count) {
dev_err(&spec->pdev->dev, "Can't receive interrupt\n");
return -EIO;
}
dev_info(&spec->pdev->dev, "Interrupts work as expected\n");
/* FIXME: configure the GPIO pins to receive interrupts */
return 0;
}
/* /*
* Finally, the real init and exit * Finally, the real init and exit
...@@ -463,7 +259,7 @@ static int spec_irq_init(struct fmc_device *fmc) ...@@ -463,7 +259,7 @@ static int spec_irq_init(struct fmc_device *fmc)
{ {
struct spec_dev *spec = fmc->carrier_data; struct spec_dev *spec = fmc->carrier_data;
uint32_t value; uint32_t value;
int i; int i, ret;
spin_lock_init(&spec->irq_lock); spin_lock_init(&spec->irq_lock);
if (spec_use_msi) { if (spec_use_msi) {
...@@ -491,9 +287,9 @@ static int spec_irq_init(struct fmc_device *fmc) ...@@ -491,9 +287,9 @@ static int spec_irq_init(struct fmc_device *fmc)
else else
gennum_writel(spec, 0x800c, GNINT_CFG(0 /* first one */ )); gennum_writel(spec, 0x800c, GNINT_CFG(0 /* first one */ ));
/* Finally, ensure we are able to receive it -- if the user asked to */ ret = gn4124_irq_domain_create(spec);
if (spec_test_irq) if (ret)
return spec_irq_test(fmc); return ret;
return 0; return 0;
} }
...@@ -505,7 +301,8 @@ static void spec_irq_exit(struct fmc_device *fmc) ...@@ -505,7 +301,8 @@ static void spec_irq_exit(struct fmc_device *fmc)
for (i = 0; i < 7; i++) for (i = 0; i < 7; i++)
gennum_writel(spec, 0, GNINT_CFG(i)); gennum_writel(spec, 0, GNINT_CFG(i));
fmc->op->irq_ack(fmc); /* just to be safe */
gn4124_irq_domain_destroy(spec);
WARN(spec->vic, "A Mezzanine driver didn't release all its IRQ handlers\n"); WARN(spec->vic, "A Mezzanine driver didn't release all its IRQ handlers\n");
} }
......
/*
* Copyright (C) 2015 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/version.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include "spec.h"
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,9,0)
#include <linux/irqchip/chained_irq.h>
#else
static inline void chained_irq_enter(struct irq_chip *chip,
struct irq_desc *desc)
{
/* FastEOI controllers require no action on entry. */
if (chip->irq_eoi)
return;
if (chip->irq_mask_ack) {
chip->irq_mask_ack(&desc->irq_data);
} else {
chip->irq_mask(&desc->irq_data);
if (chip->irq_ack)
chip->irq_ack(&desc->irq_data);
}
}
static inline void chained_irq_exit(struct irq_chip *chip,
struct irq_desc *desc)
{
if (chip->irq_eoi)
chip->irq_eoi(&desc->irq_data);
else
chip->irq_unmask(&desc->irq_data);
}
#endif
/**
* the interrupt is over, ack the GN4124 chip
*/
static void gn4124_irq_ack(struct irq_data *d)
{
struct spec_dev *spec = irq_data_get_irq_chip_data(d);
/*
* Note: we only support gpio interrupts here, i.e. the
* 0x814 (INT_STAT) register is expected to only have bit 15 set.
* We also accept software-generated irq, but they need no ack.
*/
gennum_readl(spec, GNGPIO_INT_STATUS); /* ack the gn4124 */
}
static void gn4124_irq_mask(struct irq_data *d)
{
}
static void gn4124_irq_unmask(struct irq_data *d)
{
}
/**
* GN4124 IRQ controller descriptor
*/
static struct irq_chip gn4124_ir1_chip = {
.name = "GN4124",
.irq_ack = gn4124_irq_ack,
.irq_mask = gn4124_irq_mask,
.irq_unmask = gn4124_irq_unmask,
};
/**
* It maps a local interrupt number (hwirq) to a Linux irq number (virtirq)
*/
static int gn4124_irq_domain_map(struct irq_domain *h,
unsigned int virtirq,
irq_hw_number_t hwirq)
{
struct spec_dev *spec = h->host_data;
irq_set_chip(virtirq, &gn4124_ir1_chip);
__irq_set_handler(virtirq, handle_edge_irq, 0, NULL);
irq_set_chip_data(virtirq, spec);
irq_set_handler_data(virtirq, spec);
return 0;
}
static struct irq_domain_ops gn4124_irq_domain_ops = {
.map = gn4124_irq_domain_map,
};
/**
* Handle cascade IRQ coming from the PCI controller and re-route it properly
* When the carrier receive an interrupt it will call than this function
* which then will call the proper handler
*/
static void gn4124_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
{
struct spec_dev *spec = irq_get_handler_data(irq);
struct irq_chip *chip = irq_get_chip(irq);
unsigned int cascade_irq;
/* Things to do before on the PCI controller */
chained_irq_enter(chip, desc);
/* Things to do at GN4124 level */
cascade_irq = irq_find_mapping(spec->domain, 0);
if (unlikely(0)) {
handle_bad_irq(cascade_irq, desc);
} else {
generic_handle_irq(cascade_irq);
}
/* Things to do before on the PCI controller */
chained_irq_exit(chip, desc);
}
/**
* Configure the GN4124 to be chained to the standard PCI irq controller
*/
int gn4124_irq_domain_create(struct spec_dev *spec)
{
int ret;
spec->domain = irq_domain_add_linear(NULL, 1, &gn4124_irq_domain_ops,
spec);
if (!spec->domain)
return -ENOMEM;
/* We have only one interrupt line for the time being */
ret = irq_create_mapping(spec->domain, 0);
if (!ret) {
irq_domain_remove(spec->domain);
return -EINVAL;
}
if (irq_set_handler_data(spec->pdev->irq, spec) != 0)
BUG();
irq_set_chained_handler(spec->pdev->irq, gn4124_handle_cascade_irq);
spec->fmc->irq = irq_find_mapping(spec->domain, 0);
return 0;
}
void gn4124_irq_domain_destroy(struct spec_dev *spec)
{
irq_domain_remove(spec->domain);
/* FIX parent IRQ controller */
}
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irqdomain.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -232,6 +233,9 @@ static int spec_nyab_validate(struct nyab_carrier *carrier) ...@@ -232,6 +233,9 @@ static int spec_nyab_validate(struct nyab_carrier *carrier)
err = spec_load_fpga_file(spec, data->bitstream); err = spec_load_fpga_file(spec, data->bitstream);
if (err) if (err)
return err; return err;
spec->fmc->flags &= ~FMC_DEVICE_HAS_GOLDEN;
spec->fmc->flags |= FMC_DEVICE_HAS_CUSTOM;
return 0; return 0;
} }
...@@ -317,7 +321,7 @@ static int spec_probe(struct pci_dev *pdev, ...@@ -317,7 +321,7 @@ static int spec_probe(struct pci_dev *pdev,
goto failed_nyab_alloc; goto failed_nyab_alloc;
} }
spec->ncarrier->op = &spec_nyab_op; spec->ncarrier->op = &spec_nyab_op;
spec->ncarrier->irq = pdev->irq; spec->ncarrier->irq = irq_find_mapping(spec->domain, 0);
spec->ncarrier->kernel_va = spec->remap[0]; spec->ncarrier->kernel_va = spec->remap[0];
ret = nyab_carrier_register(spec->ncarrier, ret = nyab_carrier_register(spec->ncarrier,
......
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version
*
* Driver for SPEC (Simple PCI Express FMC carrier) board.
* VIC (Vectored Interrupt Controller) support code.
*/
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
#include "hw/vic_regs.h"
#define VIC_MAX_VECTORS 32
#define VIC_SDB_VENDOR 0xce42
#define VIC_SDB_DEVICE 0x0013
/* A Vectored Interrupt Controller object */
struct vic_irq_controller {
/* It protects the handlers' vector */
spinlock_t vec_lock;
/* already-initialized flag */
int initialized;
/* Base address (FPGA-relative) */
uint32_t base;
/* Mapped base address of the VIC */
void *kernel_va;
/* Vector table */
struct vector {
/* Saved ID of the vector (for autodetection purposes) */
int saved_id;
/* Pointer to the assigned handler */
irq_handler_t handler;
/* FMC device that owns the interrupt */
struct fmc_device *requestor;
} vectors[VIC_MAX_VECTORS];
};
static inline void vic_writel(struct vic_irq_controller *vic, uint32_t value,
uint32_t offset)
{
writel(value, vic->kernel_va + offset);
}
static inline uint32_t vic_readl(struct vic_irq_controller *vic,
uint32_t offset)
{
return readl(vic->kernel_va + offset);
}
static int spec_vic_init(struct spec_dev *spec, struct fmc_device *fmc)
{
int i;
signed long vic_base;
struct vic_irq_controller *vic;
/*
* Try to look up the VIC in the SDB tree - note that IRQs
* shall be requested after the FMC driver has scanned the SDB tree
*/
vic_base =
fmc_find_sdb_device(fmc->sdb, VIC_SDB_VENDOR, VIC_SDB_DEVICE, NULL);
if (vic_base < 0) {
dev_err(&spec->pdev->dev,
"VIC controller not found, but a VIC interrupt requested. Wrong gateware?\n");
return -ENODEV;
}
dev_info(&spec->pdev->dev, "Found VIC @ 0x%lx\n", vic_base);
vic = kzalloc(sizeof(struct vic_irq_controller), GFP_KERNEL);
if (!vic)
return -ENOMEM;
spin_lock_init(&vic->vec_lock);
vic->kernel_va = spec->remap[0] + vic_base;
vic->base = (uint32_t) vic_base;
/* disable all IRQs, copy the vector table with pre-defined IRQ ids */
vic_writel(vic, 0xffffffff, VIC_REG_IDR);
for (i = 0; i < VIC_MAX_VECTORS; i++)
vic->vectors[i].saved_id =
vic_readl(vic, VIC_IVT_RAM_BASE + 4 * i);
/* config the VIC output: active high, edge, width = 256 tick (4 us) */
vic_writel(vic,
VIC_CTL_ENABLE | VIC_CTL_POL | VIC_CTL_EMU_EDGE |
VIC_CTL_EMU_LEN_W(250), VIC_REG_CTL);
vic->initialized = 1;
spec->vic = vic;
return 0;
}
static void spec_vic_exit(struct vic_irq_controller *vic)
{
if (!vic)
return;
/* Disable all irq lines and the VIC in general */
vic_writel(vic, 0xffffffff, VIC_REG_IDR);
vic_writel(vic, 0, VIC_REG_CTL);
kfree(vic);
}
/* NOTE: this function must be called while holding irq_lock */
irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec)
{
struct vic_irq_controller *vic = spec->vic;
int index, rv;
struct vector *vec;
if (unlikely(!vic))
goto fail;
/*
* Our parent IRQ handler: read the index value
* from the Vector Address Register, and find matching handler
*/
index = vic_readl(vic, VIC_REG_VAR) & 0xff;
if (index >= VIC_MAX_VECTORS)
goto fail;
vec = &vic->vectors[index];
if (!vec->handler)
goto fail;
rv = vec->handler(vec->saved_id, vec->requestor);
return rv;
fail:
return 0;
}
/* NOTE: this function must be called while holding irq_lock */
int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
unsigned long id, irq_handler_t handler)
{
struct vic_irq_controller *vic;
int rv = 0, i;
/* First interrupt to be requested? Look up and init the VIC */
if (!spec->vic) {
rv = spec_vic_init(spec, fmc);
if (rv)
return rv;
}
vic = spec->vic;
for (i = 0; i < VIC_MAX_VECTORS; i++) {
/* find vector in stored table, assign handle, enable */
if (vic->vectors[i].saved_id == id) {
spin_lock(&spec->vic->vec_lock);
vic_writel(vic, i, VIC_IVT_RAM_BASE + 4 * i);
vic->vectors[i].requestor = fmc;
vic->vectors[i].handler = handler;
vic_writel(vic, (1 << i), VIC_REG_IER);
spin_unlock(&spec->vic->vec_lock);
return 0;
}
}
return -EINVAL;
}
int vic_is_managed(struct vic_irq_controller *vic, unsigned long id)
{
int i, ret;
if (!vic)
return 0;
for (i = 0; i < VIC_MAX_VECTORS; i++) {
if (vic->vectors[i].saved_id != id)
continue;
ret = vic_readl(vic, VIC_REG_IMR) & (1 << i);
return !!ret;
}
return 0;
}
/*
* vic_handler_count
* It counts how many handlers are registered within the VIC controller
*/
static inline int vic_handler_count(struct vic_irq_controller *vic)
{
int i, count;
for (i = 0, count = 0; i < VIC_MAX_VECTORS; ++i)
if (vic->vectors[i].handler)
count++;
return count;
}
/* NOTE: this function must be called while holding irq_lock */
void spec_vic_irq_free(struct spec_dev *spec, unsigned long id)
{
int i;
for (i = 0; i < VIC_MAX_VECTORS; i++) {
if (spec->vic->vectors[i].saved_id == id) {
spin_lock(&spec->vic->vec_lock);
vic_writel(spec->vic, 1 << i, VIC_REG_IDR);
vic_writel(spec->vic, id, VIC_IVT_RAM_BASE + 4 * i);
spec->vic->vectors[i].handler = NULL;
spin_unlock(&spec->vic->vec_lock);
}
}
/* Clean up the VIC if there are no more handlers */
if (!vic_handler_count(spec->vic)) {
spec_vic_exit(spec->vic);
spec->vic = NULL;
}
}
void spec_vic_irq_ack(struct spec_dev *spec, unsigned long id)
{
vic_writel(spec->vic, 0, VIC_REG_EOIR); /* ack the irq */
}
...@@ -43,6 +43,7 @@ struct spec_dev { ...@@ -43,6 +43,7 @@ struct spec_dev {
char name[SPEC_NAME_LEN]; char name[SPEC_NAME_LEN];
struct nyab_carrier *ncarrier; struct nyab_carrier *ncarrier;
struct irq_domain *domain;
}; };
#define SPEC_FLAG_FAKE_EEPROM 0x00000001 #define SPEC_FLAG_FAKE_EEPROM 0x00000001
...@@ -162,4 +163,7 @@ irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec); ...@@ -162,4 +163,7 @@ irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec);
extern void spec_vic_irq_ack(struct spec_dev *spec, unsigned long id); extern void spec_vic_irq_ack(struct spec_dev *spec, unsigned long id);
extern int vic_is_managed(struct vic_irq_controller *vic, unsigned long id); extern int vic_is_managed(struct vic_irq_controller *vic, unsigned long id);
extern int gn4124_irq_domain_create(struct spec_dev *spec);
extern void gn4124_irq_domain_destroy(struct spec_dev *spec);
#endif /* __SPEC_H__ */ #endif /* __SPEC_H__ */
Markdown is supported
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