From patchwork Sat Sep 12 13:02:12 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Ivan T. Ivanov" X-Patchwork-Id: 53514 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-la0-f72.google.com (mail-la0-f72.google.com [209.85.215.72]) by patches.linaro.org (Postfix) with ESMTPS id 75E2722B26 for ; Sat, 12 Sep 2015 13:03:49 +0000 (UTC) Received: by lanb10 with SMTP id b10sf35050597lan.3 for ; Sat, 12 Sep 2015 06:03:48 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=Yvf4yeo1OASffmr3Go5HaEK5p0g17eRfJk96xRkYLH0=; b=Ama3Qq2aEK3hyigeGcEGqfpl6LzNsiMP4wMS2h26wJZ4PhHn/iFovkVMzB0kkUhUov uFm7IhFUEPGY86K3tH7gs50Qw2Q+P2s8pob1rp6WlqRzzr7KkSpCwV8oTP/wFxadQM3j jeNFCZfuksY8oCnAqhoGBmWdnTeLVy5k1qa6mRdDP1uzPNlpty3fds6jhhbSfD7stHz0 vi34Zna+VE7e2fzbUmUhdqTQGGUNOUEWteKiYXzI3aI9wO5XvUB9LrKcc8azj482UXy/ lMsF+996sfurbMGgb4RsXoHX60hHGY/2OVXjTF9ZgDwAHuzJ8GTO7pfjbgki4tUI3HZY Y19g== X-Gm-Message-State: ALoCoQmdJGJ60AGh6BVvqpFDn7Q97EKSRpbZrTl65yMV1AKPTKdGkI8cnlb2vD/HKbLHlRG8cJrS X-Received: by 10.112.138.2 with SMTP id qm2mr962486lbb.19.1442063028424; Sat, 12 Sep 2015 06:03:48 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.243.39 with SMTP id wv7ls407539lac.85.gmail; Sat, 12 Sep 2015 06:03:48 -0700 (PDT) X-Received: by 10.152.198.197 with SMTP id je5mr3754886lac.124.1442063028228; Sat, 12 Sep 2015 06:03:48 -0700 (PDT) Received: from mail-lb0-f175.google.com (mail-lb0-f175.google.com. [209.85.217.175]) by mx.google.com with ESMTPS id i6si3243244lag.112.2015.09.12.06.03.48 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 12 Sep 2015 06:03:48 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) client-ip=209.85.217.175; Received: by lbbmp1 with SMTP id mp1so50980078lbb.1 for ; Sat, 12 Sep 2015 06:03:48 -0700 (PDT) X-Received: by 10.112.131.98 with SMTP id ol2mr3793816lbb.56.1442063028040; Sat, 12 Sep 2015 06:03:48 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.59.35 with SMTP id w3csp230909lbq; Sat, 12 Sep 2015 06:03:46 -0700 (PDT) X-Received: by 10.68.197.97 with SMTP id it1mr9941207pbc.4.1442063026616; Sat, 12 Sep 2015 06:03:46 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id bb1si6543820pbc.105.2015.09.12.06.03.45; Sat, 12 Sep 2015 06:03:46 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-serial-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754907AbbILNDo (ORCPT + 1 other); Sat, 12 Sep 2015 09:03:44 -0400 Received: from mail-wi0-f174.google.com ([209.85.212.174]:35916 "EHLO mail-wi0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754779AbbILNCo (ORCPT ); Sat, 12 Sep 2015 09:02:44 -0400 Received: by wicgb1 with SMTP id gb1so90898781wic.1 for ; Sat, 12 Sep 2015 06:02:42 -0700 (PDT) X-Received: by 10.194.203.163 with SMTP id kr3mr1332932wjc.140.1442062962446; Sat, 12 Sep 2015 06:02:42 -0700 (PDT) Received: from localhost.localdomain ([37.157.136.206]) by smtp.googlemail.com with ESMTPSA id v6sm5464912wjf.13.2015.09.12.06.02.41 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 12 Sep 2015 06:02:42 -0700 (PDT) From: "Ivan T. Ivanov" To: Andy Gross Cc: David Brown , Greg Kroah-Hartman , Jiri Slaby , Frank Rowand , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-soc@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH 5/7] tty: serial: msm: Add TX DMA support Date: Sat, 12 Sep 2015 16:02:12 +0300 Message-Id: <1442062934-13449-6-git-send-email-ivan.ivanov@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1442062934-13449-1-git-send-email-ivan.ivanov@linaro.org> References: <1442062934-13449-1-git-send-email-ivan.ivanov@linaro.org> Sender: linux-serial-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-serial@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ivan.ivanov@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Add transmit DMA support for UARTDM type of controllers. Tested on APQ8064, which have UARTDM v1.3 and ADM DMA engine and APQ8016, which have UARTDM v1.4 and BAM DMA engine. Signed-off-by: Ivan T. Ivanov --- .../devicetree/bindings/serial/qcom,msm-uartdm.txt | 3 + drivers/tty/serial/msm_serial.c | 312 +++++++++++++++++++-- drivers/tty/serial/msm_serial.h | 3 + 3 files changed, 294 insertions(+), 24 deletions(-) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt index a2114c217376..a600023d9ec1 100644 --- a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt +++ b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt @@ -26,6 +26,9 @@ Required properties: Optional properties: - dmas: Should contain dma specifiers for transmit and receive channels - dma-names: Should contain "tx" for transmit and "rx" for receive channels +- qcom,tx-crci: Identificator for Client Rate Control Interface to be + used with TX DMA channel. Required when using DMA for transmission + with UARTDM v1.3 and bellow. Note: Aliases may be defined to ensure the correct ordering of the UARTs. The alias serialN will result in the UART being assigned port N. If any diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 1c2a02634853..e40611afad09 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -20,6 +20,8 @@ #endif #include +#include +#include #include #include #include @@ -39,6 +41,10 @@ #include "msm_serial.h" +#define UARTDM_BURST_SIZE 16 /* in bytes */ +#define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */ +#define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */ + enum { UARTDM_1P1 = 1, UARTDM_1P2, @@ -46,6 +52,17 @@ enum { UARTDM_1P4, }; +struct msm_dma { + struct dma_chan *chan; + enum dma_data_direction dir; + dma_addr_t phys; + unsigned char *virt; + dma_cookie_t cookie; + u32 enable_bit; + unsigned int count; + struct dma_async_tx_descriptor *desc; +}; + struct msm_port { struct uart_port uart; char name[16]; @@ -55,8 +72,93 @@ struct msm_port { int is_uartdm; unsigned int old_snap_state; bool break_detected; + struct msm_dma tx_dma; }; +static void msm_handle_tx(struct uart_port *port); + +void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) +{ + struct device *dev = port->dev; + unsigned int mapped; + u32 val; + + mapped = dma->count; + dma->count = 0; + + dmaengine_terminate_all(dma->chan); + + /* + * DMA Stall happens if enqueue and flush command happens concurrently. + * For example before changing the baud rate/protocol configuration and + * sending flush command to ADM, disable the channel of UARTDM. + * Note: should not reset the receiver here immediately as it is not + * suggested to do disable/reset or reset/disable at the same time. + */ + val = msm_read(port, UARTDM_DMEN); + val &= ~dma->enable_bit; + msm_write(port, val, UARTDM_DMEN); + + if (mapped) + dma_unmap_single(dev, dma->phys, mapped, dma->dir); +} + +static void msm_release_dma(struct msm_port *msm_port) +{ + struct msm_dma *dma; + + dma = &msm_port->tx_dma; + if (dma->chan) { + msm_stop_dma(&msm_port->uart, dma); + dma_release_channel(dma->chan); + } + + memset(dma, 0, sizeof(*dma)); +} + +static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) +{ + struct device *dev = msm_port->uart.dev; + struct dma_slave_config conf; + struct msm_dma *dma; + u32 crci = 0; + int ret; + + dma = &msm_port->tx_dma; + + /* allocate DMA resources, if available */ + dma->chan = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(dma->chan)) + goto no_tx; + + of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci); + + memset(&conf, 0, sizeof(conf)); + conf.direction = DMA_MEM_TO_DEV; + conf.device_fc = true; + conf.dst_addr = base + UARTDM_TF; + conf.dst_maxburst = UARTDM_BURST_SIZE; + conf.slave_id = crci; + + ret = dmaengine_slave_config(dma->chan, &conf); + if (ret) + goto rel_tx; + + dma->dir = DMA_TO_DEVICE; + + if (msm_port->is_uartdm < UARTDM_1P4) + dma->enable_bit = UARTDM_DMEN_TX_DM_ENABLE; + else + dma->enable_bit = UARTDM_DMEN_TX_BAM_ENABLE; + + return; + +rel_tx: + dma_release_channel(dma->chan); +no_tx: + memset(dma, 0, sizeof(*dma)); +} + static inline void msm_wait_for_xmitr(struct uart_port *port) { while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { @@ -78,11 +180,132 @@ static void msm_stop_tx(struct uart_port *port) static void msm_start_tx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + struct msm_dma *dma = &msm_port->tx_dma; + + /* Already started in DMA mode */ + if (dma->count) + return; + + msm_port->imr |= UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_reset_dm_count(struct uart_port *port, int count) +{ + msm_wait_for_xmitr(port); + msm_write(port, count, UARTDM_NCF_TX); + msm_read(port, UARTDM_NCF_TX); +} + +static void msm_complete_tx_dma(void *args) +{ + struct msm_port *msm_port = args; + struct uart_port *port = &msm_port->uart; + struct circ_buf *xmit = &port->state->xmit; + struct msm_dma *dma = &msm_port->tx_dma; + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + unsigned int count; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + /* Already stopped */ + if (!dma->count) + goto done; + + status = dmaengine_tx_status(dma->chan, dma->cookie, &state); + dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir); + + val = msm_read(port, UARTDM_DMEN); + val &= ~dma->enable_bit; + msm_write(port, val, UARTDM_DMEN); + + if (msm_port->is_uartdm > UARTDM_1P3) { + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); + msm_write(port, UART_CR_TX_ENABLE, UART_CR); + } + + count = dma->count - state.residue; + port->icount.tx += count; + dma->count = 0; + + xmit->tail += count; + xmit->tail &= UART_XMIT_SIZE - 1; + + /* Restore "Tx FIFO below watermark" interrupt */ msm_port->imr |= UART_IMR_TXLEV; msm_write(port, msm_port->imr, UART_IMR); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + msm_handle_tx(port); +done: + spin_unlock_irqrestore(&port->lock, flags); } +static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) +{ + struct circ_buf *xmit = &msm_port->uart.state->xmit; + struct uart_port *port = &msm_port->uart; + struct msm_dma *dma = &msm_port->tx_dma; + void *cpu_addr; + int ret; + u32 val; + + cpu_addr = &xmit->buf[xmit->tail]; + + dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir); + ret = dma_mapping_error(port->dev, dma->phys); + if (ret) + return ret; + + dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, + count, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | + DMA_PREP_FENCE); + if (!dma->desc) { + ret = -EIO; + goto unmap; + } + + dma->desc->callback = msm_complete_tx_dma; + dma->desc->callback_param = msm_port; + + dma->cookie = dmaengine_submit(dma->desc); + ret = dma_submit_error(dma->cookie); + if (ret) + goto unmap; + + /* + * Using DMA complete for Tx FIFO reload, no need for + * "Tx FIFO below watermark" one, disable it + */ + msm_port->imr &= ~UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); + + dma->count = count; + + val = msm_read(port, UARTDM_DMEN); + val |= dma->enable_bit; + + if (msm_port->is_uartdm < UARTDM_1P4) + msm_write(port, val, UARTDM_DMEN); + + msm_reset_dm_count(port, count); + + if (msm_port->is_uartdm > UARTDM_1P3) + msm_write(port, val, UARTDM_DMEN); + + dma_async_issue_pending(dma->chan); + return 0; +unmap: + dma_unmap_single(port->dev, dma->phys, count, dma->dir); + return ret; +} static void msm_stop_rx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -224,18 +447,11 @@ static void msm_handle_rx(struct uart_port *port) spin_lock(&port->lock); } -static void msm_reset_dm_count(struct uart_port *port, int count) -{ - msm_wait_for_xmitr(port); - msm_write(port, count, UARTDM_NCF_TX); - msm_read(port, UARTDM_NCF_TX); -} - -static void msm_handle_tx(struct uart_port *port) +static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) { struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = UART_TO_MSM(port); - unsigned int tx_count, num_chars; + unsigned int num_chars; unsigned int tf_pointer = 0; void __iomem *tf; @@ -244,20 +460,8 @@ static void msm_handle_tx(struct uart_port *port) else tf = port->membase + UART_TF; - tx_count = uart_circ_chars_pending(xmit); - tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail, - port->fifosize); - - if (port->x_char) { - if (msm_port->is_uartdm) - msm_reset_dm_count(port, tx_count + 1); - - iowrite8_rep(tf, &port->x_char, 1); - port->icount.tx++; - port->x_char = 0; - } else if (tx_count && msm_port->is_uartdm) { + if (tx_count && msm_port->is_uartdm) msm_reset_dm_count(port, tx_count); - } while (tf_pointer < tx_count) { int i; @@ -290,6 +494,59 @@ static void msm_handle_tx(struct uart_port *port) uart_write_wakeup(port); } +static void msm_handle_tx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + struct circ_buf *xmit = &msm_port->uart.state->xmit; + struct msm_dma *dma = &msm_port->tx_dma; + unsigned int pio_count, dma_count, dma_min; + void __iomem *tf; + int err = 0; + + if (port->x_char) { + if (msm_port->is_uartdm) + tf = port->membase + UARTDM_TF; + else + tf = port->membase + UART_TF; + + if (msm_port->is_uartdm) + msm_reset_dm_count(port, 1); + + iowrite8_rep(tf, &port->x_char, 1); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + msm_stop_tx(port); + return; + } + + pio_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + dma_min = 1; /* Always DMA */ + if (msm_port->is_uartdm > UARTDM_1P3) { + dma_count = UARTDM_TX_AIGN(dma_count); + dma_min = UARTDM_BURST_SIZE; + } else { + if (dma_count > UARTDM_TX_MAX) + dma_count = UARTDM_TX_MAX; + } + + if (pio_count > port->fifosize) + pio_count = port->fifosize; + + if (!dma->chan || dma_count < dma_min) + msm_handle_tx_pio(port, pio_count); + else + err = msm_handle_tx_dma(msm_port, dma_count); + + if (err) /* fall back to PIO mode */ + msm_handle_tx_pio(port, pio_count); +} + static void msm_handle_delta_cts(struct uart_port *port) { msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); @@ -301,9 +558,10 @@ static irqreturn_t msm_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; struct msm_port *msm_port = UART_TO_MSM(port); + unsigned long flags; unsigned int misr; - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); misr = msm_read(port, UART_MISR); msm_write(port, 0, UART_IMR); /* disable interrupt */ @@ -324,7 +582,7 @@ static irqreturn_t msm_irq(int irq, void *dev_id) msm_handle_delta_cts(port); msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ - spin_unlock(&port->lock); + spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -515,6 +773,9 @@ static int msm_startup(struct uart_port *port) data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, UART_MR1); + if (msm_port->is_uartdm) + msm_request_tx_dma(msm_port, msm_port->uart.mapbase); + return 0; } @@ -525,6 +786,9 @@ static void msm_shutdown(struct uart_port *port) msm_port->imr = 0; msm_write(port, 0, UART_IMR); /* disable interrupts */ + if (msm_port->is_uartdm) + msm_release_dma(msm_port); + clk_disable_unprepare(msm_port->clk); free_irq(port->irq, port); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 223f961f992a..8a5ca6a01de4 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -121,6 +121,9 @@ #define UARTDM_DMEN_RX_SC_ENABLE BIT(5) #define UARTDM_DMEN_TX_SC_ENABLE BIT(4) +#define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */ +#define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */ + #define UARTDM_DMRX 0x34 #define UARTDM_NCF_TX 0x40 #define UARTDM_RX_TOTAL_SNAP 0x38