summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/mps2-uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/mps2-uart.c')
-rw-r--r--drivers/tty/serial/mps2-uart.c138
1 files changed, 96 insertions, 42 deletions
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
index 9f8f63719126..587b42f754cb 100644
--- a/drivers/tty/serial/mps2-uart.c
+++ b/drivers/tty/serial/mps2-uart.c
@@ -22,6 +22,7 @@
#include <linux/serial_core.h>
#include <linux/tty_flip.h>
#include <linux/types.h>
+#include <linux/idr.h>
#define SERIAL_NAME "ttyMPS"
#define DRIVER_NAME "mps2-uart"
@@ -65,11 +66,14 @@
#define MPS2_MAX_PORTS 3
+#define UART_PORT_COMBINED_IRQ BIT(0)
+
struct mps2_uart_port {
struct uart_port port;
struct clk *clk;
unsigned int tx_irq;
unsigned int rx_irq;
+ unsigned int flags;
};
static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
@@ -264,6 +268,20 @@ static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
return handled;
}
+static irqreturn_t mps2_uart_combinedirq(int irq, void *data)
+{
+ if (mps2_uart_rxirq(irq, data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ if (mps2_uart_txirq(irq, data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ if (mps2_uart_oerrirq(irq, data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
static int mps2_uart_startup(struct uart_port *port)
{
struct mps2_uart_port *mps_port = to_mps2_port(port);
@@ -274,26 +292,37 @@ static int mps2_uart_startup(struct uart_port *port)
mps2_uart_write8(port, control, UARTn_CTRL);
- ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
- MAKE_NAME(-rx), mps_port);
- if (ret) {
- dev_err(port->dev, "failed to register rxirq (%d)\n", ret);
- return ret;
- }
+ if (mps_port->flags & UART_PORT_COMBINED_IRQ) {
+ ret = request_irq(port->irq, mps2_uart_combinedirq, 0,
+ MAKE_NAME(-combined), mps_port);
- ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
- MAKE_NAME(-tx), mps_port);
- if (ret) {
- dev_err(port->dev, "failed to register txirq (%d)\n", ret);
- goto err_free_rxirq;
- }
+ if (ret) {
+ dev_err(port->dev, "failed to register combinedirq (%d)\n", ret);
+ return ret;
+ }
+ } else {
+ ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
+ MAKE_NAME(-overrun), mps_port);
+
+ if (ret) {
+ dev_err(port->dev, "failed to register oerrirq (%d)\n", ret);
+ return ret;
+ }
+
+ ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
+ MAKE_NAME(-rx), mps_port);
+ if (ret) {
+ dev_err(port->dev, "failed to register rxirq (%d)\n", ret);
+ goto err_free_oerrirq;
+ }
- ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
- MAKE_NAME(-overrun), mps_port);
+ ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
+ MAKE_NAME(-tx), mps_port);
+ if (ret) {
+ dev_err(port->dev, "failed to register txirq (%d)\n", ret);
+ goto err_free_rxirq;
+ }
- if (ret) {
- dev_err(port->dev, "failed to register oerrirq (%d)\n", ret);
- goto err_free_txirq;
}
control |= UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP;
@@ -302,10 +331,10 @@ static int mps2_uart_startup(struct uart_port *port)
return 0;
-err_free_txirq:
- free_irq(mps_port->tx_irq, mps_port);
err_free_rxirq:
free_irq(mps_port->rx_irq, mps_port);
+err_free_oerrirq:
+ free_irq(port->irq, mps_port);
return ret;
}
@@ -319,8 +348,11 @@ static void mps2_uart_shutdown(struct uart_port *port)
mps2_uart_write8(port, control, UARTn_CTRL);
- free_irq(mps_port->rx_irq, mps_port);
- free_irq(mps_port->tx_irq, mps_port);
+ if (!(mps_port->flags & UART_PORT_COMBINED_IRQ)) {
+ free_irq(mps_port->rx_irq, mps_port);
+ free_irq(mps_port->tx_irq, mps_port);
+ }
+
free_irq(port->irq, mps_port);
}
@@ -397,7 +429,7 @@ static const struct uart_ops mps2_uart_pops = {
.verify_port = mps2_uart_verify_port,
};
-static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
+static DEFINE_IDR(ports_idr);
#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
static void mps2_uart_console_putchar(struct uart_port *port, int ch)
@@ -410,7 +442,8 @@ static void mps2_uart_console_putchar(struct uart_port *port, int ch)
static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
{
- struct uart_port *port = &mps2_uart_ports[co->index].port;
+ struct mps2_uart_port *mps_port = idr_find(&ports_idr, co->index);
+ struct uart_port *port = &mps_port->port;
uart_console_write(port, s, cnt, mps2_uart_console_putchar);
}
@@ -426,7 +459,10 @@ static int mps2_uart_console_setup(struct console *co, char *options)
if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
return -ENODEV;
- mps_port = &mps2_uart_ports[co->index];
+ mps_port = idr_find(&ports_idr, co->index);
+
+ if (!mps_port)
+ return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -487,27 +523,36 @@ static struct uart_driver mps2_uart_driver = {
.cons = MPS2_SERIAL_CONSOLE,
};
-static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
+static int mps2_of_get_port(struct platform_device *pdev,
+ struct mps2_uart_port *mps_port)
{
struct device_node *np = pdev->dev.of_node;
int id;
if (!np)
- return NULL;
+ return -ENODEV;
id = of_alias_get_id(np, "serial");
+
if (id < 0)
- id = 0;
+ id = idr_alloc_cyclic(&ports_idr, (void *)mps_port, 0, MPS2_MAX_PORTS, GFP_KERNEL);
+ else
+ id = idr_alloc(&ports_idr, (void *)mps_port, id, MPS2_MAX_PORTS, GFP_KERNEL);
- if (WARN_ON(id >= MPS2_MAX_PORTS))
- return NULL;
+ if (id < 0)
+ return id;
+
+ /* Only combined irq is presesnt */
+ if (platform_irq_count(pdev) == 1)
+ mps_port->flags |= UART_PORT_COMBINED_IRQ;
+
+ mps_port->port.line = id;
- mps2_uart_ports[id].port.line = id;
- return &mps2_uart_ports[id];
+ return 0;
}
-static int mps2_init_port(struct mps2_uart_port *mps_port,
- struct platform_device *pdev)
+static int mps2_init_port(struct platform_device *pdev,
+ struct mps2_uart_port *mps_port)
{
struct resource *res;
int ret;
@@ -519,11 +564,6 @@ static int mps2_init_port(struct mps2_uart_port *mps_port,
mps_port->port.mapbase = res->start;
mps_port->port.mapsize = resource_size(res);
-
- mps_port->rx_irq = platform_get_irq(pdev, 0);
- mps_port->tx_irq = platform_get_irq(pdev, 1);
- mps_port->port.irq = platform_get_irq(pdev, 2);
-
mps_port->port.iotype = UPIO_MEM;
mps_port->port.flags = UPF_BOOT_AUTOCONF;
mps_port->port.fifosize = 1;
@@ -542,6 +582,15 @@ static int mps2_init_port(struct mps2_uart_port *mps_port,
clk_disable_unprepare(mps_port->clk);
+
+ if (mps_port->flags & UART_PORT_COMBINED_IRQ) {
+ mps_port->port.irq = platform_get_irq(pdev, 0);
+ } else {
+ mps_port->rx_irq = platform_get_irq(pdev, 0);
+ mps_port->tx_irq = platform_get_irq(pdev, 1);
+ mps_port->port.irq = platform_get_irq(pdev, 2);
+ }
+
return ret;
}
@@ -550,11 +599,16 @@ static int mps2_serial_probe(struct platform_device *pdev)
struct mps2_uart_port *mps_port;
int ret;
- mps_port = mps2_of_get_port(pdev);
- if (!mps_port)
- return -ENODEV;
+ mps_port = devm_kzalloc(&pdev->dev, sizeof(struct mps2_uart_port), GFP_KERNEL);
+
+ if (!mps_port)
+ return -ENOMEM;
+
+ ret = mps2_of_get_port(pdev, mps_port);
+ if (ret)
+ return ret;
- ret = mps2_init_port(mps_port, pdev);
+ ret = mps2_init_port(pdev, mps_port);
if (ret)
return ret;