Commit 5b98b2d5 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Alessandro Rubini

kernel/wr-nic: fixed IRQ handling (requires new VIC bistream with edge irq)

parent 112ffbb8
......@@ -50,9 +50,14 @@
#define WRN_WRC_DEFAULT_NAME "fmc/wr_nic_dio-wrc.bin"
/* the various interrupt sources for the VIC */
#define WRN_VIC_MASK_TXTSU 0x0001
#define WRN_VIC_MASK_NIC 0x0002
#define WRN_VIC_MASK_DIO 0x0004
#define WRN_VIC_ID_TXTSU 0x0000
#define WRN_VIC_ID_NIC 0x0001
#define WRN_VIC_ID_DIO 0x0002
#define WRN_VIC_MASK_TXTSU (1 << WRN_VIC_ID_TXTSU)
#define WRN_VIC_MASK_NIC (1 << WRN_VIC_ID_NIC)
#define WRN_VIC_MASK_DIO (1 << WRN_VIC_ID_DIO)
/* This is somehow generic, but I find no better place at this time */
#ifndef SET_HI32
......
......@@ -43,6 +43,15 @@
/* 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 */
......
......@@ -27,6 +27,25 @@ peripheral {
access_dev = READ_ONLY;
access_bus = READ_WRITE;
};
field {
name = "Emulate Edge sensitive output";
description = "1: Forces a low pulse of EMU_WIDTH clock cycles at each write to EOIR. Useful for edge-only IRQ controllers such as Gennum.\
0: Normal IRQ master line behavior";
prefix = "EMU_EDGE";
type = BIT;
access_dev = READ_ONLY;
access_bus = READ_WRITE;
};
field {
name = "Emulated Edge pulse timer";
prefix = "EMU_LEN";
type = SLV;
size = 16;
access_dev = READ_ONLY;
access_bus = READ_WRITE;
};
};
reg {
......
......@@ -46,7 +46,7 @@ irqreturn_t wrn_handler(int irq, void *dev_id)
struct platform_device *pdev = fmc->mezzanine_data;
struct wrn_drvdata *drvdata;
struct VIC_WB *vic;
uint32_t mask;
uint32_t vector;
irqreturn_t ret = IRQ_HANDLED;
if (!pdev) {
......@@ -57,34 +57,43 @@ irqreturn_t wrn_handler(int irq, void *dev_id)
}
drvdata = pdev->dev.platform_data;
vic = (typeof(vic))drvdata->vic_base;
while ( (mask = readl(&vic->RISR)) ) {
if (mask & WRN_VIC_MASK_NIC)
ret = wrn_interrupt(irq, drvdata->wrn);
if (mask & WRN_VIC_MASK_TXTSU)
ret = wrn_tstamp_interrupt(irq, drvdata->wrn);
if (mask & WRN_VIC_MASK_DIO)
ret = wrn_dio_interrupt(fmc /* different arg! */);
writel(mask, &vic->EOIR);
}
fmc->op->irq_ack(fmc);
vic = (typeof(vic)) drvdata->vic_base;
/*
* The VIC is really level-active, but my Gennum refuses to work
* properly on level interrupts (maybe it's just me). So I'll use
* the typical trick you see in level-irq Ethernet drivers when
* they are plugged to edge-irq gpio lines: force an edge in case
* the line has become active again while we were serving it.
* (with a big thank you to Tomasz and Grzegorz for the sequence)
* VIC operation algorithm:
* - when a peripheral interrupt arrives (as seen in RISR register),
* it is masked by IMR and latched in bit of ISR register:
*
* ISR |= (RISR & IMR);
* if (ISR != 0) {
* int current_irq = priority_decode(ISR)
* VAR = IVT_RAM[current_irq];
* MASTER_IRQ = CTL.POL;
*
* Actually, we should check the input line before doing this...
* wait (write to EOIR)
*
* if (CTL.EMU_ENA)
* pulse(MASTER_IRQ, CTL.EMU_LEN, !CTL.POL)
* } else {
* MASTER_IRQ = !CTL.POL
* }
*
* The VIC was inspired by the original VIC used in NXP's ARM MCUs.
*/
writel(WRN_ALL_MASK, &vic->IDR); /* disable sources */
writel(0xff, &vic->EOIR);
udelay(5);
writel(WRN_ALL_MASK, &vic->IER); /* enable sources again */
/* read pending vector address - the index of currently pending IRQ. */
vector = readl(&vic->VAR);
if (vector == WRN_VIC_ID_NIC)
ret = wrn_interrupt(irq, drvdata->wrn);
else if (vector == WRN_VIC_ID_TXTSU)
ret = wrn_tstamp_interrupt(irq, drvdata->wrn);
else if (vector == WRN_VIC_ID_DIO)
ret = wrn_dio_interrupt(fmc /* different arg! */);
fmc->op->irq_ack(fmc);
writel(0, &vic->EOIR);
return ret;
}
......@@ -95,7 +104,15 @@ static int wrn_vic_init(struct fmc_device *fmc)
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct VIC_WB *vic = (typeof(vic))drvdata->vic_base;
writel(VIC_CTL_ENABLE | VIC_CTL_POL, &vic->CTL);
/* fill the vector table */
writel(WRN_VIC_ID_TXTSU, &vic->IVT_RAM[WRN_VIC_ID_TXTSU]);
writel(WRN_VIC_ID_NIC, &vic->IVT_RAM[WRN_VIC_ID_NIC]);
writel(WRN_VIC_ID_DIO, &vic->IVT_RAM[WRN_VIC_ID_DIO]);
/* 4us edge emulation timer (counts in 16ns steps) */
writel(VIC_CTL_ENABLE | VIC_CTL_POL | VIC_CTL_EMU_EDGE | \
VIC_CTL_EMU_LEN_W(4000 / 16), &vic->CTL);
writel(WRN_ALL_MASK, &vic->IER);
return 0;
}
......
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