Skip to content
Snippets Groups Projects
Commit 005dccd6 authored by Alessandro Rubini's avatar Alessandro Rubini
Browse files

kernel: added irq support in the ops

parent 239c63ba
Branches
Tags
No related merge requests found
......@@ -9,8 +9,116 @@
*/
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/interrupt.h>
#include "spec.h"
/* The main role of this file is offering the fmc_operations for the spec */
static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
char *name, int flags)
{
return request_irq(fmc->irq, handler, flags, name, fmc);
}
static void spec_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 int spec_irq_free(struct fmc_device *fmc)
{
free_irq(fmc->irq, fmc);
return 0;
}
static struct fmc_operations spec_fmc_operations = {
/* FIXME: readl/writel */
/* FIXME: reprogram */
.irq_request = spec_irq_request,
.irq_ack = spec_irq_ack,
.irq_free = spec_irq_free,
/* FIXME: eeprom */
};
/*
* 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;
printk("got %i!\n", irq);
spec->irq_count++;
complete(&spec->compl);
fmc->op->irq_ack(fmc);
return IRQ_HANDLED;
}
static int spec_irq_init(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
uint32_t value;
int i;
/*
* Enable multiple-msi to work around a chip design bug.
* See http://blog.tftechpages.com/?p=595
*/
value = gennum_readl(spec, GNPPCI_MSI_CONTROL);
if ((value & 0x810000) != 0x810000)
dev_err(&spec->pdev->dev, "invalid msi control: 0x%08x\n",
value);
value = 0xa50000 | (value & 0xffff);
gennum_writel(spec, value, GNPPCI_MSI_CONTROL);
/*
* Now check the two least-significant bits of the msi-data register,
* then enable CFG_0 or .. CFG_3 accordingly, to get proper vector.
*/
value = gennum_readl(spec, GNPPCI_MSI_DATA);
for (i = 0; i < 7; i++)
gennum_writel(spec, 0, GNINT_CFG(i));
gennum_writel(spec, 0x800c, GNINT_CFG(value & 0x03));
/* Finally, ensure we are able to receive it */
spec->irq_count = 0;
init_completion(&spec->compl);
fmc->op->irq_request(fmc, spec_test_handler, "spec-test", 0);
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;
}
static void spec_irq_exit(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
int i;
for (i = 0; i < 7; i++)
gennum_writel(spec, 0, GNINT_CFG(i));
fmc->op->irq_ack(fmc); /* just to be safe */
}
int spec_fmc_create(struct spec_dev *spec)
{
struct fmc_device *fmc;
......@@ -25,24 +133,32 @@ int spec_fmc_create(struct spec_dev *spec)
fmc->carrier_data = spec;
fmc->base = spec->remap[0];
fmc->irq = spec->pdev->irq;
fmc->op = &spec_fmc_operations;
spec->fmc = fmc;
ret = spec_i2c_init(fmc);
if (ret) {
kfree(fmc);
return ret;
}
spec->fmc = fmc;
if (ret)
goto out_free;
ret = spec_irq_init(fmc);
if (ret)
goto out_free;
ret = fmc_device_register(fmc);
if (ret) {
spec->fmc = NULL;
kfree(fmc);
}
if (ret)
goto out_irq;
return ret;
out_irq:
spec_irq_exit(fmc);
out_free:
spec->fmc = NULL;
kfree(fmc);
return ret;
}
void spec_fmc_destroy(struct spec_dev *spec)
{
fmc_device_unregister(spec->fmc);
spec_irq_exit(spec->fmc);
spec_i2c_exit(spec->fmc);
spec->fmc = NULL;
}
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