From b9fc1927790798e2db6024247ad816b4c370efb6 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 25 Nov 2019 14:51:39 +0000 Subject: [PATCH] drivers: uart_pl011: Enable shared interrupts support The PrimeCell UART (PL011) IP can use one single combined/shared interrupt line instead than different IRQ lines for TX/RX/Err/... This is the most common configuration supported in the Linux world but not currently supported in Zephyr. QEMU emulates a PL011 UART with a single interrupt line as well. To support this configuration we have to hookup the PL011 driver with a shared IRQ driver and add two new configuration options when the shared IRQ line is used. Signed-off-by: Carlo Caione --- drivers/serial/Kconfig.pl011 | 21 +++++++++++++++++++++ drivers/serial/uart_pl011.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/drivers/serial/Kconfig.pl011 b/drivers/serial/Kconfig.pl011 index 7ad54a433ed..cafdf786039 100644 --- a/drivers/serial/Kconfig.pl011 +++ b/drivers/serial/Kconfig.pl011 @@ -20,4 +20,25 @@ config UART_PL011_PORT1 help Build the driver to utilize UART controller Port 1. +config UART_PL011_SHARED_IRQ + bool + +config UART_PL011_PORT0_SHARED_IRQ + bool "Shared IRQ for UART 0" + depends on SHARED_IRQ_0 + depends on UART_PL011_PORT0 + select UART_PL011_SHARED_IRQ + help + When interrupts fire, the shared IRQ driver is notified. Then the shared IRQ + driver dispatches the interrupt to the UART driver. + +config UART_PL011_PORT1_SHARED_IRQ + bool "Shared IRQ for UART 1" + depends on SHARED_IRQ_1 + depends on UART_PL011_PORT1 + select UART_PL011_SHARED_IRQ + help + When interrupts fire, the shared IRQ driver is notified. Then the shared IRQ + driver dispatches the interrupt to the UART driver. + endif # UART_PL011 diff --git a/drivers/serial/uart_pl011.c b/drivers/serial/uart_pl011.c index a63ca7df932..5cc9eda5729 100644 --- a/drivers/serial/uart_pl011.c +++ b/drivers/serial/uart_pl011.c @@ -11,6 +11,9 @@ #include #include +#if defined(CONFIG_SHARED_IRQ) +#include +#endif /* * UART PL011 register map structure @@ -43,6 +46,9 @@ struct pl011_data { #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_callback_user_data_t irq_cb; void *irq_cb_data; +#if defined(CONFIG_UART_PL011_SHARED_IRQ) + char *shared_irq_dev_name; +#endif #endif }; @@ -423,6 +429,9 @@ static struct uart_device_config pl011_cfg_port_0 = { static struct pl011_data pl011_data_port_0 = { .baud_rate = DT_PL011_PORT0_BAUD_RATE, +#if defined(CONFIG_UART_PL011_SHARED_IRQ) + .shared_irq_dev_name = DT_INST_0_SHARED_IRQ_LABEL, +#endif }; DEVICE_AND_API_INIT(pl011_port_0, @@ -436,6 +445,14 @@ DEVICE_AND_API_INIT(pl011_port_0, #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void pl011_irq_config_func_0(struct device *dev) { +#if defined(CONFIG_UART_PL011_PORT0_SHARED_IRQ) + struct device *shared_irq_dev; + + shared_irq_dev = device_get_binding(DEV_DATA(dev)->shared_irq_dev_name); + __ASSERT(shared_irq_dev != NULL, "Failed to get shared irq"); + shared_irq_isr_register(shared_irq_dev, (isr_t)pl011_isr, dev); + shared_irq_enable(shared_irq_dev, dev); +#else IRQ_CONNECT(DT_PL011_PORT0_IRQ_TX, DT_PL011_PORT0_IRQ_PRI, pl011_isr, @@ -456,6 +473,7 @@ static void pl011_irq_config_func_0(struct device *dev) DEVICE_GET(pl011_port_0), 0); irq_enable(DT_PL011_PORT0_IRQ_RXTIM); +#endif } #endif @@ -477,6 +495,9 @@ static struct uart_device_config pl011_cfg_port_1 = { static struct pl011_data pl011_data_port_1 = { .baud_rate = DT_PL011_PORT1_BAUD_RATE, +#if defined(CONFIG_UART_PL011_SHARED_IRQ) + .shared_irq_dev_name = DT_INST_1_SHARED_IRQ_LABEL, +#endif }; DEVICE_AND_API_INIT(pl011_port_1, @@ -490,6 +511,14 @@ DEVICE_AND_API_INIT(pl011_port_1, #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void pl011_irq_config_func_1(struct device *dev) { +#if defined(CONFIG_UART_PL011_PORT1_SHARED_IRQ) + struct device *shared_irq_dev; + + shared_irq_dev = device_get_binding(DEV_DATA(dev)->shared_irq_dev_name); + __ASSERT(shared_irq_dev != NULL, "Failed to get shared irq"); + shared_irq_isr_register(shared_irq_dev, (isr_t)pl011_isr, dev); + shared_irq_enable(shared_irq_dev, dev); +#else IRQ_CONNECT(DT_PL011_PORT1_IRQ_TX, DT_PL011_PORT1_IRQ_PRI, pl011_isr, @@ -510,6 +539,7 @@ static void pl011_irq_config_func_1(struct device *dev) DEVICE_GET(pl011_port_1), 0); irq_enable(DT_PL011_PORT1_IRQ_RXTIM); +#endif } #endif