Commit bf9108f0 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'fix-review-vic'

parents c791d5b0 d4bbcc73
...@@ -124,9 +124,12 @@ static void spec_shared_irq_ack(struct fmc_device *fmc); ...@@ -124,9 +124,12 @@ static void spec_shared_irq_ack(struct fmc_device *fmc);
static irqreturn_t spec_vic_irq_handler(int id, void *data) static irqreturn_t spec_vic_irq_handler(int id, void *data)
{ {
struct fmc_device *fmc = (struct fmc_device *)data; struct fmc_device *fmc = (struct fmc_device *)data;
struct spec_dev *spec = (struct spec_dev *)fmc->carrier_data;
irqreturn_t rv; irqreturn_t rv;
rv = spec_vic_irq_dispatch((struct spec_dev *)fmc->carrier_data); spin_lock(&spec->irq_lock);
rv = spec_vic_irq_dispatch(spec);
spin_unlock(&spec->irq_lock);
spec_shared_irq_ack(fmc); spec_shared_irq_ack(fmc);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -150,29 +153,33 @@ static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler, ...@@ -150,29 +153,33 @@ static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
char *name, int flags) char *name, int flags)
{ {
struct spec_dev *spec = fmc->carrier_data; struct spec_dev *spec = fmc->carrier_data;
int rv; int rv, first_time;
/* VIC mode interrupt */ /* VIC mode interrupt */
if (!(flags & IRQF_SHARED)) { if (!(flags & IRQF_SHARED)) {
int first_time = !spec->vic; spin_lock(&spec->irq_lock);
first_time = !spec->vic;
/* configure the VIC */ /* configure the VIC */
rv = spec_vic_irq_request(spec, fmc, fmc->irq, handler); rv = spec_vic_irq_request(spec, fmc, fmc->irq, handler);
if (rv) {
if (rv) spin_unlock(&spec->irq_lock);
return rv; return rv;
}
/* on first IRQ, configure VIC "master" handler and GPIO too */ /* on first IRQ, configure VIC "master" handler and GPIO too */
if (first_time) { if (first_time) {
rv = spec_shared_irq_request(fmc, spec_vic_irq_handler, rv = spec_shared_irq_request(fmc, spec_vic_irq_handler,
"spec-vic", IRQF_SHARED); "spec-vic", IRQF_SHARED);
if (rv) if (rv) {
spin_unlock(&spec->irq_lock);
return rv; return rv;
}
fmc->op->gpio_config(fmc, spec_vic_gpio_cfg, fmc->op->gpio_config(fmc, spec_vic_gpio_cfg,
ARRAY_SIZE(spec_vic_gpio_cfg)); ARRAY_SIZE(spec_vic_gpio_cfg));
} }
spin_unlock(&spec->irq_lock);
} else { } else {
rv = spec_shared_irq_request(fmc, handler, name, flags); rv = spec_shared_irq_request(fmc, handler, name, flags);
pr_debug("Requesting irq '%s' in shared mode (rv %d)\n", name, pr_debug("Requesting irq '%s' in shared mode (rv %d)\n", name,
...@@ -207,23 +214,30 @@ static void spec_irq_ack(struct fmc_device *fmc) ...@@ -207,23 +214,30 @@ static void spec_irq_ack(struct fmc_device *fmc)
/* Nothing for VIC here, all irqs are acked by master VIC handler */ /* Nothing for VIC here, all irqs are acked by master VIC handler */
} }
static int spec_shared_irq_free(struct fmc_device *fmc) static void spec_shared_irq_free(struct fmc_device *fmc)
{ {
struct spec_dev *spec = fmc->carrier_data; struct spec_dev *spec = fmc->carrier_data;
gennum_writel(spec, 0xffff, GNGPIO_INT_MASK_SET); /* disable */ gennum_writel(spec, 0xffff, GNGPIO_INT_MASK_SET); /* disable */
free_irq(spec->pdev->irq, fmc); free_irq(spec->pdev->irq, fmc);
return 0;
} }
static int spec_irq_free(struct fmc_device *fmc) static int spec_irq_free(struct fmc_device *fmc)
{ {
struct spec_dev *spec = fmc->carrier_data; struct spec_dev *spec = fmc->carrier_data;
spin_lock(&spec->irq_lock);
if (spec->vic) if (spec->vic)
return spec_vic_irq_free(spec, spec->pdev->irq); spec_vic_irq_free(spec, fmc->irq);
else
return spec_shared_irq_free(fmc); /*
* 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 */
...@@ -392,6 +406,7 @@ static int spec_irq_init(struct fmc_device *fmc) ...@@ -392,6 +406,7 @@ static int spec_irq_init(struct fmc_device *fmc)
uint32_t value; uint32_t value;
int i; int i;
spin_lock_init(&spec->irq_lock);
if (spec_use_msi) { if (spec_use_msi) {
/* /*
* Enable multiple-msi to work around a chip design bug. * Enable multiple-msi to work around a chip design bug.
...@@ -447,12 +462,7 @@ static void spec_irq_exit(struct fmc_device *fmc) ...@@ -447,12 +462,7 @@ static void spec_irq_exit(struct fmc_device *fmc)
gennum_writel(spec, 0, GNINT_CFG(i)); gennum_writel(spec, 0, GNINT_CFG(i));
fmc->op->irq_ack(fmc); /* just to be safe */ fmc->op->irq_ack(fmc); /* just to be safe */
/* VIC mode: release VIC resources and disable VIC master IRQ line */ WARN(spec->vic, "A Mezzanine driver didn't release all its IRQ handlers\n");
if (spec->vic) {
spec_vic_cleanup(spec);
gennum_writel(spec, 0xffff, GNGPIO_INT_MASK_SET); /* disable */
free_irq(spec->pdev->irq, fmc);
}
} }
static int check_golden(struct fmc_device *fmc) static int check_golden(struct fmc_device *fmc)
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
/* A Vectored Interrupt Controller object */ /* A Vectored Interrupt Controller object */
struct vic_irq_controller { struct vic_irq_controller {
/* It protects the handlers' vector */
spinlock_t vec_lock;
/* already-initialized flag */ /* already-initialized flag */
int initialized; int initialized;
/* Base address (FPGA-relative) */ /* Base address (FPGA-relative) */
...@@ -79,6 +81,7 @@ static int spec_vic_init(struct spec_dev *spec, struct fmc_device *fmc) ...@@ -79,6 +81,7 @@ static int spec_vic_init(struct spec_dev *spec, struct fmc_device *fmc)
if (!vic) if (!vic)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&vic->vec_lock);
vic->kernel_va = spec->remap[0] + vic_base; vic->kernel_va = spec->remap[0] + vic_base;
vic->base = (uint32_t) vic_base; vic->base = (uint32_t) vic_base;
...@@ -99,24 +102,26 @@ static int spec_vic_init(struct spec_dev *spec, struct fmc_device *fmc) ...@@ -99,24 +102,26 @@ static int spec_vic_init(struct spec_dev *spec, struct fmc_device *fmc)
return 0; return 0;
} }
void spec_vic_cleanup(struct spec_dev *spec) static void spec_vic_exit(struct vic_irq_controller *vic)
{ {
if (!spec->vic) if (!vic)
return; return;
/* Disable all irq lines and the VIC in general */ /* Disable all irq lines and the VIC in general */
vic_writel(spec->vic, 0xffffffff, VIC_REG_IDR); vic_writel(vic, 0xffffffff, VIC_REG_IDR);
vic_writel(spec->vic, 0, VIC_REG_CTL); vic_writel(vic, 0, VIC_REG_CTL);
kfree(spec->vic); kfree(vic);
spec->vic = NULL;
} }
/* NOTE: this function must be called while holding irq_lock */
irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec) irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec)
{ {
struct vic_irq_controller *vic = spec->vic; struct vic_irq_controller *vic = spec->vic;
int index, rv; int index, rv;
struct vector *vec; struct vector *vec;
if (unlikely(!vic))
goto fail;
/* /*
* Our parent IRQ handler: read the index value * Our parent IRQ handler: read the index value
* from the Vector Address Register, and find matching handler * from the Vector Address Register, and find matching handler
...@@ -139,6 +144,7 @@ fail: ...@@ -139,6 +144,7 @@ fail:
return 0; 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, int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
unsigned long id, irq_handler_t handler) unsigned long id, irq_handler_t handler)
{ {
...@@ -157,41 +163,59 @@ int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc, ...@@ -157,41 +163,59 @@ int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
for (i = 0; i < VIC_MAX_VECTORS; i++) { for (i = 0; i < VIC_MAX_VECTORS; i++) {
/* find vector in stored table, assign handle, enable */ /* find vector in stored table, assign handle, enable */
if (vic->vectors[i].saved_id == id) { if (vic->vectors[i].saved_id == id) {
spin_lock(&spec->irq_lock); spin_lock(&spec->vic->vec_lock);
vic_writel(vic, i, VIC_IVT_RAM_BASE + 4 * i); vic_writel(vic, i, VIC_IVT_RAM_BASE + 4 * i);
vic->vectors[i].requestor = fmc; vic->vectors[i].requestor = fmc;
vic->vectors[i].handler = handler; vic->vectors[i].handler = handler;
vic_writel(vic, (1 << i), VIC_REG_IER); vic_writel(vic, (1 << i), VIC_REG_IER);
spin_unlock(&spec->irq_lock); spin_unlock(&spec->vic->vec_lock);
return 0; return 0;
} }
} }
return -EINVAL; return -EINVAL;
}
/*
* 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;
} }
int spec_vic_irq_free(struct spec_dev *spec, unsigned long id) /* NOTE: this function must be called while holding irq_lock */
void spec_vic_irq_free(struct spec_dev *spec, unsigned long id)
{ {
int i; int i;
for (i = 0; i < VIC_MAX_VECTORS; i++) { for (i = 0; i < VIC_MAX_VECTORS; i++) {
uint32_t vec = spec->vic->vectors[i].saved_id; if (spec->vic->vectors[i].saved_id == id) {
if (vec == id) { spin_lock(&spec->vic->vec_lock);
spin_lock(&spec->irq_lock);
vic_writel(spec->vic, 1 << i, VIC_REG_IDR); vic_writel(spec->vic, 1 << i, VIC_REG_IDR);
vic_writel(spec->vic, vec, VIC_IVT_RAM_BASE + 4 * i); vic_writel(spec->vic, id, VIC_IVT_RAM_BASE + 4 * i);
spec->vic->vectors[i].handler = NULL; spec->vic->vectors[i].handler = NULL;
spin_unlock(&spec->irq_lock); spin_unlock(&spec->vic->vec_lock);
} }
} }
return 0; /* 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) void spec_vic_irq_ack(struct spec_dev *spec, unsigned long id)
......
...@@ -144,11 +144,11 @@ extern int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset, ...@@ -144,11 +144,11 @@ extern int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset,
extern int spec_gpio_init(struct fmc_device *fmc); extern int spec_gpio_init(struct fmc_device *fmc);
extern void spec_gpio_exit(struct fmc_device *fmc); extern void spec_gpio_exit(struct fmc_device *fmc);
/* Functions in spec-vic.c */
/* NOTE: these functions must be called while holding irq_lock */
int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc, int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
unsigned long id, irq_handler_t handler); unsigned long id, irq_handler_t handler);
void spec_vic_irq_free(struct spec_dev *spec, unsigned long id);
int spec_vic_irq_free(struct spec_dev *spec, unsigned long id);
irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec); irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec);
void spec_vic_cleanup(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