aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mn10300
diff options
context:
space:
mode:
authorMark Salter <msalter@redhat.com>2012-12-12 10:36:38 -0500
committerDavid Howells <dhowells@redhat.com>2012-12-12 10:46:14 -0500
commit8d160027ff234bddea627ba54c2b85efa1884867 (patch)
tree22e2447f07a3ddde623c11a22a59571853fff84a /arch/mn10300
parent7d361cb754720d69695a3efc973e9a1a51e46b21 (diff)
MN10300: fix serial port vdma irq setup for SMP
The builtin SoC serial ports have no FIFOs and use a virtual DMA mechanism based on high priority IRQs to avoid overruns. These high priority interrupts are pinned to cpu#0 on SMP systems. This patch fixes a problem with SMP where the set_intr_level() interface is used to set the priority for these IRQs. The set_intr_level() function sets priority on the local cpu but on SMP systems, this code may be run on some other cpu than the one handling the interrupts. Instead of setting interrupt level explicitly, this patch uses a special irq_chip for these interrupts so that the mask/unmask methods can set the interrupt level implicitly. Signed-off-by: Mark Salter <msalter@redhat.com> Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'arch/mn10300')
-rw-r--r--arch/mn10300/kernel/mn10300-serial.c34
1 files changed, 30 insertions, 4 deletions
diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c
index 4968cfe66c06..cac0f0da9203 100644
--- a/arch/mn10300/kernel/mn10300-serial.c
+++ b/arch/mn10300/kernel/mn10300-serial.c
@@ -408,6 +408,34 @@ static struct irq_chip mn10300_serial_pic = {
408 .irq_unmask = mn10300_serial_nop, 408 .irq_unmask = mn10300_serial_nop,
409}; 409};
410 410
411static void mn10300_serial_low_mask(struct irq_data *d)
412{
413 unsigned long flags;
414 u16 tmp;
415
416 flags = arch_local_cli_save();
417 GxICR(d->irq) = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
418 tmp = GxICR(d->irq); /* flush write buffer */
419 arch_local_irq_restore(flags);
420}
421
422static void mn10300_serial_low_unmask(struct irq_data *d)
423{
424 unsigned long flags;
425 u16 tmp;
426
427 flags = arch_local_cli_save();
428 GxICR(d->irq) =
429 NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE;
430 tmp = GxICR(d->irq); /* flush write buffer */
431 arch_local_irq_restore(flags);
432}
433
434static struct irq_chip mn10300_serial_low_pic = {
435 .name = "mnserial-low",
436 .irq_mask = mn10300_serial_low_mask,
437 .irq_unmask = mn10300_serial_low_unmask,
438};
411 439
412/* 440/*
413 * serial virtual DMA interrupt jump table 441 * serial virtual DMA interrupt jump table
@@ -929,10 +957,8 @@ static int mn10300_serial_startup(struct uart_port *_port)
929 pint->port = port; 957 pint->port = port;
930 pint->vdma = mn10300_serial_vdma_tx_handler; 958 pint->vdma = mn10300_serial_vdma_tx_handler;
931 959
932 set_intr_level(port->rx_irq, 960 irq_set_chip(port->rx_irq, &mn10300_serial_low_pic);
933 NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)); 961 irq_set_chip(port->tx_irq, &mn10300_serial_low_pic);
934 set_intr_level(port->tx_irq,
935 NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL));
936 irq_set_chip(port->tm_irq, &mn10300_serial_pic); 962 irq_set_chip(port->tm_irq, &mn10300_serial_pic);
937 963
938 if (request_irq(port->rx_irq, mn10300_serial_interrupt, 964 if (request_irq(port->rx_irq, mn10300_serial_interrupt,