diff options
Diffstat (limited to 'drivers/tty/serial/msm_serial_hs.c')
-rw-r--r-- | drivers/tty/serial/msm_serial_hs.c | 2019 |
1 files changed, 1321 insertions, 698 deletions
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 48e94961a9e5..6c0f56abb16a 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1,10 +1,14 @@ -/* - * MSM 7k/8k High speed uart driver +/* drivers/serial/msm_serial_hs.c + * + * MSM 7k High speed uart driver * - * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. * Copyright (c) 2008 Google Inc. + * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved. * Modified: Nick Pelly <npelly@google.com> * + * All source code in this file is licensed under the following license + * except where indicated. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -30,8 +34,6 @@ #include <linux/serial.h> #include <linux/serial_core.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -40,168 +42,31 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/timer.h> +#include <linux/delay.h> #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> +#include <linux/tty_flip.h> #include <linux/wait.h> -#include <linux/workqueue.h> - -#include <linux/atomic.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/gpio.h> +#include <asm/atomic.h> #include <asm/irq.h> -#include <mach/hardware.h> -#include <mach/dma.h> -#include <linux/platform_data/msm_serial_hs.h> - -/* HSUART Registers */ -#define UARTDM_MR1_ADDR 0x0 -#define UARTDM_MR2_ADDR 0x4 - -/* Data Mover result codes */ -#define RSLT_FIFO_CNTR_BMSK (0xE << 28) -#define RSLT_VLD BIT(1) - -/* write only register */ -#define UARTDM_CSR_ADDR 0x8 -#define UARTDM_CSR_115200 0xFF -#define UARTDM_CSR_57600 0xEE -#define UARTDM_CSR_38400 0xDD -#define UARTDM_CSR_28800 0xCC -#define UARTDM_CSR_19200 0xBB -#define UARTDM_CSR_14400 0xAA -#define UARTDM_CSR_9600 0x99 -#define UARTDM_CSR_7200 0x88 -#define UARTDM_CSR_4800 0x77 -#define UARTDM_CSR_3600 0x66 -#define UARTDM_CSR_2400 0x55 -#define UARTDM_CSR_1200 0x44 -#define UARTDM_CSR_600 0x33 -#define UARTDM_CSR_300 0x22 -#define UARTDM_CSR_150 0x11 -#define UARTDM_CSR_75 0x00 - -/* write only register */ -#define UARTDM_TF_ADDR 0x70 -#define UARTDM_TF2_ADDR 0x74 -#define UARTDM_TF3_ADDR 0x78 -#define UARTDM_TF4_ADDR 0x7C - -/* write only register */ -#define UARTDM_CR_ADDR 0x10 -#define UARTDM_IMR_ADDR 0x14 - -#define UARTDM_IPR_ADDR 0x18 -#define UARTDM_TFWR_ADDR 0x1c -#define UARTDM_RFWR_ADDR 0x20 -#define UARTDM_HCR_ADDR 0x24 -#define UARTDM_DMRX_ADDR 0x34 -#define UARTDM_IRDA_ADDR 0x38 -#define UARTDM_DMEN_ADDR 0x3c - -/* UART_DM_NO_CHARS_FOR_TX */ -#define UARTDM_NCF_TX_ADDR 0x40 - -#define UARTDM_BADR_ADDR 0x44 - -#define UARTDM_SIM_CFG_ADDR 0x80 -/* Read Only register */ -#define UARTDM_SR_ADDR 0x8 - -/* Read Only register */ -#define UARTDM_RF_ADDR 0x70 -#define UARTDM_RF2_ADDR 0x74 -#define UARTDM_RF3_ADDR 0x78 -#define UARTDM_RF4_ADDR 0x7C - -/* Read Only register */ -#define UARTDM_MISR_ADDR 0x10 - -/* Read Only register */ -#define UARTDM_ISR_ADDR 0x14 -#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38 - -#define UARTDM_RXFS_ADDR 0x50 - -/* Register field Mask Mapping */ -#define UARTDM_SR_PAR_FRAME_BMSK BIT(5) -#define UARTDM_SR_OVERRUN_BMSK BIT(4) -#define UARTDM_SR_TXEMT_BMSK BIT(3) -#define UARTDM_SR_TXRDY_BMSK BIT(2) -#define UARTDM_SR_RXRDY_BMSK BIT(0) - -#define UARTDM_CR_TX_DISABLE_BMSK BIT(3) -#define UARTDM_CR_RX_DISABLE_BMSK BIT(1) -#define UARTDM_CR_TX_EN_BMSK BIT(2) -#define UARTDM_CR_RX_EN_BMSK BIT(0) - -/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */ -#define RESET_RX 0x10 -#define RESET_TX 0x20 -#define RESET_ERROR_STATUS 0x30 -#define RESET_BREAK_INT 0x40 -#define START_BREAK 0x50 -#define STOP_BREAK 0x60 -#define RESET_CTS 0x70 -#define RESET_STALE_INT 0x80 -#define RFR_LOW 0xD0 -#define RFR_HIGH 0xE0 -#define CR_PROTECTION_EN 0x100 -#define STALE_EVENT_ENABLE 0x500 -#define STALE_EVENT_DISABLE 0x600 -#define FORCE_STALE_EVENT 0x400 -#define CLEAR_TX_READY 0x300 -#define RESET_TX_ERROR 0x800 -#define RESET_TX_DONE 0x810 - -#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00 -#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f -#define UARTDM_MR1_CTS_CTL_BMSK 0x40 -#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80 - -#define UARTDM_MR2_ERROR_MODE_BMSK 0x40 -#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30 - -/* bits per character configuration */ -#define FIVE_BPC (0 << 4) -#define SIX_BPC (1 << 4) -#define SEVEN_BPC (2 << 4) -#define EIGHT_BPC (3 << 4) - -#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc -#define STOP_BIT_ONE (1 << 2) -#define STOP_BIT_TWO (3 << 2) - -#define UARTDM_MR2_PARITY_MODE_BMSK 0x3 - -/* Parity configuration */ -#define NO_PARITY 0x0 -#define EVEN_PARITY 0x1 -#define ODD_PARITY 0x2 -#define SPACE_PARITY 0x3 - -#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80 -#define UARTDM_IPR_STALE_LSB_BMSK 0x1f - -/* These can be used for both ISR and IMR register */ -#define UARTDM_ISR_TX_READY_BMSK BIT(7) -#define UARTDM_ISR_CURRENT_CTS_BMSK BIT(6) -#define UARTDM_ISR_DELTA_CTS_BMSK BIT(5) -#define UARTDM_ISR_RXLEV_BMSK BIT(4) -#define UARTDM_ISR_RXSTALE_BMSK BIT(3) -#define UARTDM_ISR_RXBREAK_BMSK BIT(2) -#define UARTDM_ISR_RXHUNT_BMSK BIT(1) -#define UARTDM_ISR_TXLEV_BMSK BIT(0) - -/* Field definitions for UART_DM_DMEN*/ -#define UARTDM_TX_DM_EN_BMSK 0x1 -#define UARTDM_RX_DM_EN_BMSK 0x2 - -#define UART_FIFOSIZE 64 -#define UARTCLK 7372800 - -/* Rx DMA request states */ +#include <soc/qcom/msm_dma.h> + +#include "msm_serial_hs.h" +#include "msm_serial_hs_hwreg.h" + +static int hs_serial_debug_mask = 1; +module_param_named(debug_mask, hs_serial_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + enum flush_reason { FLUSH_NONE, FLUSH_DATA_READY, @@ -211,7 +76,6 @@ enum flush_reason { FLUSH_SHUTDOWN, }; -/* UART clock states */ enum msm_hs_clk_states_e { MSM_HS_CLK_PORT_OFF, /* port not in use */ MSM_HS_CLK_OFF, /* clock disabled */ @@ -228,27 +92,11 @@ enum msm_hs_clk_req_off_state_e { CLK_REQ_OFF_RXSTALE_FLUSHED, }; -/** - * struct msm_hs_tx - * @tx_ready_int_en: ok to dma more tx? - * @dma_in_flight: tx dma in progress - * @xfer: top level DMA command pointer structure - * @command_ptr: third level command struct pointer - * @command_ptr_ptr: second level command list struct pointer - * @mapped_cmd_ptr: DMA view of third level command struct - * @mapped_cmd_ptr_ptr: DMA view of second level command list struct - * @tx_count: number of bytes to transfer in DMA transfer - * @dma_base: DMA view of UART xmit buffer - * - * This structure describes a single Tx DMA transaction. MSM DMA - * commands have two levels of indirection. The top level command - * ptr points to a list of command ptr which in turn points to a - * single DMA 'command'. In our case each Tx transaction consists - * of a single second level pointer pointing to a 'box type' command. - */ struct msm_hs_tx { - unsigned int tx_ready_int_en; - unsigned int dma_in_flight; + unsigned int tx_ready_int_en; /* ok to dma more tx */ + unsigned int dma_in_flight; /* tx dma in progress */ + enum flush_reason flush; + wait_queue_head_t wait; struct msm_dmov_cmd xfer; dmov_box *command_ptr; u32 *command_ptr_ptr; @@ -256,25 +104,9 @@ struct msm_hs_tx { dma_addr_t mapped_cmd_ptr_ptr; int tx_count; dma_addr_t dma_base; + struct tasklet_struct tlet; }; -/** - * struct msm_hs_rx - * @flush: Rx DMA request state - * @xfer: top level DMA command pointer structure - * @cmdptr_dmaaddr: DMA view of second level command structure - * @command_ptr: third level DMA command pointer structure - * @command_ptr_ptr: second level DMA command list pointer - * @mapped_cmd_ptr: DMA view of the third level command structure - * @wait: wait for DMA completion before shutdown - * @buffer: destination buffer for RX DMA - * @rbuffer: DMA view of buffer - * @pool: dma pool out of which coherent rx buffer is allocated - * @tty_work: private work-queue for tty flip buffer push task - * - * This structure describes a single Rx DMA transaction. Rx DMA - * transactions use box mode DMA commands. - */ struct msm_hs_rx { enum flush_reason flush; struct msm_dmov_cmd xfer; @@ -285,121 +117,286 @@ struct msm_hs_rx { wait_queue_head_t wait; dma_addr_t rbuffer; unsigned char *buffer; + unsigned int buffer_pending; struct dma_pool *pool; - struct work_struct tty_work; + struct delayed_work flip_insert_work; + struct tasklet_struct tlet; }; -/** - * struct msm_hs_rx_wakeup - * @irq: IRQ line to be configured as interrupt source on Rx activity - * @ignore: boolean value. 1 = ignore the wakeup interrupt - * @rx_to_inject: extra character to be inserted to Rx tty on wakeup - * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character - * - * This is an optional structure required for UART Rx GPIO IRQ based - * wakeup from low power state. UART wakeup can be triggered by RX activity - * (using a wakeup GPIO on the UART RX pin). This should only be used if - * there is not a wakeup GPIO on the UART CTS, and the first RX byte is - * known (eg., with the Bluetooth Texas Instruments HCILL protocol), - * since the first RX byte will always be lost. RTS will be asserted even - * while the UART is clocked off in this mode of operation. - */ -struct msm_hs_rx_wakeup { +enum buffer_states { + NONE_PENDING = 0x0, + FIFO_OVERRUN = 0x1, + PARITY_ERROR = 0x2, + CHARS_NORMAL = 0x4, +}; + +/* optional low power wakeup, typically on a GPIO RX irq */ +struct msm_hs_wakeup { int irq; /* < 0 indicates low power wakeup disabled */ - unsigned char ignore; + unsigned char ignore; /* bool */ + + /* bool: inject char into rx tty on wakeup */ unsigned char inject_rx; char rx_to_inject; }; -/** - * struct msm_hs_port - * @uport: embedded uart port structure - * @imr_reg: shadow value of UARTDM_IMR - * @clk: uart input clock handle - * @tx: Tx transaction related data structure - * @rx: Rx transaction related data structure - * @dma_tx_channel: Tx DMA command channel - * @dma_rx_channel Rx DMA command channel - * @dma_tx_crci: Tx channel rate control interface number - * @dma_rx_crci: Rx channel rate control interface number - * @clk_off_timer: Timer to poll DMA event completion before clock off - * @clk_off_delay: clk_off_timer poll interval - * @clk_state: overall clock state - * @clk_req_off_state: post flush clock states - * @rx_wakeup: optional rx_wakeup feature related data - * @exit_lpm_cb: optional callback to exit low power mode - * - * Low level serial port structure. +/* + * UART can be used in 2-wire or 4-wire mode. + * Use uart_func_mode to set 2-wire or 4-wire mode. */ +enum uart_func_mode { + UART_TWO_WIRE, /* can't support HW Flow control. */ + UART_FOUR_WIRE,/* can support HW Flow control. */ +}; + struct msm_hs_port { struct uart_port uport; - unsigned long imr_reg; + unsigned long imr_reg; /* shadow value of UARTDM_IMR */ struct clk *clk; + struct clk *pclk; struct msm_hs_tx tx; struct msm_hs_rx rx; - + /* gsbi uarts have to do additional writes to gsbi memory */ + /* block and top control status block. The following pointers */ + /* keep a handle to these blocks. */ + unsigned char __iomem *mapped_gsbi; int dma_tx_channel; int dma_rx_channel; int dma_tx_crci; int dma_rx_crci; - - struct hrtimer clk_off_timer; + struct hrtimer clk_off_timer; /* to poll TXEMT before clock off */ ktime_t clk_off_delay; enum msm_hs_clk_states_e clk_state; enum msm_hs_clk_req_off_state_e clk_req_off_state; - struct msm_hs_rx_wakeup rx_wakeup; - void (*exit_lpm_cb)(struct uart_port *); + struct msm_hs_wakeup wakeup; + + struct dentry *loopback_dir; + struct work_struct clock_off_w; /* work for actual clock off */ + struct workqueue_struct *hsuart_wq; /* hsuart workqueue */ + struct mutex clk_mutex; /* mutex to guard against clock off/clock on */ + bool tty_flush_receive; + bool rx_discard_flush_issued; + enum uart_func_mode func_mode; + bool is_shutdown; }; #define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ #define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE #define UARTDM_RX_BUF_SIZE 512 +#define RETRY_TIMEOUT 5 +#define UARTDM_NR 256 +#define RX_FLUSH_COMPLETE_TIMEOUT 300 /* In jiffies */ -#define UARTDM_NR 2 - +static struct dentry *debug_base; static struct msm_hs_port q_uart_port[UARTDM_NR]; static struct platform_driver msm_serial_hs_platform_driver; static struct uart_driver msm_hs_driver; static struct uart_ops msm_hs_ops; -static struct workqueue_struct *msm_hs_workqueue; +static void msm_hs_start_rx_locked(struct uart_port *uport); #define UARTDM_TO_MSM(uart_port) \ container_of((uart_port), struct msm_hs_port, uport) -static unsigned int use_low_power_rx_wakeup(struct msm_hs_port - *msm_uport) +static ssize_t show_clock(struct device *dev, struct device_attribute *attr, + char *buf) { - return (msm_uport->rx_wakeup.irq >= 0); + int state = 1; + enum msm_hs_clk_states_e clk_state; + unsigned long flags; + + struct platform_device *pdev = container_of(dev, struct + platform_device, dev); + struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; + + spin_lock_irqsave(&msm_uport->uport.lock, flags); + clk_state = msm_uport->clk_state; + spin_unlock_irqrestore(&msm_uport->uport.lock, flags); + + if (clk_state <= MSM_HS_CLK_OFF) + state = 0; + + return snprintf(buf, PAGE_SIZE, "%d\n", state); } -static unsigned int msm_hs_read(struct uart_port *uport, +static ssize_t set_clock(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int state; + struct platform_device *pdev = container_of(dev, struct + platform_device, dev); + struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; + + state = buf[0] - '0'; + switch (state) { + case 0: { + msm_hs_request_clock_off(&msm_uport->uport); + break; + } + case 1: { + msm_hs_request_clock_on(&msm_uport->uport); + break; + } + default: { + return -EINVAL; + } + } + return count; +} + +static DEVICE_ATTR(clock, S_IWUSR | S_IRUGO, show_clock, set_clock); + +static inline unsigned int use_low_power_wakeup(struct msm_hs_port *msm_uport) +{ + return (msm_uport->wakeup.irq > 0); +} + +static inline int is_gsbi_uart(struct msm_hs_port *msm_uport) +{ + /* assume gsbi uart if gsbi resource found in pdata */ + return ((msm_uport->mapped_gsbi != NULL)); +} + +static inline unsigned int msm_hs_read(struct uart_port *uport, unsigned int offset) { - return ioread32(uport->membase + offset); + return readl_relaxed(uport->membase + offset); } -static void msm_hs_write(struct uart_port *uport, unsigned int offset, +static inline void msm_hs_write(struct uart_port *uport, unsigned int offset, unsigned int value) { - iowrite32(value, uport->membase + offset); + writel_relaxed(value, uport->membase + offset); } static void msm_hs_release_port(struct uart_port *port) { - iounmap(port->membase); + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(port); + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *gsbi_resource; + resource_size_t size; + + if (is_gsbi_uart(msm_uport)) { + iowrite32(GSBI_PROTOCOL_IDLE, msm_uport->mapped_gsbi + + GSBI_CONTROL_ADDR); + gsbi_resource = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "gsbi_resource"); + if (unlikely(!gsbi_resource)) + return; + + size = resource_size(gsbi_resource); + release_mem_region(gsbi_resource->start, size); + iounmap(msm_uport->mapped_gsbi); + msm_uport->mapped_gsbi = NULL; + } } static int msm_hs_request_port(struct uart_port *port) { - port->membase = ioremap(port->mapbase, PAGE_SIZE); - if (unlikely(!port->membase)) - return -ENOMEM; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(port); + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *gsbi_resource; + resource_size_t size; + + gsbi_resource = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "gsbi_resource"); + if (gsbi_resource) { + size = resource_size(gsbi_resource); + if (unlikely(!request_mem_region(gsbi_resource->start, size, + "msm_serial_hs"))) + return -EBUSY; + msm_uport->mapped_gsbi = ioremap(gsbi_resource->start, + size); + if (!msm_uport->mapped_gsbi) { + release_mem_region(gsbi_resource->start, size); + return -EBUSY; + } + } + /* no gsbi uart */ + return 0; +} - /* configure the CR Protection to Enable */ - msm_hs_write(port, UARTDM_CR_ADDR, CR_PROTECTION_EN); +static int msm_serial_loopback_enable_set(void *data, u64 val) +{ + struct msm_hs_port *msm_uport = data; + struct uart_port *uport = &(msm_uport->uport); + unsigned long flags; + int ret = 0; + + clk_prepare_enable(msm_uport->clk); + if (msm_uport->pclk) + clk_prepare_enable(msm_uport->pclk); + + if (val) { + spin_lock_irqsave(&uport->lock, flags); + ret = msm_hs_read(uport, UARTDM_MR2_ADDR); + ret |= UARTDM_MR2_LOOP_MODE_BMSK; + msm_hs_write(uport, UARTDM_MR2_ADDR, ret); + spin_unlock_irqrestore(&uport->lock, flags); + } else { + spin_lock_irqsave(&uport->lock, flags); + ret = msm_hs_read(uport, UARTDM_MR2_ADDR); + ret &= ~UARTDM_MR2_LOOP_MODE_BMSK; + msm_hs_write(uport, UARTDM_MR2_ADDR, ret); + spin_unlock_irqrestore(&uport->lock, flags); + } + /* Calling CLOCK API. Hence mb() requires here. */ + mb(); + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + + return 0; +} + +static int msm_serial_loopback_enable_get(void *data, u64 *val) +{ + struct msm_hs_port *msm_uport = data; + struct uart_port *uport = &(msm_uport->uport); + unsigned long flags; + int ret = 0; + + clk_prepare_enable(msm_uport->clk); + if (msm_uport->pclk) + clk_prepare_enable(msm_uport->pclk); + + spin_lock_irqsave(&uport->lock, flags); + ret = msm_hs_read(&msm_uport->uport, UARTDM_MR2_ADDR); + spin_unlock_irqrestore(&uport->lock, flags); + + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + + *val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0; return 0; } +DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_serial_loopback_enable_get, + msm_serial_loopback_enable_set, "%llu\n"); + +/* + * msm_serial_hs debugfs node: <debugfs_root>/msm_serial_hs/loopback.<id> + * writing 1 turns on internal loopback mode in HW. Useful for automation + * test scripts. + * writing 0 disables the internal loopback mode. Default is disabled. + */ +static void msm_serial_debugfs_init(struct msm_hs_port *msm_uport, + int id) +{ + char node_name[15]; + snprintf(node_name, sizeof(node_name), "loopback.%d", id); + msm_uport->loopback_dir = debugfs_create_file(node_name, + S_IRUGO | S_IWUSR, + debug_base, + msm_uport, + &loopback_enable_fops); + + if (IS_ERR_OR_NULL(msm_uport->loopback_dir)) + pr_err("%s(): Cannot create loopback.%d debug entry", + __func__, id); +} static int msm_hs_remove(struct platform_device *pdev) { @@ -415,6 +412,9 @@ static int msm_hs_remove(struct platform_device *pdev) msm_uport = &q_uart_port[pdev->id]; dev = msm_uport->uport.dev; + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_clock.attr); + debugfs_remove(msm_uport->loopback_dir); + dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box), DMA_TO_DEVICE); dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, @@ -428,8 +428,13 @@ static int msm_hs_remove(struct platform_device *pdev) dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), DMA_TO_DEVICE); + destroy_workqueue(msm_uport->hsuart_wq); + mutex_destroy(&msm_uport->clk_mutex); + uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); clk_put(msm_uport->clk); + if (msm_uport->pclk) + clk_put(msm_uport->pclk); /* Free the tx resources */ kfree(msm_uport->tx.command_ptr); @@ -444,64 +449,176 @@ static int msm_hs_remove(struct platform_device *pdev) return 0; } -static int msm_hs_init_clk_locked(struct uart_port *uport) +/** + * msm_hs_config_uart_tx_rx_gpios - Configures UART Tx and RX GPIOs + * @port: uart port + */ +static int msm_hs_config_uart_tx_rx_gpios(struct uart_port *uport) +{ + struct platform_device *pdev = to_platform_device(uport->dev); + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; + int ret = -EINVAL; + + + ret = gpio_request(pdata->uart_tx_gpio, "UART_TX_GPIO"); + if (unlikely(ret)) { + pr_err("gpio request failed for:%d\n", + pdata->uart_tx_gpio); + goto exit_uart_config; + } + + ret = gpio_request(pdata->uart_rx_gpio, "UART_RX_GPIO"); + if (unlikely(ret)) { + pr_err("gpio request failed for:%d\n", + pdata->uart_rx_gpio); + gpio_free(pdata->uart_tx_gpio); + goto exit_uart_config; + } + +exit_uart_config: + return ret; +} + +/** + * msm_hs_unconfig_uart_tx_rx_gpios: Unconfigures UART Tx and RX GPIOs + * @port: uart port + */ +static void msm_hs_unconfig_uart_tx_rx_gpios(struct uart_port *uport) +{ + struct platform_device *pdev = to_platform_device(uport->dev); + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; + + + gpio_free(pdata->uart_tx_gpio); + gpio_free(pdata->uart_rx_gpio); + +} + +/** + * msm_hs_config_uart_hwflow_gpios: Configures UART HWFlow GPIOs + * @port: uart port + */ +static int msm_hs_config_uart_hwflow_gpios(struct uart_port *uport) +{ + struct platform_device *pdev = to_platform_device(uport->dev); + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; + int ret = -EINVAL; + + ret = gpio_request(pdata->uart_cts_gpio, "UART_CTS_GPIO"); + if (unlikely(ret)) { + pr_err("gpio request failed for:%d\n", pdata->uart_cts_gpio); + goto exit_config_uart; + } + + ret = gpio_request(pdata->uart_rfr_gpio, "UART_RFR_GPIO"); + if (unlikely(ret)) { + pr_err("gpio request failed for:%d\n", pdata->uart_rfr_gpio); + gpio_free(pdata->uart_cts_gpio); + goto exit_config_uart; + } + +exit_config_uart: + return ret; +} + +/** + * msm_hs_unconfig_uart_hwflow_gpios: Unonfigures UART HWFlow GPIOs + * @port: uart port + */ +static void msm_hs_unconfig_uart_hwflow_gpios(struct uart_port *uport) +{ + struct platform_device *pdev = to_platform_device(uport->dev); + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; + + gpio_free(pdata->uart_cts_gpio); + gpio_free(pdata->uart_rfr_gpio); + +} + +/** + * msm_hs_config_uart_gpios: Configures UART GPIOs and returns success or + * Failure + * @port: uart port + */ +static int msm_hs_config_uart_gpios(struct uart_port *uport) { - int ret; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + int ret; - ret = clk_enable(msm_uport->clk); - if (ret) { - printk(KERN_ERR "Error could not turn on UART clk\n"); - return ret; + /* Configure UART Tx and Rx GPIOs */ + ret = msm_hs_config_uart_tx_rx_gpios(uport); + if (!ret) { + if (msm_uport->func_mode == UART_FOUR_WIRE) { + /*if 4-wire uart, configure CTS and RFR GPIOs */ + ret = msm_hs_config_uart_hwflow_gpios(uport); + if (ret) + msm_hs_unconfig_uart_tx_rx_gpios(uport); + } } + return ret; +} + +/** + * msm_hs_unconfig_uart_gpios: Unconfigures UART GPIOs + * @port: uart port + */ +static void msm_hs_unconfig_uart_gpios(struct uart_port *port) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(port); + + msm_hs_unconfig_uart_tx_rx_gpios(port); + if (msm_uport->func_mode == UART_FOUR_WIRE) + msm_hs_unconfig_uart_hwflow_gpios(port); +} + +static int msm_hs_init_clk(struct uart_port *uport) +{ + int ret; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + /* Set up the MREG/NREG/DREG/MNDREG */ ret = clk_set_rate(msm_uport->clk, uport->uartclk); if (ret) { printk(KERN_WARNING "Error setting clock rate on UART\n"); - clk_disable(msm_uport->clk); return ret; } + ret = clk_prepare_enable(msm_uport->clk); + if (ret) { + printk(KERN_ERR "Error could not turn on UART clk\n"); + return ret; + } + if (msm_uport->pclk) { + ret = clk_prepare_enable(msm_uport->pclk); + if (ret) { + clk_disable_unprepare(msm_uport->clk); + dev_err(uport->dev, + "Error could not turn on UART pclk\n"); + return ret; + } + } + msm_uport->clk_state = MSM_HS_CLK_ON; return 0; } -/* Enable and Disable clocks (Used for power management) */ -static void msm_hs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - if (use_low_power_rx_wakeup(msm_uport) || - msm_uport->exit_lpm_cb) - return; /* ignore linux PM states, - use msm_hs_request_clock API */ - - switch (state) { - case 0: - clk_enable(msm_uport->clk); - break; - case 3: - clk_disable(msm_uport->clk); - break; - default: - dev_err(uport->dev, "msm_serial: Unknown PM state %d\n", - state); - } -} - /* * programs the UARTDM_CSR register with correct bit rates * * Interrupts should be disabled before we are called, as * we modify Set Baud rate - * Set receive stale interrupt level, dependent on Bit Rate + * Set receive stale interrupt level, dependant on Bit Rate * Goal is to have around 8 ms before indicate stale. * roundup (((Bit Rate * .008) / 10) + 1 */ -static void msm_hs_set_bps_locked(struct uart_port *uport, - unsigned int bps) +static unsigned long msm_hs_set_bps_locked(struct uart_port *uport, + unsigned int bps, + unsigned long flags) { unsigned long rxstale; unsigned long data; @@ -509,63 +626,63 @@ static void msm_hs_set_bps_locked(struct uart_port *uport, switch (bps) { case 300: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_75); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x00); rxstale = 1; break; case 600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_150); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x11); rxstale = 1; break; case 1200: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_300); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x22); rxstale = 1; break; case 2400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_600); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x33); rxstale = 1; break; case 4800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_1200); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x44); rxstale = 1; break; case 9600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x55); rxstale = 2; break; case 14400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_3600); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x66); rxstale = 3; break; case 19200: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_4800); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x77); rxstale = 4; break; case 28800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_7200); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x88); rxstale = 6; break; case 38400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_9600); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99); rxstale = 8; break; case 57600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_14400); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xaa); rxstale = 16; break; case 76800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_19200); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xbb); rxstale = 16; break; case 115200: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_28800); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xcc); rxstale = 31; break; case 230400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_57600); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xee); rxstale = 31; break; case 460800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); rxstale = 31; break; case 4000000: @@ -578,24 +695,87 @@ static void msm_hs_set_bps_locked(struct uart_port *uport, case 1152000: case 1000000: case 921600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); rxstale = 31; break; default: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); /* default to 9600 */ bps = 9600; rxstale = 2; break; } - if (bps > 460800) + /* + * uart baud rate depends on CSR and MND Values + * we are updating CSR before and then calling + * clk_set_rate which updates MND Values. Hence + * dsb requires here. + */ + mb(); + if (bps > 460800) { uport->uartclk = bps * 16; - else - uport->uartclk = UARTCLK; + } else { + uport->uartclk = 7372800; + } + spin_unlock_irqrestore(&uport->lock, flags); if (clk_set_rate(msm_uport->clk, uport->uartclk)) { printk(KERN_WARNING "Error setting clock rate on UART\n"); - return; + WARN_ON(1); + spin_lock_irqsave(&uport->lock, flags); + return flags; + } + + spin_lock_irqsave(&uport->lock, flags); + data = rxstale & UARTDM_IPR_STALE_LSB_BMSK; + data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2); + + msm_hs_write(uport, UARTDM_IPR_ADDR, data); + return flags; +} + + +static void msm_hs_set_std_bps_locked(struct uart_port *uport, + unsigned int bps) +{ + unsigned long rxstale; + unsigned long data; + + switch (bps) { + case 9600: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99); + rxstale = 2; + break; + case 14400: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xaa); + rxstale = 3; + break; + case 19200: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xbb); + rxstale = 4; + break; + case 28800: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xcc); + rxstale = 6; + break; + case 38400: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xdd); + rxstale = 8; + break; + case 57600: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xee); + rxstale = 16; + break; + case 115200: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); + rxstale = 31; + break; + default: + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99); + /* default to 9600 */ + bps = 9600; + rxstale = 2; + break; } data = rxstale & UARTDM_IPR_STALE_LSB_BMSK; @@ -611,17 +791,31 @@ static void msm_hs_set_bps_locked(struct uart_port *uport, * Configure the serial port */ static void msm_hs_set_termios(struct uart_port *uport, - struct ktermios *termios, - struct ktermios *oldtermios) + struct ktermios *termios, + struct ktermios *oldtermios) { unsigned int bps; unsigned long data; unsigned long flags; + int ret; unsigned int c_cflag = termios->c_cflag; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + mutex_lock(&msm_uport->clk_mutex); spin_lock_irqsave(&uport->lock, flags); - clk_enable(msm_uport->clk); + + /* + * Disable Rx channel of UARTDM + * DMA Rx Stall happens if enqueue and flush of Rx command happens + * concurrently. Hence before changing the baud rate/protocol + * configuration and sending flush command to ADM, disable the Rx + * 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. + */ + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data &= ~UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); /* 300 is the minimum baud support by the driver */ bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); @@ -630,18 +824,23 @@ static void msm_hs_set_termios(struct uart_port *uport, if (bps == 200) bps = 3200000; - msm_hs_set_bps_locked(uport, bps); + uport->uartclk = clk_get_rate(msm_uport->clk); + if (!uport->uartclk) + msm_hs_set_std_bps_locked(uport, bps); + else + flags = msm_hs_set_bps_locked(uport, bps, flags); data = msm_hs_read(uport, UARTDM_MR2_ADDR); data &= ~UARTDM_MR2_PARITY_MODE_BMSK; /* set parity */ if (PARENB == (c_cflag & PARENB)) { - if (PARODD == (c_cflag & PARODD)) + if (PARODD == (c_cflag & PARODD)) { data |= ODD_PARITY; - else if (CMSPAR == (c_cflag & CMSPAR)) + } else if (CMSPAR == (c_cflag & CMSPAR)) { data |= SPACE_PARITY; - else + } else { data |= EVEN_PARITY; + } } /* Set bits per char */ @@ -686,6 +885,8 @@ static void msm_hs_set_termios(struct uart_port *uport, uport->ignore_status_mask = termios->c_iflag & INPCK; uport->ignore_status_mask |= termios->c_iflag & IGNPAR; + uport->ignore_status_mask |= termios->c_iflag & IGNBRK; + uport->read_status_mask = (termios->c_cflag & CREAD); msm_hs_write(uport, UARTDM_IMR_ADDR, 0); @@ -693,40 +894,59 @@ static void msm_hs_set_termios(struct uart_port *uport, /* Set Transmit software time out */ uart_update_timeout(uport, c_cflag, bps); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); - if (msm_uport->rx.flush == FLUSH_NONE) { msm_uport->rx.flush = FLUSH_IGNORE; - msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); + /* + * Before using dmov APIs make sure that + * previous writel are completed. Hence + * dsb requires here. + */ + mb(); + msm_uport->rx_discard_flush_issued = true; + /* do discard flush */ + msm_dmov_flush(msm_uport->dma_rx_channel, 0); + spin_unlock_irqrestore(&uport->lock, flags); + ret = wait_event_timeout(msm_uport->rx.wait, + msm_uport->rx_discard_flush_issued == false, + RX_FLUSH_COMPLETE_TIMEOUT); + if (!ret) + pr_err("%s(): dmov flush callback timed out\n", + __func__); + spin_lock_irqsave(&uport->lock, flags); } - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + /* After baudrate changes, should perform RX and TX reset */ + msm_hs_write(uport, UARTDM_CR_ADDR, (RESET_RX | RESET_TX)); + /* + * Rx and Tx reset operation takes few clock cycles, hence as + * safe side adding 10us delay. + */ + udelay(10); - clk_disable(msm_uport->clk); + /* Initiate new Rx transfer */ + msm_hs_start_rx_locked(&msm_uport->uport); + + mb(); spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); } /* * Standard API, Transmitter * Any character in the transmit shift register is sent */ -static unsigned int msm_hs_tx_empty(struct uart_port *uport) +unsigned int msm_hs_tx_empty(struct uart_port *uport) { unsigned int data; unsigned int ret = 0; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); data = msm_hs_read(uport, UARTDM_SR_ADDR); if (data & UARTDM_SR_TXEMT_BMSK) ret = TIOCSER_TEMT; - clk_disable(msm_uport->clk); - return ret; } +EXPORT_SYMBOL(msm_hs_tx_empty); /* * Standard API, Stop transmitter. @@ -753,21 +973,20 @@ static void msm_hs_stop_rx_locked(struct uart_port *uport) struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); unsigned int data; - clk_enable(msm_uport->clk); - /* disable dlink */ data = msm_hs_read(uport, UARTDM_DMEN_ADDR); data &= ~UARTDM_RX_DM_EN_BMSK; msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + /* calling DMOV or CLOCK API. Hence mb() */ + mb(); /* Disable the receiver */ - if (msm_uport->rx.flush == FLUSH_NONE) - msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); - + if (msm_uport->rx.flush == FLUSH_NONE) { + /* do discard flush */ + msm_dmov_flush(msm_uport->dma_rx_channel, 0); + } if (msm_uport->rx.flush != FLUSH_SHUTDOWN) msm_uport->rx.flush = FLUSH_STOP; - - clk_disable(msm_uport->clk); } /* Transmit the next chunk of data */ @@ -775,11 +994,16 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) { int left; int tx_count; + int aligned_tx_count; dma_addr_t src_addr; + dma_addr_t aligned_src_addr; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); struct msm_hs_tx *tx = &msm_uport->tx; struct circ_buf *tx_buf = &msm_uport->uport.state->xmit; + if (tx->dma_in_flight || msm_uport->is_shutdown) + return; + if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) { msm_hs_stop_tx_locked(uport); return; @@ -798,8 +1022,13 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) tx_count = left; src_addr = tx->dma_base + tx_buf->tail; - dma_sync_single_for_device(uport->dev, src_addr, tx_count, - DMA_TO_DEVICE); + /* Mask the src_addr to align on a cache + * and add those bytes to tx_count */ + aligned_src_addr = src_addr & ~(dma_get_cache_alignment() - 1); + aligned_tx_count = tx_count + src_addr - aligned_src_addr; + + dma_sync_single_for_device(uport->dev, aligned_src_addr, + aligned_tx_count, DMA_TO_DEVICE); tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) | ((tx_count + 15) >> 4); @@ -810,9 +1039,6 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr); - dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr, - sizeof(u32), DMA_TO_DEVICE); - /* Save tx_count to use in Callback */ tx->tx_count = tx_count; msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count); @@ -820,6 +1046,12 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) /* Disable the tx_ready interrupt */ msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + /* Calling next DMOV API. Hence mb() here. */ + mb(); + + dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr, + sizeof(u32), DMA_TO_DEVICE); + msm_uport->tx.flush = FLUSH_NONE; msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer); } @@ -827,106 +1059,122 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) static void msm_hs_start_rx_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned int buffer_pending = msm_uport->rx.buffer_pending; + unsigned int data; + + msm_uport->rx.buffer_pending = 0; + if (buffer_pending && hs_serial_debug_mask) + printk(KERN_ERR "Error: rx started in buffer state = %x", + buffer_pending); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK; + + /* + * Enable UARTDM Rx Interface as previously it has been + * disable in set_termios before configuring baud rate. + */ + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data |= UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + /* Calling next DMOV API. Hence mb() here. */ + mb(); msm_uport->rx.flush = FLUSH_NONE; msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer); - /* might have finished RX and be ready to clock off */ - hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay, - HRTIMER_MODE_REL); -} - -/* Enable the transmitter Interrupt */ -static void msm_hs_start_tx_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - if (msm_uport->exit_lpm_cb) - msm_uport->exit_lpm_cb(uport); - - if (msm_uport->tx.tx_ready_int_en == 0) { - msm_uport->tx.tx_ready_int_en = 1; - msm_hs_submit_tx_locked(uport); - } - - clk_disable(msm_uport->clk); } -/* - * This routine is called when we are done with a DMA transfer - * - * This routine is registered with Data mover when we set - * up a Data Mover transfer. It is called from Data mover ISR - * when the DMA transfer is done. - */ -static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, - unsigned int result, - struct msm_dmov_errdata *err) +static void flip_insert_work(struct work_struct *work) { unsigned long flags; - struct msm_hs_port *msm_uport; - - /* DMA did not finish properly */ - WARN_ON((((result & RSLT_FIFO_CNTR_BMSK) >> 28) == 1) && - !(result & RSLT_VLD)); - - msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer); + int retval; + struct msm_hs_port *msm_uport = + container_of(work, struct msm_hs_port, + rx.flip_insert_work.work); + struct tty_struct *tty = msm_uport->uport.state->port.tty; + struct tty_port *port = tty->port; spin_lock_irqsave(&msm_uport->uport.lock, flags); - clk_enable(msm_uport->clk); - - msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; - msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - clk_disable(msm_uport->clk); + if (msm_uport->rx.buffer_pending == NONE_PENDING) { + if (hs_serial_debug_mask) + printk(KERN_ERR "Error: No buffer pending in %s", + __func__); + return; + } + if (msm_uport->rx.buffer_pending & FIFO_OVERRUN) { + retval = tty_insert_flip_char(port, 0, TTY_OVERRUN); + if (retval) + msm_uport->rx.buffer_pending &= ~FIFO_OVERRUN; + } + if (msm_uport->rx.buffer_pending & PARITY_ERROR) { + retval = tty_insert_flip_char(port, 0, TTY_PARITY); + if (retval) + msm_uport->rx.buffer_pending &= ~PARITY_ERROR; + } + if (msm_uport->rx.buffer_pending & CHARS_NORMAL) { + int rx_count, rx_offset; + rx_count = (msm_uport->rx.buffer_pending & 0xFFFF0000) >> 16; + rx_offset = (msm_uport->rx.buffer_pending & 0xFFD0) >> 5; + retval = tty_insert_flip_string(port, msm_uport->rx.buffer + + rx_offset, rx_count); + msm_uport->rx.buffer_pending &= (FIFO_OVERRUN | + PARITY_ERROR); + if (retval != rx_count) + msm_uport->rx.buffer_pending |= CHARS_NORMAL | + retval << 8 | (rx_count - retval) << 16; + } + if (msm_uport->rx.buffer_pending) + schedule_delayed_work(&msm_uport->rx.flip_insert_work, + msecs_to_jiffies(RETRY_TIMEOUT)); + else + if ((msm_uport->clk_state == MSM_HS_CLK_ON) && + (msm_uport->rx.flush <= FLUSH_IGNORE)) { + if (hs_serial_debug_mask) + printk(KERN_WARNING + "msm_serial_hs: " + "Pending buffers cleared. " + "Restarting\n"); + msm_hs_start_rx_locked(&msm_uport->uport); + } spin_unlock_irqrestore(&msm_uport->uport.lock, flags); + tty_flip_buffer_push(port); } -/* - * This routine is called when we are done with a DMA transfer or the - * a flush has been sent to the data mover driver. - * - * This routine is registered with Data mover when we set up a Data Mover - * transfer. It is called from Data mover ISR when the DMA transfer is done. - */ -static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, - unsigned int result, - struct msm_dmov_errdata *err) +static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr) { int retval; int rx_count; unsigned long status; - unsigned int error_f = 0; unsigned long flags; - unsigned int flush; - struct tty_port *port; + unsigned int error_f = 0; struct uart_port *uport; struct msm_hs_port *msm_uport; + unsigned int flush; + struct tty_struct *tty; + struct tty_port *port; - msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer); + msm_uport = container_of((struct tasklet_struct *)tlet_ptr, + struct msm_hs_port, rx.tlet); uport = &msm_uport->uport; + tty = uport->state->port.tty; + port = tty->port; - spin_lock_irqsave(&uport->lock, flags); - clk_enable(msm_uport->clk); + status = msm_hs_read(uport, UARTDM_SR_ADDR); - port = &uport->state->port; + spin_lock_irqsave(&uport->lock, flags); msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); - status = msm_hs_read(uport, UARTDM_SR_ADDR); - /* overflow is not connect to data in a FIFO */ if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) && (uport->read_status_mask & CREAD))) { - tty_insert_flip_char(port, 0, TTY_OVERRUN); + retval = tty_insert_flip_char(port, 0, TTY_OVERRUN); + if (!retval) + msm_uport->rx.buffer_pending |= TTY_OVERRUN; uport->icount.buf_overrun++; error_f = 1; } @@ -936,10 +1184,27 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) { /* Can not tell difference between parity & frame error */ + if (hs_serial_debug_mask) + printk(KERN_WARNING "msm_serial_hs: parity error\n"); uport->icount.parity++; error_f = 1; - if (uport->ignore_status_mask & IGNPAR) - tty_insert_flip_char(port, 0, TTY_PARITY); + if (!(uport->ignore_status_mask & IGNPAR)) { + retval = tty_insert_flip_char(port, 0, TTY_PARITY); + if (!retval) + msm_uport->rx.buffer_pending |= TTY_PARITY; + } + } + + if (unlikely(status & UARTDM_SR_RX_BREAK_BMSK)) { + if (hs_serial_debug_mask) + printk(KERN_WARNING "msm_serial_hs: Rx break\n"); + uport->icount.brk++; + error_f = 1; + if (!(uport->ignore_status_mask & IGNBRK)) { + retval = tty_insert_flip_char(port, 0, TTY_BREAK); + if (!retval) + msm_uport->rx.buffer_pending |= TTY_BREAK; + } } if (error_f) @@ -947,40 +1212,149 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED) msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED; - flush = msm_uport->rx.flush; - if (flush == FLUSH_IGNORE) - msm_hs_start_rx_locked(uport); - if (flush == FLUSH_STOP) + + /* Part of set_termios sets the flush as FLUSH_IGNORE */ + if (flush == FLUSH_IGNORE) { + if (!msm_uport->rx_discard_flush_issued && + !msm_uport->rx.buffer_pending) { + msm_hs_start_rx_locked(uport); + } else { + msm_uport->rx_discard_flush_issued = false; + wake_up(&msm_uport->rx.wait); + } + } + + /* Part of stop_rx sets the flush as FLUSH_STOP */ + if (flush == FLUSH_STOP) { + if (msm_uport->rx_discard_flush_issued) + msm_uport->rx_discard_flush_issued = false; msm_uport->rx.flush = FLUSH_SHUTDOWN; + wake_up(&msm_uport->rx.wait); + } + if (flush >= FLUSH_DATA_INVALID) goto out; rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR); + /* order the read of rx.buffer */ + rmb(); + if (0 != (uport->read_status_mask & CREAD)) { retval = tty_insert_flip_string(port, msm_uport->rx.buffer, rx_count); - BUG_ON(retval != rx_count); + if (retval != rx_count) { + msm_uport->rx.buffer_pending |= CHARS_NORMAL | + retval << 5 | (rx_count - retval) << 16; + } } - msm_hs_start_rx_locked(uport); + /* order the read of rx.buffer and the start of next rx xfer */ + wmb(); -out: - clk_disable(msm_uport->clk); + if (!msm_uport->rx.buffer_pending) + msm_hs_start_rx_locked(uport); +out: + if (msm_uport->rx.buffer_pending) { + if (hs_serial_debug_mask) + printk(KERN_WARNING + "msm_serial_hs: " + "tty buffer exhausted. " + "Stalling\n"); + schedule_delayed_work(&msm_uport->rx.flip_insert_work + , msecs_to_jiffies(RETRY_TIMEOUT)); + } + /* tty_flip_buffer_push() might call msm_hs_start(), so unlock */ spin_unlock_irqrestore(&uport->lock, flags); - if (flush < FLUSH_DATA_INVALID) - queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); + tty_flip_buffer_push(port); } -static void msm_hs_tty_flip_buffer_work(struct work_struct *work) +/* Enable the transmitter Interrupt */ +static void msm_hs_start_tx_locked(struct uart_port *uport ) { - struct msm_hs_port *msm_uport = - container_of(work, struct msm_hs_port, rx.tty_work); + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - tty_flip_buffer_push(&msm_uport->uport.state->port); + if (msm_uport->is_shutdown) + return; + + if (msm_uport->tx.tx_ready_int_en == 0) { + msm_uport->tx.tx_ready_int_en = 1; + if (msm_uport->tx.dma_in_flight == 0) + msm_hs_submit_tx_locked(uport); + } +} + +/* + * This routine is called when we are done with a DMA transfer + * + * This routine is registered with Data mover when we set + * up a Data Mover transfer. It is called from Data mover ISR + * when the DMA transfer is done. + */ +static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msm_hs_port *msm_uport; + + msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer); + if (msm_uport->tx.flush == FLUSH_STOP) + /* DMA FLUSH unsuccesfful */ + WARN_ON(!(result & DMOV_RSLT_FLUSH)); + else + /* DMA did not finish properly */ + WARN_ON(!(result & DMOV_RSLT_DONE)); + + tasklet_schedule(&msm_uport->tx.tlet); +} + +static void msm_serial_hs_tx_tlet(unsigned long tlet_ptr) +{ + unsigned long flags; + struct msm_hs_port *msm_uport = container_of((struct tasklet_struct *) + tlet_ptr, struct msm_hs_port, tx.tlet); + struct msm_hs_tx *tx = &msm_uport->tx; + + spin_lock_irqsave(&(msm_uport->uport.lock), flags); + + tx->dma_in_flight = 0; + if (msm_uport->tx.flush == FLUSH_STOP) { + msm_uport->tx.flush = FLUSH_SHUTDOWN; + wake_up(&msm_uport->tx.wait); + spin_unlock_irqrestore(&(msm_uport->uport.lock), flags); + return; + } + + msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; + msm_hs_write(&(msm_uport->uport), UARTDM_IMR_ADDR, msm_uport->imr_reg); + /* Calling clk API. Hence mb() requires. */ + mb(); + + spin_unlock_irqrestore(&(msm_uport->uport.lock), flags); +} + +/* + * This routine is called when we are done with a DMA transfer or the + * a flush has been sent to the data mover driver. + * + * This routine is registered with Data mover when we set up a Data Mover + * transfer. It is called from Data mover ISR when the DMA transfer is done. + */ +static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msm_hs_port *msm_uport; + + if (result & DMOV_RSLT_ERROR) + pr_err("%s(): DMOV_RSLT_ERROR\n", __func__); + + msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer); + + tasklet_schedule(&msm_uport->rx.tlet); } /* @@ -1002,59 +1376,66 @@ static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport) } /* - * True enables UART auto RFR, which indicates we are ready for data if the RX - * buffer is not full. False disables auto RFR, and deasserts RFR to indicate - * we are not ready for data. Must be called with UART clock on. + * Standard API, Set or clear RFR_signal + * + * Set RFR high, (Indicate we are not ready for data), we disable auto + * ready for receiving and then set RFR_N high. To set RFR to low we just turn + * back auto ready for receiving and it should lower RFR signal + * when hardware is ready */ -static void set_rfr_locked(struct uart_port *uport, int auto_rfr) +void msm_hs_set_mctrl_locked(struct uart_port *uport, + unsigned int mctrl) { + unsigned int set_rts; unsigned int data; - data = msm_hs_read(uport, UARTDM_MR1_ADDR); + /* RTS is active low */ + set_rts = TIOCM_RTS & mctrl ? 0 : 1; - if (auto_rfr) { - /* enable auto ready-for-receiving */ - data |= UARTDM_MR1_RX_RDY_CTL_BMSK; - msm_hs_write(uport, UARTDM_MR1_ADDR, data); - } else { - /* disable auto ready-for-receiving */ + data = msm_hs_read(uport, UARTDM_MR1_ADDR); + if (set_rts) { + /*disable auto ready-for-receiving */ data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; msm_hs_write(uport, UARTDM_MR1_ADDR, data); - /* RFR is active low, set high */ + /* set RFR_N to high */ msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH); + } else { + /* Enable auto ready-for-receiving */ + data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + msm_hs_write(uport, UARTDM_MR1_ADDR, data); } + mb(); } -/* - * Standard API, used to set or clear RFR - */ -static void msm_hs_set_mctrl_locked(struct uart_port *uport, +void msm_hs_set_mctrl(struct uart_port *uport, unsigned int mctrl) { - unsigned int auto_rfr; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - auto_rfr = TIOCM_RTS & mctrl ? 1 : 0; - set_rfr_locked(uport, auto_rfr); + unsigned long flags; - clk_disable(msm_uport->clk); + spin_lock_irqsave(&uport->lock, flags); + msm_hs_set_mctrl_locked(uport, mctrl); + spin_unlock_irqrestore(&uport->lock, flags); } +EXPORT_SYMBOL(msm_hs_set_mctrl); /* Standard API, Enable modem status (CTS) interrupt */ static void msm_hs_enable_ms_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - clk_enable(msm_uport->clk); - /* Enable DELTA_CTS Interrupt */ msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + mb(); + +} - clk_disable(msm_uport->clk); +static void msm_hs_flush_buffer_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + if (msm_uport->tx.dma_in_flight) + msm_uport->tty_flush_receive = true; } /* @@ -1065,38 +1446,45 @@ static void msm_hs_enable_ms_locked(struct uart_port *uport) */ static void msm_hs_break_ctl(struct uart_port *uport, int ctl) { - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned long flags; - clk_enable(msm_uport->clk); + spin_lock_irqsave(&uport->lock, flags); msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK); - clk_disable(msm_uport->clk); + mb(); + spin_unlock_irqrestore(&uport->lock, flags); } static void msm_hs_config_port(struct uart_port *uport, int cfg_flags) { unsigned long flags; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - spin_lock_irqsave(&uport->lock, flags); if (cfg_flags & UART_CONFIG_TYPE) { uport->type = PORT_MSM; msm_hs_request_port(uport); } - spin_unlock_irqrestore(&uport->lock, flags); + + if (is_gsbi_uart(msm_uport)) { + if (msm_uport->pclk) + clk_prepare_enable(msm_uport->pclk); + spin_lock_irqsave(&uport->lock, flags); + iowrite32(GSBI_PROTOCOL_UART, msm_uport->mapped_gsbi + + GSBI_CONTROL_ADDR); + spin_unlock_irqrestore(&uport->lock, flags); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + } } /* Handle CTS changes (Called from interrupt handler) */ static void msm_hs_handle_delta_cts_locked(struct uart_port *uport) { - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - /* clear interrupt */ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); + /* Calling CLOCK API. Hence mb() requires here. */ + mb(); uport->icount.cts++; - clk_disable(msm_uport->clk); - /* clear the IOCTL TIOCMIWAIT if called */ wake_up_interruptible(&uport->state->port.delta_msr_wait); } @@ -1106,81 +1494,126 @@ static void msm_hs_handle_delta_cts_locked(struct uart_port *uport) * -1 did not clock off, do not retry * 1 if we clocked off */ -static int msm_hs_check_clock_off_locked(struct uart_port *uport) +static int msm_hs_check_clock_off(struct uart_port *uport) { unsigned long sr_status; + unsigned long flags; + int ret; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); struct circ_buf *tx_buf = &uport->state->xmit; + mutex_lock(&msm_uport->clk_mutex); + spin_lock_irqsave(&uport->lock, flags); + /* Cancel if tx tty buffer is not empty, dma is in flight, - * or tx fifo is not empty, or rx fifo is not empty */ + * or tx fifo is not empty */ if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF || !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight || - (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) || - !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK)) { + msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) { + spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); return -1; } /* Make sure the uart is finished with the last byte */ sr_status = msm_hs_read(uport, UARTDM_SR_ADDR); - if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) + if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) { + spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); return 0; /* retry */ + } /* Make sure forced RXSTALE flush complete */ switch (msm_uport->clk_req_off_state) { case CLK_REQ_OFF_START: msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED; msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT); + /* + * Before returning make sure that device writel completed. + * Hence mb() requires here. + */ + mb(); + spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); return 0; /* RXSTALE flush not complete - retry */ case CLK_REQ_OFF_RXSTALE_ISSUED: case CLK_REQ_OFF_FLUSH_ISSUED: + spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); return 0; /* RXSTALE flush not complete - retry */ case CLK_REQ_OFF_RXSTALE_FLUSHED: break; /* continue */ } if (msm_uport->rx.flush != FLUSH_SHUTDOWN) { - if (msm_uport->rx.flush == FLUSH_NONE) + if (msm_uport->rx.flush == FLUSH_NONE) { msm_hs_stop_rx_locked(uport); + msm_uport->rx_discard_flush_issued = true; + } + + spin_unlock_irqrestore(&uport->lock, flags); + if (msm_uport->rx_discard_flush_issued) { + pr_debug("%s(): wainting for flush completion.\n", + __func__); + ret = wait_event_timeout(msm_uport->rx.wait, + msm_uport->rx_discard_flush_issued == false, + RX_FLUSH_COMPLETE_TIMEOUT); + if (!ret) + pr_err("%s(): Flush complete pending.\n", + __func__); + } + + mutex_unlock(&msm_uport->clk_mutex); return 0; /* come back later to really clock off */ } + spin_unlock_irqrestore(&uport->lock, flags); + /* we really want to clock off */ - clk_disable(msm_uport->clk); + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + msm_uport->clk_state = MSM_HS_CLK_OFF; - if (use_low_power_rx_wakeup(msm_uport)) { - msm_uport->rx_wakeup.ignore = 1; - enable_irq(msm_uport->rx_wakeup.irq); + spin_lock_irqsave(&uport->lock, flags); + if (use_low_power_wakeup(msm_uport)) { + msm_uport->wakeup.ignore = 1; + enable_irq(msm_uport->wakeup.irq); } + + spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); return 1; } -static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer) +static void hsuart_clock_off_work(struct work_struct *w) { - unsigned long flags; - int ret = HRTIMER_NORESTART; - struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port, - clk_off_timer); + struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port, + clock_off_w); struct uart_port *uport = &msm_uport->uport; - spin_lock_irqsave(&uport->lock, flags); - - if (!msm_hs_check_clock_off_locked(uport)) { - hrtimer_forward_now(timer, msm_uport->clk_off_delay); - ret = HRTIMER_RESTART; + if (!msm_hs_check_clock_off(uport)) { + hrtimer_start(&msm_uport->clk_off_timer, + msm_uport->clk_off_delay, + HRTIMER_MODE_REL); } +} - spin_unlock_irqrestore(&uport->lock, flags); +static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer) +{ + struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port, + clk_off_timer); - return ret; + queue_work(msm_uport->hsuart_wq, &msm_uport->clock_off_w); + return HRTIMER_NORESTART; } static irqreturn_t msm_hs_isr(int irq, void *dev) { unsigned long flags; unsigned long isr_status; - struct msm_hs_port *msm_uport = dev; + struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev; struct uart_port *uport = &msm_uport->uport; struct circ_buf *tx_buf = &uport->state->xmit; struct msm_hs_tx *tx = &msm_uport->tx; @@ -1188,24 +1621,37 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) spin_lock_irqsave(&uport->lock, flags); + if (msm_uport->is_shutdown) { + spin_unlock_irqrestore(&uport->lock, flags); + return IRQ_HANDLED; + } + isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR); /* Uart RX starting */ if (isr_status & UARTDM_ISR_RXLEV_BMSK) { msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + /* Complete device write for IMR. Hence mb() requires. */ + mb(); } /* Stale rx interrupt */ if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + /* + * Complete device write before calling DMOV API. Hence + * mb() requires here. + */ + mb(); if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED) msm_uport->clk_req_off_state = - CLK_REQ_OFF_FLUSH_ISSUED; + CLK_REQ_OFF_FLUSH_ISSUED; + if (rx->flush == FLUSH_NONE) { rx->flush = FLUSH_DATA_READY; - msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); + msm_dmov_flush(msm_uport->dma_rx_channel, 1); } } /* tx ready interrupt */ @@ -1218,11 +1664,20 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); } - + /* + * Complete both writes before starting new TX. + * Hence mb() requires here. + */ + mb(); /* Complete DMA TX transactions and submit new transactions */ - tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE; - tx->dma_in_flight = 0; + /* Do not update tx_buf.tail if uart_flush_buffer already + called in serial core */ + if (!msm_uport->tty_flush_receive) + tx_buf->tail = (tx_buf->tail + + tx->tx_count) & ~UART_XMIT_SIZE; + else + msm_uport->tty_flush_receive = false; uport->icount.tx += tx->tx_count; if (tx->tx_ready_int_en) @@ -1235,10 +1690,12 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) /* TX FIFO is empty */ msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - if (!msm_hs_check_clock_off_locked(uport)) - hrtimer_start(&msm_uport->clk_off_timer, - msm_uport->clk_off_delay, - HRTIMER_MODE_REL); + /* + * Complete device write before starting clock_off request. + * Hence mb() requires here. + */ + mb(); + queue_work(msm_uport->hsuart_wq, &msm_uport->clock_off_w); } /* Change in CTS interrupt */ @@ -1250,51 +1707,59 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) return IRQ_HANDLED; } -void msm_hs_request_clock_off_locked(struct uart_port *uport) -{ +/* request to turn off uart clock once pending TX is flushed */ +void msm_hs_request_clock_off(struct uart_port *uport) { + unsigned long flags; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + spin_lock_irqsave(&uport->lock, flags); if (msm_uport->clk_state == MSM_HS_CLK_ON) { msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; msm_uport->clk_req_off_state = CLK_REQ_OFF_START; - if (!use_low_power_rx_wakeup(msm_uport)) - set_rfr_locked(uport, 0); msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + /* + * Complete device write before retuning back. + * Hence mb() requires here. + */ + mb(); } -} - -/** - * msm_hs_request_clock_off - request to (i.e. asynchronously) turn off uart - * clock once pending TX is flushed and Rx DMA command is terminated. - * @uport: uart_port structure for the device instance. - * - * This functions puts the device into a partially active low power mode. It - * waits to complete all pending tx transactions, flushes ongoing Rx DMA - * command and terminates UART side Rx transaction, puts UART HW in non DMA - * mode and then clocks off the device. A client calls this when no UART - * data is expected. msm_request_clock_on() must be called before any further - * UART can be sent or received. - */ -void msm_hs_request_clock_off(struct uart_port *uport) -{ - unsigned long flags; - - spin_lock_irqsave(&uport->lock, flags); - msm_hs_request_clock_off_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); } +EXPORT_SYMBOL(msm_hs_request_clock_off); -void msm_hs_request_clock_on_locked(struct uart_port *uport) +void msm_hs_request_clock_on(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned long flags; unsigned int data; + int ret = 0; + + mutex_lock(&msm_uport->clk_mutex); + spin_lock_irqsave(&uport->lock, flags); switch (msm_uport->clk_state) { case MSM_HS_CLK_OFF: - clk_enable(msm_uport->clk); - disable_irq_nosync(msm_uport->rx_wakeup.irq); - /* fall-through */ + disable_irq_nosync(msm_uport->wakeup.irq); + spin_unlock_irqrestore(&uport->lock, flags); + ret = clk_prepare_enable(msm_uport->clk); + if (ret) { + dev_err(uport->dev, "Clock ON Failure" + "For UART CLK Stalling HSUART\n"); + break; + } + + if (msm_uport->pclk) { + ret = clk_prepare_enable(msm_uport->pclk); + if (unlikely(ret)) { + clk_disable_unprepare(msm_uport->clk); + dev_err(uport->dev, "Clock ON Failure" + "For UART Pclk Stalling HSUART\n"); + break; + } + } + spin_lock_irqsave(&uport->lock, flags); + /* else fall-through */ case MSM_HS_CLK_REQUEST_OFF: if (msm_uport->rx.flush == FLUSH_STOP || msm_uport->rx.flush == FLUSH_SHUTDOWN) { @@ -1302,12 +1767,12 @@ void msm_hs_request_clock_on_locked(struct uart_port *uport) data = msm_hs_read(uport, UARTDM_DMEN_ADDR); data |= UARTDM_RX_DM_EN_BMSK; msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + /* Complete above device write. Hence mb() here. */ + mb(); } hrtimer_try_to_cancel(&msm_uport->clk_off_timer); if (msm_uport->rx.flush == FLUSH_SHUTDOWN) msm_hs_start_rx_locked(uport); - if (!use_low_power_rx_wakeup(msm_uport)) - set_rfr_locked(uport, 1); if (msm_uport->rx.flush == FLUSH_STOP) msm_uport->rx.flush = FLUSH_IGNORE; msm_uport->clk_state = MSM_HS_CLK_ON; @@ -1317,39 +1782,27 @@ void msm_hs_request_clock_on_locked(struct uart_port *uport) case MSM_HS_CLK_PORT_OFF: break; } -} -/** - * msm_hs_request_clock_on - Switch the device from partially active low - * power mode to fully active (i.e. clock on) mode. - * @uport: uart_port structure for the device. - * - * This function switches on the input clock, puts UART HW into DMA mode - * and enqueues an Rx DMA command if the device was in partially active - * mode. It has no effect if called with the device in inactive state. - */ -void msm_hs_request_clock_on(struct uart_port *uport) -{ - unsigned long flags; - - spin_lock_irqsave(&uport->lock, flags); - msm_hs_request_clock_on_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); + mutex_unlock(&msm_uport->clk_mutex); } +EXPORT_SYMBOL(msm_hs_request_clock_on); -static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) +static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) { unsigned int wakeup = 0; unsigned long flags; - struct msm_hs_port *msm_uport = dev; + struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev; struct uart_port *uport = &msm_uport->uport; + struct tty_struct *tty = NULL; + struct tty_port *port = NULL; spin_lock_irqsave(&uport->lock, flags); - if (msm_uport->clk_state == MSM_HS_CLK_OFF) { - /* ignore the first irq - it is a pending irq that occurred + if (msm_uport->clk_state == MSM_HS_CLK_OFF) { + /* ignore the first irq - it is a pending irq that occured * before enable_irq() */ - if (msm_uport->rx_wakeup.ignore) - msm_uport->rx_wakeup.ignore = 0; + if (msm_uport->wakeup.ignore) + msm_uport->wakeup.ignore = 0; else wakeup = 1; } @@ -1357,23 +1810,28 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) if (wakeup) { /* the uart was clocked off during an rx, wake up and * optionally inject char into tty rx */ - msm_hs_request_clock_on_locked(uport); - if (msm_uport->rx_wakeup.inject_rx) { - tty_insert_flip_char(&uport->state->port, - msm_uport->rx_wakeup.rx_to_inject, + spin_unlock_irqrestore(&uport->lock, flags); + msm_hs_request_clock_on(uport); + spin_lock_irqsave(&uport->lock, flags); + if (msm_uport->wakeup.inject_rx) { + tty = uport->state->port.tty; + port = tty->port; + tty_insert_flip_char(port, + msm_uport->wakeup.rx_to_inject, TTY_NORMAL); - queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); } } spin_unlock_irqrestore(&uport->lock, flags); + if (wakeup && msm_uport->wakeup.inject_rx) + tty_flip_buffer_push(port); return IRQ_HANDLED; } static const char *msm_hs_type(struct uart_port *port) { - return (port->type == PORT_MSM) ? "MSM_HS_UART" : NULL; + return ("MSM HS UART"); } /* Called when port is opened */ @@ -1384,9 +1842,13 @@ static int msm_hs_startup(struct uart_port *uport) unsigned long flags; unsigned int data; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct platform_device *pdev = to_platform_device(uport->dev); + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; struct circ_buf *tx_buf = &uport->state->xmit; struct msm_hs_tx *tx = &msm_uport->tx; - struct msm_hs_rx *rx = &msm_uport->rx; + + msm_uport->is_shutdown = false; rfr_level = uport->fifosize; if (rfr_level > 16) @@ -1395,15 +1857,19 @@ static int msm_hs_startup(struct uart_port *uport) tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE, DMA_TO_DEVICE); - /* do not let tty layer execute RX in global workqueue, use a - * dedicated workqueue managed by this driver */ - uport->state->port.low_latency = 1; - /* turn on uart clk */ - ret = msm_hs_init_clk_locked(uport); + ret = msm_hs_init_clk(uport); if (unlikely(ret)) { - printk(KERN_ERR "Turning uartclk failed!\n"); - goto err_msm_hs_init_clk; + pr_err("Turning ON uartclk error\n"); + return ret; + } + + if (pdata && pdata->config_gpio) { + ret = msm_hs_config_uart_gpios(uport); + if (ret) + goto deinit_uart_clk; + } else { + pr_debug("%s(): UART GPIOs not specified.\n", __func__); } /* Set auto RFR Level */ @@ -1425,9 +1891,14 @@ static int msm_hs_startup(struct uart_port *uport) data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK; msm_hs_write(uport, UARTDM_DMEN_ADDR, data); - /* Reset TX */ - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + /* Reset both RX and TX HW state machine */ + msm_hs_write(uport, UARTDM_CR_ADDR, (RESET_RX | RESET_TX)); + /* + * Rx and Tx reset operation takes few clock cycles, hence as + * safe side adding 10us delay. + */ + udelay(10); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_BREAK_INT); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); @@ -1444,7 +1915,6 @@ static int msm_hs_startup(struct uart_port *uport) tx->dma_in_flight = 0; tx->xfer.complete_func = msm_hs_dmov_tx_callback; - tx->xfer.execute_func = NULL; tx->command_ptr->cmd = CMD_LC | CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX; @@ -1457,63 +1927,67 @@ static int msm_hs_startup(struct uart_port *uport) tx->command_ptr->dst_row_addr = msm_uport->uport.mapbase + UARTDM_TF_ADDR; - - /* Turn on Uart Receive */ - rx->xfer.complete_func = msm_hs_dmov_rx_callback; - rx->xfer.execute_func = NULL; - - rx->command_ptr->cmd = CMD_LC | - CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; - - rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) - | (MSM_UARTDM_BURST_SIZE); - rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; - rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR; - - msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK; /* Enable reading the current CTS, no harm even if CTS is ignored */ msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK; msm_hs_write(uport, UARTDM_TFWR_ADDR, 0); /* TXLEV on empty TX fifo */ + /* + * Complete all device write related configuration before + * queuing RX request. Hence mb() requires here. + */ + mb(); + if (use_low_power_wakeup(msm_uport)) { + ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1); + if (unlikely(ret)) { + pr_err("%s():Err setting wakeup irq\n", __func__); + goto unconfigure_uart_gpio; + } + } ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH, "msm_hs_uart", msm_uport); if (unlikely(ret)) { - printk(KERN_ERR "Request msm_hs_uart IRQ failed!\n"); - goto err_request_irq; - } - if (use_low_power_rx_wakeup(msm_uport)) { - ret = request_irq(msm_uport->rx_wakeup.irq, - msm_hs_rx_wakeup_isr, - IRQF_TRIGGER_FALLING, - "msm_hs_rx_wakeup", msm_uport); + pr_err("%s():Error getting uart irq\n", __func__); + goto free_wake_irq; + } + if (use_low_power_wakeup(msm_uport)) { + + ret = request_threaded_irq(msm_uport->wakeup.irq, NULL, + msm_hs_wakeup_isr, + IRQF_TRIGGER_FALLING, + "msm_hs_wakeup", msm_uport); + if (unlikely(ret)) { - printk(KERN_ERR "Request msm_hs_rx_wakeup IRQ failed!\n"); - free_irq(uport->irq, msm_uport); - goto err_request_irq; + pr_err("%s():Err getting uart wakeup_irq\n", __func__); + goto free_uart_irq; } - disable_irq(msm_uport->rx_wakeup.irq); + disable_irq(msm_uport->wakeup.irq); } spin_lock_irqsave(&uport->lock, flags); - msm_hs_write(uport, UARTDM_RFWR_ADDR, 0); msm_hs_start_rx_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); - ret = pm_runtime_set_active(uport->dev); - if (ret) - dev_err(uport->dev, "set active error:%d\n", ret); - pm_runtime_enable(uport->dev); + pm_runtime_enable(uport->dev); + printk("%s: 10\n", __func__); return 0; -err_request_irq: -err_msm_hs_init_clk: - dma_unmap_single(uport->dev, tx->dma_base, - UART_XMIT_SIZE, DMA_TO_DEVICE); +free_uart_irq: + free_irq(uport->irq, msm_uport); +free_wake_irq: + irq_set_irq_wake(msm_uport->wakeup.irq, 0); +unconfigure_uart_gpio: + if (pdata && pdata->config_gpio) + msm_hs_unconfig_uart_gpios(uport); +deinit_uart_clk: + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + return ret; } @@ -1533,7 +2007,7 @@ static int uartdm_init_port(struct uart_port *uport) tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); if (!tx->command_ptr_ptr) { ret = -ENOMEM; - goto err_tx_command_ptr_ptr; + goto free_tx_command_ptr; } tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr, @@ -1544,20 +2018,26 @@ static int uartdm_init_port(struct uart_port *uport) tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr); init_waitqueue_head(&rx->wait); + init_waitqueue_head(&tx->wait); + + tasklet_init(&rx->tlet, msm_serial_hs_rx_tlet, + (unsigned long) &rx->tlet); + tasklet_init(&tx->tlet, msm_serial_hs_tx_tlet, + (unsigned long) &tx->tlet); rx->pool = dma_pool_create("rx_buffer_pool", uport->dev, UARTDM_RX_BUF_SIZE, 16, 0); if (!rx->pool) { pr_err("%s(): cannot allocate rx_buffer_pool", __func__); ret = -ENOMEM; - goto err_dma_pool_create; + goto exit_tasket_init; } rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer); if (!rx->buffer) { pr_err("%s(): cannot allocate rx->buffer", __func__); ret = -ENOMEM; - goto err_dma_pool_alloc; + goto free_pool; } /* Allocate the command pointer. Needs to be 64 bit aligned */ @@ -1565,14 +2045,14 @@ static int uartdm_init_port(struct uart_port *uport) if (!rx->command_ptr) { pr_err("%s(): cannot allocate rx->command_ptr", __func__); ret = -ENOMEM; - goto err_rx_command_ptr; + goto free_rx_buffer; } rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); if (!rx->command_ptr_ptr) { pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__); ret = -ENOMEM; - goto err_rx_command_ptr_ptr; + goto free_rx_command_ptr; } rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) | @@ -1580,6 +2060,19 @@ static int uartdm_init_port(struct uart_port *uport) rx->command_ptr->dst_row_addr = rx->rbuffer; + /* Set up Uart Receive */ + msm_hs_write(uport, UARTDM_RFWR_ADDR, 0); + + rx->xfer.complete_func = msm_hs_dmov_rx_callback; + + rx->command_ptr->cmd = CMD_LC | + CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; + + rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); + rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; + rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR; + rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr, sizeof(dmov_box), DMA_TO_DEVICE); @@ -1589,43 +2082,60 @@ static int uartdm_init_port(struct uart_port *uport) sizeof(u32), DMA_TO_DEVICE); rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr); - INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work); + INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work); return ret; -err_rx_command_ptr_ptr: +free_rx_command_ptr: kfree(rx->command_ptr); -err_rx_command_ptr: + +free_rx_buffer: dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, - msm_uport->rx.rbuffer); -err_dma_pool_alloc: + msm_uport->rx.rbuffer); + +free_pool: dma_pool_destroy(msm_uport->rx.pool); -err_dma_pool_create: + +exit_tasket_init: + tasklet_kill(&msm_uport->tx.tlet); + tasklet_kill(&msm_uport->rx.tlet); dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr, - sizeof(u32), DMA_TO_DEVICE); + sizeof(u32), DMA_TO_DEVICE); dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr, - sizeof(dmov_box), DMA_TO_DEVICE); + sizeof(dmov_box), DMA_TO_DEVICE); kfree(msm_uport->tx.command_ptr_ptr); -err_tx_command_ptr_ptr: + +free_tx_command_ptr: kfree(msm_uport->tx.command_ptr); return ret; } +static atomic_t msm_uart_next_id = ATOMIC_INIT(0); + static int msm_hs_probe(struct platform_device *pdev) { - int ret; + int ret, line, size; struct uart_port *uport; struct msm_hs_port *msm_uport; struct resource *resource; - const struct msm_serial_hs_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data; - if (pdev->id < 0 || pdev->id >= UARTDM_NR) { + if (pdev->dev.of_node) + line = of_alias_get_id(pdev->dev.of_node, "serial"); + else + line = pdev->id; + + if (line < 0) + line = atomic_inc_return(&msm_uart_next_id) - 1; + + if (unlikely(line < 0 || line >= 2)) { printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); - return -EINVAL; + return -ENXIO; } - msm_uport = &q_uart_port[pdev->id]; + dev_info(&pdev->dev, "msm_serial_hs: detected port #%d\n", line); + + msm_uport = &q_uart_port[line]; uport = &msm_uport->uport; uport->dev = &pdev->dev; @@ -1633,64 +2143,143 @@ static int msm_hs_probe(struct platform_device *pdev) resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) return -ENXIO; + uport->mapbase = resource->start; /* virtual address */ - uport->mapbase = resource->start; - uport->irq = platform_get_irq(pdev, 0); - if (unlikely(uport->irq < 0)) - return -ENXIO; + size = resource_size(resource); + if (!request_mem_region(uport->mapbase, size, "msm_serial_hs")) + return -EBUSY; + + uport->membase = ioremap(uport->mapbase, PAGE_SIZE); + if (unlikely(!uport->membase)) + return -ENOMEM; - if (unlikely(irq_set_irq_wake(uport->irq, 1))) + uport->irq = platform_get_irq(pdev, 0); + if (unlikely((int)uport->irq < 0)) return -ENXIO; - if (pdata == NULL || pdata->rx_wakeup_irq < 0) - msm_uport->rx_wakeup.irq = -1; + if (pdata == NULL) + msm_uport->wakeup.irq = -1; else { - msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq; - msm_uport->rx_wakeup.ignore = 1; - msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup; - msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject; + msm_uport->wakeup.irq = pdata->wakeup_irq; + msm_uport->wakeup.ignore = 1; + msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup; + msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject; - if (unlikely(msm_uport->rx_wakeup.irq < 0)) + if (unlikely(msm_uport->wakeup.irq < 0)) return -ENXIO; - if (unlikely(irq_set_irq_wake(msm_uport->rx_wakeup.irq, 1))) - return -ENXIO; } - if (pdata == NULL) - msm_uport->exit_lpm_cb = NULL; - else - msm_uport->exit_lpm_cb = pdata->exit_lpm_cb; - + /* Identify UART functional mode as 2-wire or 4-wire. */ + if (pdata && pdata->config_gpio) { + switch (pdata->config_gpio) { + case 4: + if (gpio_is_valid(pdata->uart_tx_gpio) + && gpio_is_valid(pdata->uart_rx_gpio) + && gpio_is_valid(pdata->uart_cts_gpio) + && gpio_is_valid(pdata->uart_rfr_gpio)) { + msm_uport->func_mode = UART_FOUR_WIRE; + } else { + pr_err("%s(): Wrong GPIO Number for 4-Wire.\n", + __func__); + return -EINVAL; + } + break; + case 2: + if (gpio_is_valid(pdata->uart_tx_gpio) + && gpio_is_valid(pdata->uart_rx_gpio)) { + msm_uport->func_mode = UART_TWO_WIRE; + } else { + pr_err("%s(): Wrong GPIO Number for 2-Wire.\n", + __func__); + return -EINVAL; + } + break; + default: + pr_err("%s(): Invalid number of GPIOs.\n", __func__); + pdata->config_gpio = 0; + return -EINVAL; + } + } +/* resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, "uartdm_channels"); if (unlikely(!resource)) return -ENXIO; +*/ + msm_uport->dma_tx_channel = 7; + msm_uport->dma_rx_channel = 6; - msm_uport->dma_tx_channel = resource->start; - msm_uport->dma_rx_channel = resource->end; - +/* resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, "uartdm_crci"); if (unlikely(!resource)) return -ENXIO; - - msm_uport->dma_tx_crci = resource->start; - msm_uport->dma_rx_crci = resource->end; +*/ + msm_uport->dma_tx_crci = 6; + msm_uport->dma_rx_crci = 11; uport->iotype = UPIO_MEM; - uport->fifosize = UART_FIFOSIZE; + uport->fifosize = 64; uport->ops = &msm_hs_ops; uport->flags = UPF_BOOT_AUTOCONF; - uport->uartclk = UARTCLK; + uport->uartclk = 7372800; msm_uport->imr_reg = 0x0; - msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk"); + + msm_uport->clk = clk_get(&pdev->dev, "core"); if (IS_ERR(msm_uport->clk)) return PTR_ERR(msm_uport->clk); + msm_uport->pclk = clk_get(&pdev->dev, "iface"); + /* + * Some configurations do not require explicit pclk control so + * do not flag error on pclk get failure. + */ + if (IS_ERR(msm_uport->pclk)) + msm_uport->pclk = NULL; + + ret = clk_set_rate(msm_uport->clk, uport->uartclk); + if (ret) { + printk(KERN_WARNING "Error setting clock rate on UART\n"); + return ret; + } + + msm_uport->hsuart_wq = alloc_workqueue("k_hsuart", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!msm_uport->hsuart_wq) { + pr_err("%s(): Unable to create workqueue hsuart_wq\n", + __func__); + return -ENOMEM; + } + + INIT_WORK(&msm_uport->clock_off_w, hsuart_clock_off_work); + mutex_init(&msm_uport->clk_mutex); + + clk_prepare_enable(msm_uport->clk); + if (msm_uport->pclk) + clk_prepare_enable(msm_uport->pclk); + ret = uartdm_init_port(uport); - if (unlikely(ret)) + if (unlikely(ret)) { + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); return ret; + } + + /* configure the CR Protection to Enable */ + msm_hs_write(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN); + + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + + /* + * Enable Command register protection before going ahead as this hw + * configuration makes sure that issued cmd to CR register gets complete + * before next issued cmd start. Hence mb() requires here. + */ + mb(); msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC, @@ -1698,43 +2287,44 @@ static int msm_hs_probe(struct platform_device *pdev) msm_uport->clk_off_timer.function = msm_hs_clk_off_retry; msm_uport->clk_off_delay = ktime_set(0, 1000000); /* 1ms */ - uport->line = pdev->id; + ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr); + if (unlikely(ret)) + return ret; + + msm_serial_debugfs_init(msm_uport, pdev->id); + return uart_add_one_port(&msm_hs_driver, uport); } static int __init msm_serial_hs_init(void) { - int ret, i; + int ret; + int i; /* Init all UARTS as non-configured */ for (i = 0; i < UARTDM_NR; i++) q_uart_port[i].uport.type = PORT_UNKNOWN; - msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs"); - if (unlikely(!msm_hs_workqueue)) - return -ENOMEM; - ret = uart_register_driver(&msm_hs_driver); if (unlikely(ret)) { - printk(KERN_ERR "%s failed to load\n", __func__); - goto err_uart_register_driver; + printk(KERN_ERR "%s failed to load\n", __FUNCTION__); + return ret; } + debug_base = debugfs_create_dir("msm_serial_hs", NULL); + if (IS_ERR_OR_NULL(debug_base)) + pr_info("msm_serial_hs: Cannot create debugfs dir\n"); ret = platform_driver_register(&msm_serial_hs_platform_driver); if (ret) { - printk(KERN_ERR "%s failed to load\n", __func__); - goto err_platform_driver_register; + printk(KERN_ERR "%s failed to load\n", __FUNCTION__); + debugfs_remove_recursive(debug_base); + uart_unregister_driver(&msm_hs_driver); + return ret; } - return ret; - -err_platform_driver_register: - uart_unregister_driver(&msm_hs_driver); -err_uart_register_driver: - destroy_workqueue(msm_hs_workqueue); + printk(KERN_INFO "msm_serial_hs module loaded\n"); return ret; } -module_init(msm_serial_hs_init); /* * Called by the upper layer when port is closed. @@ -1743,56 +2333,88 @@ module_init(msm_serial_hs_init); */ static void msm_hs_shutdown(struct uart_port *uport) { + int ret; + unsigned int data; unsigned long flags; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct platform_device *pdev = to_platform_device(uport->dev); + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; - BUG_ON(msm_uport->rx.flush < FLUSH_STOP); spin_lock_irqsave(&uport->lock, flags); - clk_enable(msm_uport->clk); + /* Disable all UART interrupts */ + msm_uport->imr_reg = 0; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_uport->is_shutdown = true; + /* disable UART TX interface to DM */ + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data &= ~UARTDM_TX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + mb(); + + if (msm_uport->tx.dma_in_flight) { + msm_uport->tx.flush = FLUSH_STOP; + /* discard flush */ + msm_dmov_flush(msm_uport->dma_tx_channel, 0); + spin_unlock_irqrestore(&uport->lock, flags); + ret = wait_event_timeout(msm_uport->tx.wait, + msm_uport->tx.flush == FLUSH_SHUTDOWN, 100); + if (!ret) + pr_err("%s():HSUART TX Stalls.\n", __func__); + } else { + spin_unlock_irqrestore(&uport->lock, flags); + } + + tasklet_kill(&msm_uport->tx.tlet); + BUG_ON(msm_uport->rx.flush < FLUSH_STOP); + wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); + tasklet_kill(&msm_uport->rx.tlet); + cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work); + flush_workqueue(msm_uport->hsuart_wq); + pm_runtime_disable(uport->dev); /* Disable the transmitter */ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); /* Disable the receiver */ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); - pm_runtime_disable(uport->dev); - pm_runtime_set_suspended(uport->dev); - - /* Free the interrupt */ - free_irq(uport->irq, msm_uport); - if (use_low_power_rx_wakeup(msm_uport)) - free_irq(msm_uport->rx_wakeup.irq, msm_uport); - - msm_uport->imr_reg = 0; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); + /* + * Complete all device write before actually disabling uartclk. + * Hence mb() requires here. + */ + mb(); + if (msm_uport->clk_state != MSM_HS_CLK_OFF) { + /* to balance clk_state */ + clk_disable_unprepare(msm_uport->clk); + if (msm_uport->pclk) + clk_disable_unprepare(msm_uport->pclk); + } - clk_disable(msm_uport->clk); /* to balance local clk_enable() */ - if (msm_uport->clk_state != MSM_HS_CLK_OFF) - clk_disable(msm_uport->clk); /* to balance clk_state */ msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; - dma_unmap_single(uport->dev, msm_uport->tx.dma_base, UART_XMIT_SIZE, DMA_TO_DEVICE); - spin_unlock_irqrestore(&uport->lock, flags); + if (use_low_power_wakeup(msm_uport)) + irq_set_irq_wake(msm_uport->wakeup.irq, 0); - if (cancel_work_sync(&msm_uport->rx.tty_work)) - msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work); + /* Free the interrupt */ + free_irq(uport->irq, msm_uport); + if (use_low_power_wakeup(msm_uport)) + free_irq(msm_uport->wakeup.irq, msm_uport); + + if (pdata && pdata->config_gpio) + msm_hs_unconfig_uart_gpios(uport); } static void __exit msm_serial_hs_exit(void) { - flush_workqueue(msm_hs_workqueue); - destroy_workqueue(msm_hs_workqueue); + printk(KERN_INFO "msm_serial_hs module removed\n"); + debugfs_remove_recursive(debug_base); platform_driver_unregister(&msm_serial_hs_platform_driver); uart_unregister_driver(&msm_hs_driver); } -module_exit(msm_serial_hs_exit); -#ifdef CONFIG_PM_RUNTIME static int msm_hs_runtime_idle(struct device *dev) { /* @@ -1807,7 +2429,6 @@ static int msm_hs_runtime_resume(struct device *dev) struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; - msm_hs_request_clock_on(&msm_uport->uport); return 0; } @@ -1817,15 +2438,9 @@ static int msm_hs_runtime_suspend(struct device *dev) struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; - msm_hs_request_clock_off(&msm_uport->uport); return 0; } -#else -#define msm_hs_runtime_idle NULL -#define msm_hs_runtime_resume NULL -#define msm_hs_runtime_suspend NULL -#endif static const struct dev_pm_ops msm_hs_dev_pm_ops = { .runtime_suspend = msm_hs_runtime_suspend, @@ -1833,12 +2448,18 @@ static const struct dev_pm_ops msm_hs_dev_pm_ops = { .runtime_idle = msm_hs_runtime_idle, }; +static const struct of_device_id msm_match_table[] = { + { .compatible = "qcom,msm-uart" }, + { .compatible = "qcom,msm-uartdm-hs" }, + {} +}; + static struct platform_driver msm_serial_hs_platform_driver = { - .probe = msm_hs_probe, + .probe = msm_hs_probe, .remove = msm_hs_remove, .driver = { .name = "msm_serial_hs", - .owner = THIS_MODULE, + .of_match_table = msm_match_table, .pm = &msm_hs_dev_pm_ops, }, }; @@ -1863,13 +2484,15 @@ static struct uart_ops msm_hs_ops = { .startup = msm_hs_startup, .shutdown = msm_hs_shutdown, .set_termios = msm_hs_set_termios, - .pm = msm_hs_pm, .type = msm_hs_type, .config_port = msm_hs_config_port, .release_port = msm_hs_release_port, .request_port = msm_hs_request_port, + .flush_buffer = msm_hs_flush_buffer_locked, }; +module_init(msm_serial_hs_init); +module_exit(msm_serial_hs_exit); MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset"); MODULE_VERSION("1.2"); MODULE_LICENSE("GPL v2"); |