Skip to content

Commit

Permalink
[ARM] S3C64XX: Demux UART interrupts
Browse files Browse the repository at this point in the history
Add demux handling for the UART interrupts
generated by the VIC into their seperate IRQs
that the serial driver can register.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
  • Loading branch information
Ben Dooks committed Dec 15, 2008
1 parent b73c289 commit 3e694d4
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 1 deletion.
8 changes: 8 additions & 0 deletions arch/arm/mach-s3c6400/include/mach/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
#define S3C_PA_UART3 (S3C_PA_UART + 0xC00)
#define S3C_UART_OFFSET (0x400)

/* See notes on UART VA mapping in debug-macro.S */
#define S3C_VA_UARTx(x) (S3C_VA_UART + (S3C_PA_UART & 0xfffff) + ((x) * S3C_UART_OFFSET))

#define S3C_VA_UART0 S3C_VA_UARTx(0)
#define S3C_VA_UART1 S3C_VA_UARTx(1)
#define S3C_VA_UART2 S3C_VA_UARTx(2)
#define S3C_VA_UART3 S3C_VA_UARTx(3)

#define S3C64XX_PA_SYSCON (0x7E00F000)
#define S3C64XX_PA_TIMER (0x7F006000)

Expand Down
140 changes: 139 additions & 1 deletion arch/arm/plat-s3c64xx/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,144 @@ static struct irq_chip s3c_irq_timer = {
.ack = s3c_irq_timer_ack,
};

struct uart_irq {
void __iomem *regs;
unsigned int base_irq;
unsigned int parent_irq;
};

/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3]
* are consecutive when looking up the interrupt in the demux routines.
*/
static struct uart_irq uart_irqs[] = {
[0] = {
.regs = S3C_VA_UART0,
.base_irq = IRQ_S3CUART_BASE0,
.parent_irq = IRQ_UART0,
},
[1] = {
.regs = S3C_VA_UART1,
.base_irq = IRQ_S3CUART_BASE1,
.parent_irq = IRQ_UART1,
},
[2] = {
.regs = S3C_VA_UART2,
.base_irq = IRQ_S3CUART_BASE2,
.parent_irq = IRQ_UART2,
},
[3] = {
.regs = S3C_VA_UART3,
.base_irq = IRQ_S3CUART_BASE3,
.parent_irq = IRQ_UART3,
},
};

static inline void __iomem *s3c_irq_uart_base(unsigned int irq)
{
struct uart_irq *uirq = get_irq_chip_data(irq);
return uirq->regs;
}

static inline unsigned int s3c_irq_uart_bit(unsigned int irq)
{
return irq & 3;
}

/* UART interrupt registers, not worth adding to seperate include header */
#define S3C64XX_UINTP 0x30
#define S3C64XX_UINTSP 0x34
#define S3C64XX_UINTM 0x38

static void s3c_irq_uart_mask(unsigned int irq)
{
void __iomem *regs = s3c_irq_uart_base(irq);
unsigned int bit = s3c_irq_uart_bit(irq);
u32 reg;

reg = __raw_readl(regs + S3C64XX_UINTM);
reg |= (1 << bit);
__raw_writel(reg, regs + S3C64XX_UINTM);
}

static void s3c_irq_uart_maskack(unsigned int irq)
{
void __iomem *regs = s3c_irq_uart_base(irq);
unsigned int bit = s3c_irq_uart_bit(irq);
u32 reg;

reg = __raw_readl(regs + S3C64XX_UINTM);
reg |= (1 << bit);
__raw_writel(reg, regs + S3C64XX_UINTM);
__raw_writel(1 << bit, regs + S3C64XX_UINTP);
}

static void s3c_irq_uart_unmask(unsigned int irq)
{
void __iomem *regs = s3c_irq_uart_base(irq);
unsigned int bit = s3c_irq_uart_bit(irq);
u32 reg;

reg = __raw_readl(regs + S3C64XX_UINTM);
reg &= ~(1 << bit);
__raw_writel(reg, regs + S3C64XX_UINTM);
}

static void s3c_irq_uart_ack(unsigned int irq)
{
void __iomem *regs = s3c_irq_uart_base(irq);
unsigned int bit = s3c_irq_uart_bit(irq);

__raw_writel(1 << bit, regs + S3C64XX_UINTP);
}

static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc)
{
struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0];
u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP);
int base = uirq->base_irq;

if (pend & (1 << 0))
generic_handle_irq(base);
if (pend & (1 << 1))
generic_handle_irq(base + 1);
if (pend & (1 << 2))
generic_handle_irq(base + 2);
if (pend & (1 << 3))
generic_handle_irq(base + 3);
}

static struct irq_chip s3c_irq_uart = {
.name = "s3c-uart",
.mask = s3c_irq_uart_mask,
.unmask = s3c_irq_uart_unmask,
.mask_ack = s3c_irq_uart_maskack,
.ack = s3c_irq_uart_ack,
};

static void __init s3c64xx_uart_irq(struct uart_irq *uirq)
{
void *reg_base = uirq->regs;
unsigned int irq;
int offs;

/* mask all interrupts at the start. */
__raw_writel(0xf, reg_base + S3C64XX_UINTM);

for (offs = 0; offs < 3; offs++) {
irq = uirq->base_irq + offs;

set_irq_chip(irq, &s3c_irq_uart);
set_irq_chip_data(irq, uirq);
set_irq_handler(irq, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}

set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart);
}

void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
{
int irq;
int uart, irq;

printk(KERN_INFO "%s: initialising interrupts\n", __func__);

Expand All @@ -114,6 +249,9 @@ void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
set_irq_handler(irq, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}

for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
s3c64xx_uart_irq(&uart_irqs[uart]);
}


0 comments on commit 3e694d4

Please sign in to comment.