drivers: PL011: refactor pl011_startup()
[deliverable/linux.git] / drivers / tty / serial / amba-pl011.c
index 8d94c194f090b8ed16ae1e9a418502f224041e36..209aeb68abad780ed94829365ec69f1b431fb8e8 100644 (file)
@@ -164,6 +164,7 @@ struct uart_amba_port {
        bool                    using_rx_dma;
        struct pl011_dmarx_data dmarx;
        struct pl011_dmatx_data dmatx;
+       bool                    dma_probed;
 #endif
 };
 
@@ -261,10 +262,11 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
        }
 }
 
-static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
+static void pl011_dma_probe(struct uart_amba_port *uap)
 {
        /* DMA is the sole user of the platform data right now */
        struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
+       struct device *dev = uap->port.dev;
        struct dma_slave_config tx_conf = {
                .dst_addr = uap->port.mapbase + UART01x_DR,
                .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
@@ -275,9 +277,14 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
        struct dma_chan *chan;
        dma_cap_mask_t mask;
 
-       chan = dma_request_slave_channel(dev, "tx");
+       uap->dma_probed = true;
+       chan = dma_request_slave_channel_reason(dev, "tx");
+       if (IS_ERR(chan)) {
+               if (PTR_ERR(chan) == -EPROBE_DEFER) {
+                       uap->dma_probed = false;
+                       return;
+               }
 
-       if (!chan) {
                /* We need platform data */
                if (!plat || !plat->dma_filter) {
                        dev_info(uap->port.dev, "no DMA platform data\n");
@@ -385,63 +392,17 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
        }
 }
 
-#ifndef MODULE
-/*
- * Stack up the UARTs and let the above initcall be done at device
- * initcall time, because the serial driver is called as an arch
- * initcall, and at this time the DMA subsystem is not yet registered.
- * At this point the driver will switch over to using DMA where desired.
- */
-struct dma_uap {
-       struct list_head node;
-       struct uart_amba_port *uap;
-       struct device *dev;
-};
-
-static LIST_HEAD(pl011_dma_uarts);
-
-static int __init pl011_dma_initcall(void)
-{
-       struct list_head *node, *tmp;
-
-       list_for_each_safe(node, tmp, &pl011_dma_uarts) {
-               struct dma_uap *dmau = list_entry(node, struct dma_uap, node);
-               pl011_dma_probe_initcall(dmau->dev, dmau->uap);
-               list_del(node);
-               kfree(dmau);
-       }
-       return 0;
-}
-
-device_initcall(pl011_dma_initcall);
-
-static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
-{
-       struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL);
-       if (dmau) {
-               dmau->uap = uap;
-               dmau->dev = dev;
-               list_add_tail(&dmau->node, &pl011_dma_uarts);
-       }
-}
-#else
-static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
-{
-       pl011_dma_probe_initcall(dev, uap);
-}
-#endif
-
 static void pl011_dma_remove(struct uart_amba_port *uap)
 {
-       /* TODO: remove the initcall if it has not yet executed */
        if (uap->dmatx.chan)
                dma_release_channel(uap->dmatx.chan);
        if (uap->dmarx.chan)
                dma_release_channel(uap->dmarx.chan);
 }
 
-/* Forward declare this for the refill routine */
+/* Forward declare these for the refill routine */
 static int pl011_dma_tx_refill(struct uart_amba_port *uap);
+static void pl011_start_tx_pio(struct uart_amba_port *uap);
 
 /*
  * The current DMA TX buffer has been sent.
@@ -479,14 +440,13 @@ static void pl011_dma_tx_callback(void *data)
                return;
        }
 
-       if (pl011_dma_tx_refill(uap) <= 0) {
+       if (pl011_dma_tx_refill(uap) <= 0)
                /*
                 * We didn't queue a DMA buffer for some reason, but we
                 * have data pending to be sent.  Re-enable the TX IRQ.
                 */
-               uap->im |= UART011_TXIM;
-               writew(uap->im, uap->port.membase + UART011_IMSC);
-       }
+               pl011_start_tx_pio(uap);
+
        spin_unlock_irqrestore(&uap->port.lock, flags);
 }
 
@@ -664,12 +624,10 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
                if (!uap->dmatx.queued) {
                        if (pl011_dma_tx_refill(uap) > 0) {
                                uap->im &= ~UART011_TXIM;
-                               ret = true;
-                       } else {
-                               uap->im |= UART011_TXIM;
+                               writew(uap->im, uap->port.membase +
+                                      UART011_IMSC);
+                       } else
                                ret = false;
-                       }
-                       writew(uap->im, uap->port.membase + UART011_IMSC);
                } else if (!(uap->dmacr & UART011_TXDMAE)) {
                        uap->dmacr |= UART011_TXDMAE;
                        writew(uap->dmacr,
@@ -1021,6 +979,9 @@ static void pl011_dma_startup(struct uart_amba_port *uap)
 {
        int ret;
 
+       if (!uap->dma_probed)
+               pl011_dma_probe(uap);
+
        if (!uap->dmatx.chan)
                return;
 
@@ -1142,7 +1103,7 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
 
 #else
 /* Blank functions if the DMA engine is not available */
-static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
+static inline void pl011_dma_probe(struct uart_amba_port *uap)
 {
 }
 
@@ -1208,15 +1169,23 @@ static void pl011_stop_tx(struct uart_port *port)
        pl011_dma_tx_stop(uap);
 }
 
+static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
+
+/* Start TX with programmed I/O only (no DMA) */
+static void pl011_start_tx_pio(struct uart_amba_port *uap)
+{
+       uap->im |= UART011_TXIM;
+       writew(uap->im, uap->port.membase + UART011_IMSC);
+       pl011_tx_chars(uap, false);
+}
+
 static void pl011_start_tx(struct uart_port *port)
 {
        struct uart_amba_port *uap =
            container_of(port, struct uart_amba_port, port);
 
-       if (!pl011_dma_tx_start(uap)) {
-               uap->im |= UART011_TXIM;
-               writew(uap->im, uap->port.membase + UART011_IMSC);
-       }
+       if (!pl011_dma_tx_start(uap))
+               pl011_start_tx_pio(uap);
 }
 
 static void pl011_stop_rx(struct uart_port *port)
@@ -1274,16 +1243,29 @@ __acquires(&uap->port.lock)
        spin_lock(&uap->port.lock);
 }
 
-static void pl011_tx_chars(struct uart_amba_port *uap)
+static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
+                         bool from_irq)
+{
+       if (unlikely(!from_irq) &&
+           readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+               return false; /* unable to transmit character */
+
+       writew(c, uap->port.membase + UART01x_DR);
+       uap->port.icount.tx++;
+
+       return true;
+}
+
+static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
 {
        struct circ_buf *xmit = &uap->port.state->xmit;
-       int count;
+       int count = uap->fifosize >> 1;
 
        if (uap->port.x_char) {
-               writew(uap->port.x_char, uap->port.membase + UART01x_DR);
-               uap->port.icount.tx++;
+               if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
+                       return;
                uap->port.x_char = 0;
-               return;
+               --count;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
                pl011_stop_tx(&uap->port);
@@ -1294,14 +1276,15 @@ static void pl011_tx_chars(struct uart_amba_port *uap)
        if (pl011_dma_tx_irq(uap))
                return;
 
-       count = uap->fifosize >> 1;
        do {
-               writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR);
-               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-               uap->port.icount.tx++;
-               if (uart_circ_empty(xmit))
+               if (likely(from_irq) && count-- == 0)
+                       break;
+
+               if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq))
                        break;
-       } while (--count > 0);
+
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+       } while (!uart_circ_empty(xmit));
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(&uap->port);
@@ -1373,7 +1356,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
                                      UART011_CTSMIS|UART011_RIMIS))
                                pl011_modem_status(uap);
                        if (status & UART011_TXIS)
-                               pl011_tx_chars(uap);
+                               pl011_tx_chars(uap, true);
 
                        if (pass_counter-- == 0)
                                break;
@@ -1573,52 +1556,51 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
        }
 }
 
+static int pl011_allocate_irq(struct uart_amba_port *uap)
+{
+       writew(uap->im, uap->port.membase + UART011_IMSC);
+
+       return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
+}
+
+/*
+ * Enable interrupts, only timeouts when using DMA
+ * if initial RX DMA job failed, start in interrupt mode
+ * as well.
+ */
+static void pl011_enable_interrupts(struct uart_amba_port *uap)
+{
+       spin_lock_irq(&uap->port.lock);
+
+       /* Clear out any spuriously appearing RX interrupts */
+       writew(UART011_RTIS | UART011_RXIS,
+              uap->port.membase + UART011_ICR);
+       uap->im = UART011_RTIM;
+       if (!pl011_dma_rx_running(uap))
+               uap->im |= UART011_RXIM;
+       writew(uap->im, uap->port.membase + UART011_IMSC);
+       spin_unlock_irq(&uap->port.lock);
+}
+
 static int pl011_startup(struct uart_port *port)
 {
        struct uart_amba_port *uap =
            container_of(port, struct uart_amba_port, port);
-       unsigned int cr, lcr_h, fbrd, ibrd;
+       unsigned int cr;
        int retval;
 
        retval = pl011_hwinit(port);
        if (retval)
                goto clk_dis;
 
-       writew(uap->im, uap->port.membase + UART011_IMSC);
-
-       /*
-        * Allocate the IRQ
-        */
-       retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
+       retval = pl011_allocate_irq(uap);
        if (retval)
                goto clk_dis;
 
        writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
 
-       /*
-        * Provoke TX FIFO interrupt into asserting. Taking care to preserve
-        * baud rate and data format specified by FBRD, IBRD and LCRH as the
-        * UART may already be in use as a console.
-        */
        spin_lock_irq(&uap->port.lock);
 
-       fbrd = readw(uap->port.membase + UART011_FBRD);
-       ibrd = readw(uap->port.membase + UART011_IBRD);
-       lcr_h = readw(uap->port.membase + uap->lcrh_rx);
-
-       cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
-       writew(cr, uap->port.membase + UART011_CR);
-       writew(0, uap->port.membase + UART011_FBRD);
-       writew(1, uap->port.membase + UART011_IBRD);
-       pl011_write_lcr_h(uap, 0);
-       writew(0, uap->port.membase + UART01x_DR);
-       while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
-               barrier();
-
-       writew(fbrd, uap->port.membase + UART011_FBRD);
-       writew(ibrd, uap->port.membase + UART011_IBRD);
-       pl011_write_lcr_h(uap, lcr_h);
-
        /* restore RTS and DTR */
        cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
        cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
@@ -1634,20 +1616,7 @@ static int pl011_startup(struct uart_port *port)
        /* Startup DMA */
        pl011_dma_startup(uap);
 
-       /*
-        * Finally, enable interrupts, only timeouts when using DMA
-        * if initial RX DMA job failed, start in interrupt mode
-        * as well.
-        */
-       spin_lock_irq(&uap->port.lock);
-       /* Clear out any spuriously appearing RX interrupts */
-        writew(UART011_RTIS | UART011_RXIS,
-               uap->port.membase + UART011_ICR);
-       uap->im = UART011_RTIM;
-       if (!pl011_dma_rx_running(uap))
-               uap->im |= UART011_RXIM;
-       writew(uap->im, uap->port.membase + UART011_IMSC);
-       spin_unlock_irq(&uap->port.lock);
+       pl011_enable_interrupts(uap);
 
        return 0;
 
@@ -2174,6 +2143,23 @@ static int pl011_probe_dt_alias(int index, struct device *dev)
        return ret;
 }
 
+/* unregisters the driver also if no more ports are left */
+static void pl011_unregister_port(struct uart_amba_port *uap)
+{
+       int i;
+       bool busy = false;
+
+       for (i = 0; i < ARRAY_SIZE(amba_ports); i++) {
+               if (amba_ports[i] == uap)
+                       amba_ports[i] = NULL;
+               else if (amba_ports[i])
+                       busy = true;
+       }
+       pl011_dma_remove(uap);
+       if (!busy)
+               uart_unregister_driver(&amba_reg);
+}
+
 static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 {
        struct uart_amba_port *uap;
@@ -2218,7 +2204,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
        uap->port.ops = &amba_pl011_pops;
        uap->port.flags = UPF_BOOT_AUTOCONF;
        uap->port.line = i;
-       pl011_dma_probe(&dev->dev, uap);
 
        /* Ensure interrupts from this UART are masked and cleared */
        writew(0, uap->port.membase + UART011_IMSC);
@@ -2233,17 +2218,15 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
        if (!amba_reg.state) {
                ret = uart_register_driver(&amba_reg);
                if (ret < 0) {
-                       pr_err("Failed to register AMBA-PL011 driver\n");
+                       dev_err(&dev->dev,
+                               "Failed to register AMBA-PL011 driver\n");
                        return ret;
                }
        }
 
        ret = uart_add_one_port(&amba_reg, &uap->port);
-       if (ret) {
-               amba_ports[i] = NULL;
-               uart_unregister_driver(&amba_reg);
-               pl011_dma_remove(uap);
-       }
+       if (ret)
+               pl011_unregister_port(uap);
 
        return ret;
 }
@@ -2251,20 +2234,9 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 static int pl011_remove(struct amba_device *dev)
 {
        struct uart_amba_port *uap = amba_get_drvdata(dev);
-       bool busy = false;
-       int i;
 
        uart_remove_one_port(&amba_reg, &uap->port);
-
-       for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
-               if (amba_ports[i] == uap)
-                       amba_ports[i] = NULL;
-               else if (amba_ports[i])
-                       busy = true;
-
-       pl011_dma_remove(uap);
-       if (!busy)
-               uart_unregister_driver(&amba_reg);
+       pl011_unregister_port(uap);
        return 0;
 }
 
This page took 0.029744 seconds and 5 git commands to generate.