On 12/17/2012 06:10 AM, Laxman Dewangan wrote: > Nvidia's Tegra has multiple uart controller which supports: > - APB dma based controller fifo read/write. > - End Of Data interrupt in incoming data to know whether end > of frame achieve or not. > - Hw controlled RTS and CTS flow control to reduce SW overhead. > > Add serial driver to use all above feature. > > Signed-off-by: Laxman Dewangan <ldewangan@xxxxxxxxxx> > --- > .../bindings/serial/nvidia,serial-tegra.txt | 26 + Can we have a name without a comma. If serial-tegra is specific enough for the kernel, then it is for the binding doc too. > drivers/tty/serial/Kconfig | 14 + > drivers/tty/serial/Makefile | 1 + > drivers/tty/serial/serial_tegra.c | 1398 ++++++++++++++++++++ > include/linux/serial_tegra.h | 33 + > 5 files changed, 1472 insertions(+), 0 deletions(-) > create mode 100644 Documentation/devicetree/bindings/serial/nvidia,serial-tegra.txt > create mode 100644 drivers/tty/serial/serial_tegra.c > create mode 100644 include/linux/serial_tegra.h > > diff --git a/Documentation/devicetree/bindings/serial/nvidia,serial-tegra.txt b/Documentation/devicetree/bindings/serial/nvidia,serial-tegra.txt > new file mode 100644 > index 0000000..fc5803b > --- /dev/null > +++ b/Documentation/devicetree/bindings/serial/nvidia,serial-tegra.txt > @@ -0,0 +1,26 @@ > +NVIDIA Tegra20/Tegra30 high speed (dma based) UART controller driver. > + > +Required properties: > +- compatible : should be "nvidia,tegra20-hsuart", "nvidia,tegra30-hsuart". > +- reg: Should contain UART controller registers location and length. > +- interrupts: Should contain UART controller interrupts. > +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and > + request selector for this UART controller. This should use the generic DMA binding although I'm not sure if that made it into 3.8. > +- port-number: Uart port number for /dev/ttyHSx where x is port number. Use aliases like other platforms like i.MX have done. > +Optional properties: > +- nvidia,enable-modem-interrupt: Enable modem interrupts. Should be enable s/enable/enabled/ on the last one. > + only if all 8 lines of uart controller is pinmuxed. s/is/are/ > + > +Example: > + > +serial@70006000 { > + compatible = "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart"; > + reg = <0x70006000 0x40>; > + reg-shift = <2>; > + interrupts = <0 36 0x04>; > + port-number = <0>; > + nvidia,dma-request-selector = <&apbdma 8>; > + nvidia,enable-modem-interrupt; > + status = "disabled"; > +}; > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > index 59c23d0..57dbbc1 100644 > --- a/drivers/tty/serial/Kconfig > +++ b/drivers/tty/serial/Kconfig > @@ -269,6 +269,20 @@ config SERIAL_SIRFSOC_CONSOLE > your boot loader about how to pass options to the kernel at > boot time.) > > +config SERIAL_SAMSUNG_UARTS_4 > + bool Huh? > + > +config SERIAL_TEGRA > + tristate "Nvidia Tegra20/30 SoC serial controller" > + depends on ARCH_TEGRA && TEGRA20_APB_DMA > + select SERIAL_CORE > + help > + Support for the on-chip UARTs on the Nvidia Tegra seria SOCs > + providing /dev/ttyHS0, 1, 2, 3 and 4 (note, some machines may not > + provide all of these ports, depending on how the serial port > + are enabled). This driver uses the APB dma to achieve higher baudrate > + and better performance. > + > config SERIAL_MAX3100 > tristate "MAX3100 support" > depends on SPI > diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile > index df1b998..73fb688 100644 > --- a/drivers/tty/serial/Makefile > +++ b/drivers/tty/serial/Makefile > @@ -80,6 +80,7 @@ obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o > obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o > obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o > obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o > +obj-$(CONFIG_SERIAL_TEGRA) += serial_tegra.o > obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o > obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o > obj-$(CONFIG_SERIAL_ARC) += arc_uart.o > diff --git a/drivers/tty/serial/serial_tegra.c b/drivers/tty/serial/serial_tegra.c > new file mode 100644 > index 0000000..791cf35 > --- /dev/null > +++ b/drivers/tty/serial/serial_tegra.c > @@ -0,0 +1,1398 @@ > +/* > + * serial_tegra.c > + * > + * High-speed serial driver for NVIDIA Tegra SoCs > + * > + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. > + * > + * Author: Laxman Dewangan <ldewangan@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/clk.h> > +#include <linux/debugfs.h> > +#include <linux/delay.h> > +#include <linux/dmaengine.h> > +#include <linux/dma-mapping.h> > +#include <linux/dmapool.h> > +#include <linux/io.h> > +#include <linux/irq.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/pagemap.h> > +#include <linux/platform_device.h> > +#include <linux/serial.h> > +#include <linux/serial_8250.h> > +#include <linux/serial_core.h> > +#include <linux/serial_reg.h> > +#include <linux/slab.h> > +#include <linux/string.h> > +#include <linux/serial_tegra.h> > +#include <linux/termios.h> > +#include <linux/tty_flip.h> > + > +#include <mach/clk.h> > + > +#define TEGRA_UART_TYPE "TEGRA_UART" > +#define TX_EMPTY_STATUS (UART_LSR_TEMT | UART_LSR_THRE) > +#define BYTES_TO_ALIGN(x) ((unsigned long)(x) & 0x3) > + > +#define TEGRA_UART_RX_DMA_BUFFER_SIZE 4096 > +#define TEGRA_UART_LSR_TXFIFO_FULL 0x100 > +#define TEGRA_UART_IER_EORD 0x20 > +#define TEGRA_UART_MCR_RTS_EN 0x40 > +#define TEGRA_UART_MCR_CTS_EN 0x20 > +#define TEGRA_UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \ > + UART_LSR_PE | UART_LSR_FE) > +#define TEGRA_UART_IRDA_CSR 0x08 > +#define TEGRA_UART_SIR_ENABLED 0x80 > + > +#define TEGRA_UART_TX_PIO 1 > +#define TEGRA_UART_TX_DMA 2 > +#define TEGRA_UART_MIN_DMA 16 > +#define TEGRA_UART_FIFO_SIZE 32 > + > +/* > + * Tx fifo trigger level setting in tegra uart is in > + * reverse way then conventional uart. > + */ > +#define TEGRA_UART_TX_TRIG_16B 0x00 > +#define TEGRA_UART_TX_TRIG_8B 0x10 > +#define TEGRA_UART_TX_TRIG_4B 0x20 > +#define TEGRA_UART_TX_TRIG_1B 0x30 > + > +#define TEGRA_UART_MAXIMUM 5 > + > +/* Default UART setting when started: 115200 no parity, stop, 8 data bits */ > +#define TEGRA_UART_DEFAULT_BAUD 115200 > +#define TEGRA_UART_DEFAULT_LSR UART_LCR_WLEN8 > + > +/* Tx transfer mode */ > +#define TEGRA_TX_PIO 1 > +#define TEGRA_TX_DMA 2 > + > +/** > + * tegra_uart_chip_data: SOC specific data. > + * > + * @tx_fifo_full_status: Status flag available for checking tx fifo full. > + * @allow_txfifo_reset_fifo_mode: allow_tx fifo reset with fifo mode or not. > + * Tegra30 does not allow this. > + * @support_clk_src_div: Clock source support the clock divider. > + */ > +struct tegra_uart_chip_data { > + bool tx_fifo_full_status; > + bool allow_txfifo_reset_fifo_mode; > + bool support_clk_src_div; > +}; > + > +struct tegra_uart_chip_data tegra20_uart_chip_data = { > + .tx_fifo_full_status = false, > + .allow_txfifo_reset_fifo_mode = true, > + .support_clk_src_div = false, > +}; > + > +struct tegra_uart_chip_data tegra30_uart_chip_data = { > + .tx_fifo_full_status = true, > + .allow_txfifo_reset_fifo_mode = false, > + .support_clk_src_div = true, > +}; > + > +struct tegra_uart_port { > + struct uart_port uport; > + const struct tegra_uart_chip_data *cdata; > + > + struct clk *uart_clk; > + unsigned int current_baud; > + > + /* Register shadow */ > + unsigned long fcr_shadow; > + unsigned long mcr_shadow; > + unsigned long lcr_shadow; > + unsigned long ier_shadow; > + bool rts_active; > + > + int tx_in_progress; > + unsigned int tx_bytes; > + > + int enable_modem_interrupt; > + > + bool rx_timeout; > + int rx_in_progress; > + int symb_bit; > + int dma_req_sel; > + > + struct dma_chan *rx_dma_chan; > + struct dma_chan *tx_dma_chan; > + dma_addr_t rx_dma_buf_phys; > + dma_addr_t tx_dma_buf_phys; > + unsigned char *rx_dma_buf_virt; > + unsigned char *tx_dma_buf_virt; > + struct dma_async_tx_descriptor *tx_dma_desc; > + struct dma_async_tx_descriptor *rx_dma_desc; > + dma_cookie_t tx_cookie; > + dma_cookie_t rx_cookie; > + int tx_bytes_requested; > + int rx_bytes_requested; > +}; > + > +static void tegra_uart_start_next_tx(struct tegra_uart_port *tup); > +static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup); > + > +static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup, > + unsigned long reg) > +{ > + return readl(tup->uport.membase + (reg << tup->uport.regshift)); > +} > + > +static inline void tegra_uart_write(struct tegra_uart_port *tup, unsigned val, > + unsigned long reg) > +{ > + writel(val, tup->uport.membase + (reg << tup->uport.regshift)); > +} > + > +static inline struct tegra_uart_port *to_tegra_uport(struct uart_port *u) > +{ > + return container_of(u, struct tegra_uart_port, uport); > +} > + > +static unsigned int tegra_uart_get_mctrl(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + > + /* > + * RI - Ring detector is active > + * CD/DCD/CAR - Carrier detect is always active. For some reason > + * linux has different names for carrier detect. > + * DSR - Data Set ready is active as the hardware doesn't support it. > + * Don't know if the linux support this yet? > + * CTS - Clear to send. Always set to active, as the hardware handles > + * CTS automatically. > + */ > + if (tup->enable_modem_interrupt) > + return TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS; > + return TIOCM_CTS; > +} > + > +static void set_rts(struct tegra_uart_port *tup, bool active) > +{ > + unsigned long mcr; > + > + mcr = tup->mcr_shadow; > + if (active) > + mcr |= TEGRA_UART_MCR_RTS_EN; > + else > + mcr &= ~TEGRA_UART_MCR_RTS_EN; > + if (mcr != tup->mcr_shadow) { > + tegra_uart_write(tup, mcr, UART_MCR); > + tup->mcr_shadow = mcr; > + } > + return; > +} > + > +static void set_dtr(struct tegra_uart_port *tup, bool active) > +{ > + unsigned long mcr; > + > + mcr = tup->mcr_shadow; > + if (active) > + mcr |= UART_MCR_DTR; > + else > + mcr &= ~UART_MCR_DTR; > + if (mcr != tup->mcr_shadow) { > + tegra_uart_write(tup, mcr, UART_MCR); > + tup->mcr_shadow = mcr; > + } > + return; > +} > + > +static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + unsigned long mcr; > + > + mcr = tup->mcr_shadow; > + if (mctrl & TIOCM_RTS) { > + tup->rts_active = true; > + set_rts(tup, true); > + } else { > + tup->rts_active = false; > + set_rts(tup, false); > + } > + > + if (mctrl & TIOCM_DTR) > + set_dtr(tup, true); > + else > + set_dtr(tup, false); > + return; > +} > + > +static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + unsigned long lcr; > + > + lcr = tup->lcr_shadow; > + if (break_ctl) > + lcr |= UART_LCR_SBC; > + else > + lcr &= ~UART_LCR_SBC; > + tegra_uart_write(tup, lcr, UART_LCR); > + tup->lcr_shadow = lcr; > +} > + > +/* Wait for a symbol-time. */ > +static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup, > + unsigned int syms) > +{ > + if (tup->current_baud) > + udelay(DIV_ROUND_UP(syms * tup->symb_bit * 1000000, > + tup->current_baud)); > +} > + > +static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) > +{ > + unsigned long fcr = tup->fcr_shadow; > + > + if (tup->cdata->allow_txfifo_reset_fifo_mode) { > + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); > + tegra_uart_write(tup, fcr, UART_FCR); > + } else { > + fcr &= ~UART_FCR_ENABLE_FIFO; > + tegra_uart_write(tup, fcr, UART_FCR); > + udelay(60); > + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); > + tegra_uart_write(tup, fcr, UART_FCR); > + fcr |= UART_FCR_ENABLE_FIFO; > + tegra_uart_write(tup, fcr, UART_FCR); > + } > + > + /* Dummy read to ensure the write is posted */ > + tegra_uart_read(tup, UART_SCR); > + > + /* Wait for the flush to propagate. */ > + tegra_uart_wait_sym_time(tup, 1); > +} > + > +static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) > +{ > + unsigned long rate; > + unsigned int divisor; > + unsigned long lcr; > + int ret; > + > + if (tup->current_baud == baud) > + return 0; > + > + if (tup->cdata->support_clk_src_div) { > + rate = baud * 16; > + ret = clk_set_rate(tup->uart_clk, rate); > + if (ret < 0) { > + dev_err(tup->uport.dev, > + "clk_set_rate() failed for rate %lu\n", rate); > + return ret; > + } > + divisor = 1; > + } else { > + rate = clk_get_rate(tup->uart_clk); > + divisor = DIV_ROUND_CLOSEST(rate, baud * 16); > + } > + > + lcr = tup->lcr_shadow; > + lcr |= UART_LCR_DLAB; > + tegra_uart_write(tup, lcr, UART_LCR); > + > + tegra_uart_write(tup, divisor & 0xFF, UART_TX); > + tegra_uart_write(tup, ((divisor >> 8) & 0xFF), UART_IER); > + > + lcr &= ~UART_LCR_DLAB; > + tegra_uart_write(tup, lcr, UART_LCR); > + > + /* Dummy read to ensure the write is posted */ > + tegra_uart_read(tup, UART_SCR); > + > + tup->current_baud = baud; > + > + /* wait two character intervals at new rate */ > + tegra_uart_wait_sym_time(tup, 2); > + return 0; > +} > + > +static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup, > + unsigned long lsr) > +{ > + char flag = TTY_NORMAL; > + > + if (unlikely(lsr & TEGRA_UART_LSR_ANY)) { > + if (lsr & UART_LSR_OE) { > + /* Overrrun error */ > + flag |= TTY_OVERRUN; > + tup->uport.icount.overrun++; > + dev_err(tup->uport.dev, "Got overrun errors\n"); > + } else if (lsr & UART_LSR_PE) { > + /* Parity error */ > + flag |= TTY_PARITY; > + tup->uport.icount.parity++; > + dev_err(tup->uport.dev, "Got Parity errors\n"); > + } else if (lsr & UART_LSR_FE) { > + flag |= TTY_FRAME; > + tup->uport.icount.frame++; > + dev_err(tup->uport.dev, "Got frame errors\n"); > + } else if (lsr & UART_LSR_BI) { > + dev_err(tup->uport.dev, "Got Break\n"); > + tup->uport.icount.brk++; > + /* If FIFO read error without any data, reset Rx FIFO */ > + if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) > + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR); > + } > + } > + return flag; > +} > + > +static int tegra_uart_request_port(struct uart_port *u) > +{ > + return 0; > +} > + > +static void tegra_uart_release_port(struct uart_port *u) > +{ > + /* Nothing to do here */ > +} > + > +static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes) > +{ > + struct circ_buf *xmit = &tup->uport.state->xmit; > + int i; > + > + for (i = 0; i < max_bytes; i++) { > + BUG_ON(uart_circ_empty(xmit)); > + if (tup->cdata->tx_fifo_full_status) { > + unsigned long lsr = tegra_uart_read(tup, UART_LSR); > + if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL)) > + break; > + } > + tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX); > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); > + tup->uport.icount.tx++; > + } > +} > + > +static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup, > + unsigned int bytes) > +{ > + if (bytes > TEGRA_UART_MIN_DMA) > + bytes = TEGRA_UART_MIN_DMA; > + > + tup->tx_in_progress = TEGRA_UART_TX_PIO; > + tup->tx_bytes = bytes; > + tup->ier_shadow |= UART_IER_THRI; > + tegra_uart_write(tup, tup->ier_shadow, UART_IER); > +} > + > +static void tegra_uart_tx_dma_complete(void *args) > +{ > + struct tegra_uart_port *tup = args; > + struct circ_buf *xmit = &tup->uport.state->xmit; > + struct dma_tx_state state; > + unsigned long flags; > + int count; > + > + dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state); > + count = tup->tx_bytes_requested - state.residue; > + async_tx_ack(tup->tx_dma_desc); > + spin_lock_irqsave(&tup->uport.lock, flags); > + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); > + tup->tx_in_progress = 0; > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(&tup->uport); > + tegra_uart_start_next_tx(tup); > + spin_unlock_irqrestore(&tup->uport.lock, flags); > +} > + > +static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, > + unsigned long count) > +{ > + struct circ_buf *xmit = &tup->uport.state->xmit; > + dma_addr_t tx_phys_addr; > + > + dma_sync_single_for_device(tup->uport.dev, tup->tx_dma_buf_phys, > + UART_XMIT_SIZE, DMA_TO_DEVICE); > + > + tup->tx_bytes = count & ~(0xF); > + tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail; > + tup->tx_dma_desc = dmaengine_prep_slave_single(tup->tx_dma_chan, > + tx_phys_addr, tup->tx_bytes, DMA_MEM_TO_DEV, > + DMA_PREP_INTERRUPT); > + if (!tup->tx_dma_desc) { > + dev_err(tup->uport.dev, "Not able to get desc for Tx\n"); > + return -EIO; > + } > + > + tup->tx_dma_desc->callback = tegra_uart_tx_dma_complete; > + tup->tx_dma_desc->callback_param = tup; > + tup->tx_in_progress = TEGRA_UART_TX_DMA; > + tup->tx_bytes_requested = tup->tx_bytes; > + tup->tx_cookie = dmaengine_submit(tup->tx_dma_desc); > + dma_async_issue_pending(tup->tx_dma_chan); > + return 0; > +} > + > +static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) > +{ > + unsigned long tail; > + unsigned long count; > + struct circ_buf *xmit = &tup->uport.state->xmit; > + > + tail = (unsigned long)&xmit->buf[xmit->tail]; > + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); > + if (!count) > + return; > + > + if (count < TEGRA_UART_MIN_DMA) > + tegra_uart_start_pio_tx(tup, count); > + else if (BYTES_TO_ALIGN(tail) > 0) > + tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail)); > + else > + tegra_uart_start_tx_dma(tup, count); > +} > + > +/* Called by serial core driver with u->lock taken. */ > +static void tegra_uart_start_tx(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + struct circ_buf *xmit = &u->state->xmit; > + > + if (!uart_circ_empty(xmit) && !tup->tx_in_progress) > + tegra_uart_start_next_tx(tup); > +} > + > +static unsigned int tegra_uart_tx_empty(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + unsigned int ret = 0; > + unsigned long flags; > + > + spin_lock_irqsave(&u->lock, flags); > + if (!tup->tx_in_progress) { > + unsigned long lsr = tegra_uart_read(tup, UART_LSR); > + if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) > + ret = TIOCSER_TEMT; > + } > + spin_unlock_irqrestore(&u->lock, flags); > + return ret; > +} > + > +static void tegra_uart_stop_tx(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + struct circ_buf *xmit = &tup->uport.state->xmit; > + struct dma_tx_state state; > + int count; > + > + dmaengine_terminate_all(tup->tx_dma_chan); > + dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state); > + count = tup->tx_bytes_requested - state.residue; > + async_tx_ack(tup->tx_dma_desc); > + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); > + tup->tx_in_progress = 0; > + return; > +} > + > +static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) > +{ > + struct circ_buf *xmit = &tup->uport.state->xmit; > + > + tegra_uart_fill_tx_fifo(tup, tup->tx_bytes); > + tup->tx_in_progress = 0; > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(&tup->uport); > + tegra_uart_start_next_tx(tup); > + return; > +} > + > +static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup) > +{ > + do { > + char flag = TTY_NORMAL; > + unsigned long lsr = 0; > + unsigned char ch; > + > + lsr = tegra_uart_read(tup, UART_LSR); > + if (!(lsr & UART_LSR_DR)) > + break; > + > + flag = tegra_uart_decode_rx_error(tup, lsr); > + ch = (unsigned char) tegra_uart_read(tup, UART_RX); > + tup->uport.icount.rx++; > + > + if (!uart_handle_sysrq_char(&tup->uport, ch)) > + uart_insert_char(&tup->uport, lsr, > + UART_LSR_OE, ch, flag); > + } while (1); > + > + return; > +} > + > +static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, int count) > +{ > + int copied; > + > + tup->uport.icount.rx += count; > + dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys, > + TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); > + copied = tty_insert_flip_string(tup->uport.state->port.tty, > + ((unsigned char *)(tup->rx_dma_buf_virt)), count); > + if (copied != count) { > + WARN_ON(1); > + dev_err(tup->uport.dev, "RxData copy to tty layer failed\n"); > + } > + dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys, > + TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); > +} > + > +static void tegra_uart_rx_dma_complete(void *args) > +{ > + struct tegra_uart_port *tup = args; > + struct uart_port *u = &tup->uport; > + int count = tup->rx_bytes_requested; > + unsigned long flags; > + > + async_tx_ack(tup->rx_dma_desc); > + spin_lock_irqsave(&u->lock, flags); > + > + /* Deactivate flow control to stop sender */ > + if (tup->rts_active) > + set_rts(tup, false); > + > + /* If we are here, DMA is stopped */ > + if (count) > + tegra_uart_copy_rx_to_tty(tup, count); > + > + tegra_uart_handle_rx_pio(tup); > + tty_flip_buffer_push(u->state->port.tty); > + tegra_uart_start_rx_dma(tup); > + > + /* Activate flow control to start transfer */ > + if (tup->rts_active) > + set_rts(tup, true); > + > + spin_unlock_irqrestore(&u->lock, flags); > +} > + > +static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup) > +{ > + struct uart_port *u = &tup->uport; > + struct dma_tx_state state; > + int count; > + > + /* Deactivate flow control to stop sender */ > + if (tup->rts_active) > + set_rts(tup, false); > + > + dmaengine_terminate_all(tup->rx_dma_chan); > + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); > + count = tup->rx_bytes_requested - state.residue; > + > + /* If we are here, DMA is stopped */ > + if (count) > + tegra_uart_copy_rx_to_tty(tup, count); > + > + tegra_uart_handle_rx_pio(tup); > + tty_flip_buffer_push(u->state->port.tty); > + tegra_uart_start_rx_dma(tup); > + > + if (tup->rts_active) > + set_rts(tup, true); > +} > + > +static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup) > +{ > + unsigned int count = TEGRA_UART_RX_DMA_BUFFER_SIZE; > + > + tup->rx_dma_desc = dmaengine_prep_slave_single(tup->rx_dma_chan, > + tup->rx_dma_buf_phys, count, DMA_DEV_TO_MEM, > + DMA_PREP_INTERRUPT); > + if (!tup->rx_dma_desc) { > + dev_err(tup->uport.dev, "Not able to get desc for Rx\n"); > + return -EIO; > + } > + > + tup->rx_dma_desc->callback = tegra_uart_rx_dma_complete; > + tup->rx_dma_desc->callback_param = tup; > + dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys, > + count, DMA_TO_DEVICE); > + tup->rx_bytes_requested = count; > + tup->rx_cookie = dmaengine_submit(tup->rx_dma_desc); > + dma_async_issue_pending(tup->rx_dma_chan); > + return 0; > +} > + > +static void tegra_uart_handle_modem_signal_change(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + unsigned long msr; > + > + msr = tegra_uart_read(tup, UART_MSR); > + if (!(msr & UART_MSR_ANY_DELTA)) > + return; > + > + if (msr & UART_MSR_TERI) > + tup->uport.icount.rng++; > + if (msr & UART_MSR_DDSR) > + tup->uport.icount.dsr++; > + /* We may only get DDCD when HW init and reset */ > + if (msr & UART_MSR_DDCD) > + uart_handle_dcd_change(&tup->uport, msr & UART_MSR_DCD); > + /* Will start/stop_tx accordingly */ > + if (msr & UART_MSR_DCTS) > + uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS); > + return; > +} > + > +static irqreturn_t tegra_uart_isr(int irq, void *data) > +{ > + struct tegra_uart_port *tup = data; > + struct uart_port *u = &tup->uport; > + unsigned long iir; > + unsigned long ier; > + bool is_rx_int = false; > + unsigned long flags; > + > + spin_lock_irqsave(&u->lock, flags); > + while (1) { > + iir = tegra_uart_read(tup, UART_IIR); > + if (iir & UART_IIR_NO_INT) { > + if (is_rx_int) { > + tegra_uart_handle_rx_dma(tup); > + if (tup->rx_in_progress) { > + ier = tup->ier_shadow; > + ier |= (UART_IER_RLSI | UART_IER_RTOIE | > + TEGRA_UART_IER_EORD); > + tup->ier_shadow = ier; > + tegra_uart_write(tup, ier, UART_IER); > + } > + } > + spin_unlock_irqrestore(&u->lock, flags); > + return IRQ_HANDLED; > + } > + > + switch ((iir >> 1) & 0x7) { > + case 0: /* Modem signal change interrupt */ > + tegra_uart_handle_modem_signal_change(u); > + break; > + > + case 1: /* Transmit interrupt only triggered when using PIO */ > + tup->ier_shadow &= ~UART_IER_THRI; > + tegra_uart_write(tup, tup->ier_shadow, UART_IER); > + tegra_uart_handle_tx_pio(tup); > + break; > + > + case 4: /* End of data */ > + case 6: /* Rx timeout */ > + case 2: /* Receive */ > + if (!is_rx_int) { > + is_rx_int = true; > + /* Disable Rx interrupts */ > + ier = tup->ier_shadow; > + ier |= UART_IER_RDI; > + tegra_uart_write(tup, ier, UART_IER); > + ier &= ~(UART_IER_RDI | UART_IER_RLSI | > + UART_IER_RTOIE | TEGRA_UART_IER_EORD); > + tup->ier_shadow = ier; > + tegra_uart_write(tup, ier, UART_IER); > + } > + break; > + > + case 3: /* Receive error */ > + tegra_uart_decode_rx_error(tup, > + tegra_uart_read(tup, UART_LSR)); > + break; > + > + case 5: /* break nothing to handle */ > + case 7: /* break nothing to handle */ > + break; > + } > + } > +} > + > +static void tegra_uart_stop_rx(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + struct dma_tx_state state; > + unsigned long ier; > + int count; > + > + if (tup->rts_active) > + set_rts(tup, false); > + > + if (!tup->rx_in_progress) > + return; > + > + tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ > + > + ier = tup->ier_shadow; > + ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | > + TEGRA_UART_IER_EORD); > + tup->ier_shadow = ier; > + tegra_uart_write(tup, ier, UART_IER); > + tup->rx_in_progress = 0; > + if (tup->rx_dma_chan) { > + dmaengine_terminate_all(tup->rx_dma_chan); > + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); > + async_tx_ack(tup->rx_dma_desc); > + count = tup->rx_bytes_requested - state.residue; > + tegra_uart_copy_rx_to_tty(tup, count); > + tegra_uart_handle_rx_pio(tup); > + } else { > + tegra_uart_handle_rx_pio(tup); > + } > + tty_flip_buffer_push(u->state->port.tty); > + return; > +} > + > +static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) > +{ > + unsigned long flags; > + unsigned long char_time = DIV_ROUND_UP(10000000, tup->current_baud); > + unsigned long fifo_empty_time = tup->uport.fifosize * char_time; > + unsigned long wait_time; > + unsigned long lsr; > + unsigned long msr; > + unsigned long mcr; > + > + /* Disable interrupts */ > + tegra_uart_write(tup, 0, UART_IER); > + > + lsr = tegra_uart_read(tup, UART_LSR); > + if ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) { > + msr = tegra_uart_read(tup, UART_MSR); > + mcr = tegra_uart_read(tup, UART_MCR); > + if ((mcr & TEGRA_UART_MCR_CTS_EN) && (msr & UART_MSR_CTS)) > + dev_err(tup->uport.dev, > + "Tx Fifo not empty, CTS disabled, waiting\n"); > + > + /* Wait for Tx fifo to be empty */ > + while ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) { > + wait_time = min(fifo_empty_time, 100lu); > + udelay(wait_time); > + fifo_empty_time -= wait_time; > + if (!fifo_empty_time) { > + msr = tegra_uart_read(tup, UART_MSR); > + mcr = tegra_uart_read(tup, UART_MCR); > + if ((mcr & TEGRA_UART_MCR_CTS_EN) && > + (msr & UART_MSR_CTS)) > + dev_err(tup->uport.dev, > + "Slave not ready\n"); > + break; > + } > + lsr = tegra_uart_read(tup, UART_LSR); > + } > + } > + > + spin_lock_irqsave(&tup->uport.lock, flags); > + /* Reset the Rx and Tx FIFOs */ > + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); > + tup->current_baud = 0; > + spin_unlock_irqrestore(&tup->uport.lock, flags); > + > + clk_disable_unprepare(tup->uart_clk); > +} > + > +static int tegra_uart_hw_init(struct tegra_uart_port *tup) > +{ > + int ret; > + > + tup->fcr_shadow = 0; > + tup->mcr_shadow = 0; > + tup->lcr_shadow = 0; > + tup->ier_shadow = 0; > + tup->current_baud = 0; > + > + clk_prepare_enable(tup->uart_clk); > + > + /* Reset the UART controller to clear all previous status.*/ > + tegra_periph_reset_assert(tup->uart_clk); > + udelay(10); > + tegra_periph_reset_deassert(tup->uart_clk); > + > + tup->rx_in_progress = 0; > + tup->tx_in_progress = 0; > + > + /* > + * Set the trigger level > + * > + * For PIO mode: > + * > + * For receive, this will interrupt the CPU after that many number of > + * bytes are received, for the remaining bytes the receive timeout > + * interrupt is received. Rx high watermark is set to 4. > + * > + * For transmit, if the trasnmit interrupt is enabled, this will > + * interrupt the CPU when the number of entries in the FIFO reaches the > + * low watermark. Tx low watermark is set to 16 bytes. > + * > + * For DMA mode: > + * > + * Set the Tx trigger to 16. This should match the DMA burst size that > + * programmed in the DMA registers. > + */ > + tup->fcr_shadow = UART_FCR_ENABLE_FIFO; > + tup->fcr_shadow |= UART_FCR_R_TRIG_01; > + tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B; > + tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); > + > + /* > + * Initialize the UART with default configuration > + * (115200, N, 8, 1) so that the receive DMA buffer may be > + * enqueued > + */ > + tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR; > + tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); > + tup->fcr_shadow |= UART_FCR_DMA_SELECT; > + tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); > + > + ret = tegra_uart_start_rx_dma(tup); > + if (ret < 0) { > + dev_err(tup->uport.dev, "Not able to start Rx DMA\n"); > + return ret; > + } > + tup->rx_in_progress = 1; > + > + /* > + * Enable IE_RXS for the receive status interrupts like line errros. > + * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd. > + * > + * If using DMA mode, enable EORD instead of receive interrupt which > + * will interrupt after the UART is done with the receive instead of > + * the interrupt when the FIFO "threshold" is reached. > + * > + * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when > + * the DATA is sitting in the FIFO and couldn't be transferred to the > + * DMA as the DMA size alignment(4 bytes) is not met. EORD will be > + * triggered when there is a pause of the incomming data stream for 4 > + * characters long. > + * > + * For pauses in the data which is not aligned to 4 bytes, we get > + * both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first > + * then the EORD. > + */ > + tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD; > + tegra_uart_write(tup, tup->ier_shadow, UART_IER); > + return 0; > +} > + > +static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, > + bool dma_to_memory) > +{ > + struct dma_chan *dma_chan; > + unsigned char *dma_buf; > + dma_addr_t dma_phys; > + int ret; > + struct dma_slave_config dma_sconfig; > + dma_cap_mask_t mask; > + > + dma_cap_zero(mask); > + dma_cap_set(DMA_SLAVE, mask); > + dma_chan = dma_request_channel(mask, NULL, NULL); > + if (!dma_chan) { > + dev_err(tup->uport.dev, > + "Dma channel is not available, will try later\n"); > + return -EPROBE_DEFER; > + } > + > + if (dma_to_memory) { > + dma_buf = dma_alloc_coherent(tup->uport.dev, > + TEGRA_UART_RX_DMA_BUFFER_SIZE, > + &dma_phys, GFP_KERNEL); > + if (!dma_buf) { > + dev_err(tup->uport.dev, > + "Not able to allocate the dma buffer\n"); > + dma_release_channel(dma_chan); > + return -ENOMEM; > + } > + } else { > + dma_phys = dma_map_single(tup->uport.dev, > + tup->uport.state->xmit.buf, UART_XMIT_SIZE, > + DMA_TO_DEVICE); > + dma_buf = tup->uport.state->xmit.buf; > + } > + > + dma_sconfig.slave_id = tup->dma_req_sel; > + if (dma_to_memory) { > + dma_sconfig.src_addr = tup->uport.mapbase; > + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; > + dma_sconfig.src_maxburst = 4; > + } else { > + dma_sconfig.dst_addr = tup->uport.mapbase; > + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; > + dma_sconfig.dst_maxburst = 16; > + } > + > + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); > + if (ret < 0) { > + dev_err(tup->uport.dev, > + "Dma slave config failed, err = %d\n", ret); > + goto scrub; > + } > + > + if (dma_to_memory) { > + tup->rx_dma_chan = dma_chan; > + tup->rx_dma_buf_virt = dma_buf; > + tup->rx_dma_buf_phys = dma_phys; > + } else { > + tup->tx_dma_chan = dma_chan; > + tup->tx_dma_buf_virt = dma_buf; > + tup->tx_dma_buf_phys = dma_phys; > + } > + return 0; > + > +scrub: > + dma_release_channel(dma_chan); > + return ret; > +} > + > +static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup, > + bool dma_to_memory) > +{ > + struct dma_chan *dma_chan; > + > + if (dma_to_memory) { > + dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE, > + tup->rx_dma_buf_virt, tup->rx_dma_buf_phys); > + dma_chan = tup->rx_dma_chan; > + tup->rx_dma_chan = NULL; > + tup->rx_dma_buf_phys = 0; > + tup->rx_dma_buf_virt = NULL; > + } else { > + dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys, > + UART_XMIT_SIZE, DMA_TO_DEVICE); > + dma_chan = tup->tx_dma_chan; > + tup->tx_dma_chan = NULL; > + tup->tx_dma_buf_phys = 0; > + tup->tx_dma_buf_virt = NULL; > + } > + dma_release_channel(dma_chan); > +} > + > +static int tegra_uart_startup(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + int ret; > + > + ret = tegra_uart_dma_channel_allocate(tup, false); > + if (ret < 0) { > + dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret); > + return ret; > + } > + > + ret = tegra_uart_dma_channel_allocate(tup, true); > + if (ret < 0) { > + dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret); > + goto fail_rx_dma; > + } > + > + ret = tegra_uart_hw_init(tup); > + if (ret < 0) { > + dev_err(u->dev, "Uart HW init failed, err = %d\n", ret); > + goto fail_hw_init; > + } > + > + ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED, > + dev_name(u->dev), tup); > + if (ret < 0) { > + dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq); > + goto fail_hw_init; > + } > + return 0; > + > +fail_hw_init: > + tegra_uart_dma_channel_free(tup, true); > +fail_rx_dma: > + tegra_uart_dma_channel_free(tup, false); > + return ret; > +} > + > +static void tegra_uart_shutdown(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + > + tegra_uart_hw_deinit(tup); > + > + tup->rx_in_progress = 0; > + tup->tx_in_progress = 0; > + > + tegra_uart_dma_channel_free(tup, true); > + tegra_uart_dma_channel_free(tup, false); > + free_irq(u->irq, tup); > +} > + > +static void tegra_uart_enable_ms(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + > + if (tup->enable_modem_interrupt) { > + tup->ier_shadow |= UART_IER_MSI; > + tegra_uart_write(tup, tup->ier_shadow, UART_IER); > + } > +} > + > +static void tegra_uart_set_termios(struct uart_port *u, > + struct ktermios *termios, struct ktermios *oldtermios) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + unsigned int baud; > + unsigned long flags; > + unsigned int lcr; > + unsigned int c_cflag = termios->c_cflag; > + int symb_bit = 1; > + > + spin_lock_irqsave(&u->lock, flags); > + > + /* Changing configuration, it is safe to stop any rx now */ > + if (tup->rts_active) > + set_rts(tup, false); > + > + /* Clear all interrupts as configuration is going to be change */ > + tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER); > + tegra_uart_read(tup, UART_IER); > + tegra_uart_write(tup, 0, UART_IER); > + tegra_uart_read(tup, UART_IER); > + > + /* Parity */ > + lcr = tup->lcr_shadow; > + lcr &= ~UART_LCR_PARITY; > + if ((c_cflag & PARENB) == PARENB) { > + symb_bit++; > + if ((c_cflag & CMSPAR) == CMSPAR) { > + dev_err(tup->uport.dev, "Space parity not supported\n"); > + return; > + } else if (c_cflag & PARODD) { > + lcr |= UART_LCR_PARITY; > + lcr &= ~UART_LCR_EPAR; > + lcr &= ~UART_LCR_SPAR; > + } else { > + lcr |= UART_LCR_PARITY; > + lcr |= UART_LCR_EPAR; > + lcr &= ~UART_LCR_SPAR; > + } > + } > + > + lcr &= ~UART_LCR_WLEN8; > + switch (c_cflag & CSIZE) { > + case CS5: > + lcr |= UART_LCR_WLEN5; > + symb_bit += 5; > + break; > + case CS6: > + lcr |= UART_LCR_WLEN6; > + symb_bit += 6; > + break; > + case CS7: > + lcr |= UART_LCR_WLEN7; > + symb_bit += 7; > + break; > + default: > + lcr |= UART_LCR_WLEN8; > + symb_bit += 8; > + break; > + } > + > + /* Stop bits */ > + if (termios->c_cflag & CSTOPB) { > + lcr |= UART_LCR_STOP; > + symb_bit += 2; > + } else { > + lcr &= ~UART_LCR_STOP; > + symb_bit++; > + } > + > + tegra_uart_write(tup, lcr, UART_LCR); > + tup->lcr_shadow = lcr; > + tup->symb_bit = symb_bit; > + > + /* Baud rate. */ > + baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000); > + spin_unlock_irqrestore(&u->lock, flags); > + tegra_set_baudrate(tup, baud); > + spin_lock_irqsave(&u->lock, flags); > + > + /* Flow control */ > + if (termios->c_cflag & CRTSCTS) { > + tup->mcr_shadow |= TEGRA_UART_MCR_CTS_EN; > + tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN; > + tegra_uart_write(tup, tup->mcr_shadow, UART_MCR); > + /* if top layer has asked to set rts active then do so here */ > + if (tup->rts_active) > + set_rts(tup, true); > + } else { > + tup->mcr_shadow &= ~TEGRA_UART_MCR_CTS_EN; > + tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN; > + tegra_uart_write(tup, tup->mcr_shadow, UART_MCR); > + } > + > + /* update the port timeout based on new settings */ > + uart_update_timeout(u, termios->c_cflag, baud); > + > + /* Make sure all write has completed */ > + tegra_uart_read(tup, UART_IER); > + > + /* Reenable interrupt */ > + tegra_uart_write(tup, tup->ier_shadow, UART_IER); > + tegra_uart_read(tup, UART_IER); > + > + spin_unlock_irqrestore(&u->lock, flags); > + return; > +} > + > +/* > + * Flush any TX data submitted for DMA and PIO. Called when the > + * TX circular buffer is reset. > + */ > +static void tegra_uart_flush_buffer(struct uart_port *u) > +{ > + struct tegra_uart_port *tup = to_tegra_uport(u); > + > + tup->tx_bytes = 0; > + if (tup->tx_dma_chan) > + dmaengine_terminate_all(tup->tx_dma_chan); > + return; > +} > + > +static const char *tegra_uart_type(struct uart_port *u) > +{ > + return TEGRA_UART_TYPE; > +} > + > +static struct uart_ops tegra_uart_ops = { > + .tx_empty = tegra_uart_tx_empty, > + .set_mctrl = tegra_uart_set_mctrl, > + .get_mctrl = tegra_uart_get_mctrl, > + .stop_tx = tegra_uart_stop_tx, > + .start_tx = tegra_uart_start_tx, > + .stop_rx = tegra_uart_stop_rx, > + .flush_buffer = tegra_uart_flush_buffer, > + .enable_ms = tegra_uart_enable_ms, > + .break_ctl = tegra_uart_break_ctl, > + .startup = tegra_uart_startup, > + .shutdown = tegra_uart_shutdown, > + .set_termios = tegra_uart_set_termios, > + .type = tegra_uart_type, > + .request_port = tegra_uart_request_port, > + .release_port = tegra_uart_release_port, > +}; > + > +static struct uart_driver tegra_uart_driver = { > + .owner = THIS_MODULE, > + .driver_name = "tegra_hsuart", > + .dev_name = "ttyHS", > + .cons = 0, > + .nr = TEGRA_UART_MAXIMUM, > +}; > + > +static struct tegra_uart_platform_data *tegra_uart_parse_dt( > + struct platform_device *pdev) > +{ > + struct tegra_uart_platform_data *pdata; > + struct device_node *np = pdev->dev.of_node; > + u32 of_dma[2]; > + > + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) { > + dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); > + return NULL; > + } > + > + if (of_property_read_u32_array(np, "nvidia,dma-request-selector", > + of_dma, 2) >= 0) { > + pdata->dma_req_sel = of_dma[1]; > + } else { > + dev_err(&pdev->dev, "missing dma requestor in device tree\n"); > + return NULL; > + } > + > + if (!of_property_read_u32(np, "port-number", &of_dma[0])) { > + pdata->port_number = of_dma[0]; > + } else { > + dev_err(&pdev->dev, "missing port-number in device tree\n"); > + return NULL; > + } > + > + if (of_get_property(np, "nvidia,enable-modem-interrupt", NULL)) > + pdata->enable_modem_interrupt = 1; > + > + return pdata; > +} > + > +static struct of_device_id tegra_uart_of_match[] __devinitconst = { > + { > + .compatible = "nvidia,tegra30-hsuart", > + .data = &tegra30_uart_chip_data, > + }, { > + .compatible = "nvidia,tegra20-hsuart", > + .data = &tegra20_uart_chip_data, > + }, { > + }, > +}; > +MODULE_DEVICE_TABLE(of, tegra_uart_of_match); > + > +static int __devinit tegra_uart_probe(struct platform_device *pdev) > +{ > + struct tegra_uart_port *tup; > + struct uart_port *u; > + struct tegra_uart_platform_data *pdata = pdev->dev.platform_data; > + struct resource *resource; > + int ret; > + const struct tegra_uart_chip_data *cdata = &tegra30_uart_chip_data; > + const struct of_device_id *match; > + > + if (pdev->dev.of_node) { > + match = of_match_device(of_match_ptr(tegra_uart_of_match), > + &pdev->dev); > + if (!match) { > + dev_err(&pdev->dev, "Error: No device match found\n"); > + return -ENODEV; > + } > + cdata = match->data; > + } > + > + if (!pdata && pdev->dev.of_node) > + pdata = tegra_uart_parse_dt(pdev); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data, exiting\n"); > + return -ENODEV; > + } > + > + tup = devm_kzalloc(&pdev->dev, sizeof(*tup), GFP_KERNEL); > + if (!tup) { > + dev_err(&pdev->dev, "Failed to allocate memory for tup\n"); > + return -ENOMEM; > + } > + > + u = &tup->uport; > + u->dev = &pdev->dev; > + u->line = pdata->port_number; > + u->ops = &tegra_uart_ops; > + u->type = PORT_TEGRA; > + u->fifosize = 32; > + tup->dma_req_sel = pdata->dma_req_sel; > + tup->cdata = cdata; > + tup->enable_modem_interrupt = pdata->enable_modem_interrupt; > + > + platform_set_drvdata(pdev, tup); > + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!resource) { > + dev_err(&pdev->dev, "No IO memory resource\n"); > + return -ENODEV; > + } > + > + u->mapbase = resource->start; > + u->membase = devm_request_and_ioremap(&pdev->dev, resource); > + if (!u->membase) { > + dev_err(&pdev->dev, "memregion/iomap address req failed\n"); > + return -EADDRNOTAVAIL; > + } > + > + tup->uart_clk = devm_clk_get(&pdev->dev, "uart-clk"); > + if (IS_ERR(tup->uart_clk)) { > + dev_err(&pdev->dev, "Couldn't get the clock\n"); > + return PTR_ERR(tup->uart_clk); > + } > + > + u->iotype = UPIO_MEM32; > + u->irq = platform_get_irq(pdev, 0); > + u->regshift = 2; > + ret = uart_add_one_port(&tegra_uart_driver, u); > + if (ret < 0) { > + dev_err(&pdev->dev, "Failed to add uart port, err %d\n", ret); > + return ret; > + } > + return ret; > +} > + > +static int __devexit tegra_uart_remove(struct platform_device *pdev) > +{ > + struct tegra_uart_port *tup = platform_get_drvdata(pdev); > + struct uart_port *u = &tup->uport; > + > + uart_remove_one_port(&tegra_uart_driver, u); > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int tegra_uart_suspend(struct device *dev) > +{ > + struct tegra_uart_port *tup = dev_get_drvdata(dev); > + struct uart_port *u = &tup->uport; > + > + return uart_suspend_port(&tegra_uart_driver, u); > +} > + > +static int tegra_uart_resume(struct device *dev) > +{ > + struct tegra_uart_port *tup = dev_get_drvdata(dev); > + struct uart_port *u = &tup->uport; > + > + return uart_resume_port(&tegra_uart_driver, u); > +} > +#endif > + > +static const struct dev_pm_ops tegra_uart_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(tegra_uart_suspend, tegra_uart_resume) > +}; > + > +static struct platform_driver tegra_uart_platform_driver = { > + .probe = tegra_uart_probe, > + .remove = __devexit_p(tegra_uart_remove), > + .driver = { > + .name = "serial-tegra", > + .of_match_table = of_match_ptr(tegra_uart_of_match), > + .pm = &tegra_uart_pm_ops, > + }, > +}; > + > +static int __init tegra_uart_init(void) > +{ > + int ret; > + > + ret = uart_register_driver(&tegra_uart_driver); > + if (ret < 0) { > + pr_err("Could not register %s driver\n", > + tegra_uart_driver.driver_name); > + return ret; > + } > + > + ret = platform_driver_register(&tegra_uart_platform_driver); > + if (ret < 0) { > + pr_err("Uart platfrom driver register failed, e = %d\n", ret); > + uart_unregister_driver(&tegra_uart_driver); > + return ret; > + } > + return 0; > +} > + > +static void __exit tegra_uart_exit(void) > +{ > + pr_info("Unloading tegra uart driver\n"); > + platform_driver_unregister(&tegra_uart_platform_driver); > + uart_unregister_driver(&tegra_uart_driver); > +} > + > +module_init(tegra_uart_init); > +module_exit(tegra_uart_exit); > + > +MODULE_ALIAS("platform:serial-tegra"); > +MODULE_DESCRIPTION("High speed UART driver for tegra chipset"); > +MODULE_AUTHOR("Laxman Dewangan <ldewangan@xxxxxxxxxx>"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/serial_tegra.h b/include/linux/serial_tegra.h > new file mode 100644 > index 0000000..f9828b8 > --- /dev/null > +++ b/include/linux/serial_tegra.h > @@ -0,0 +1,33 @@ > +/* > + * serial_tegra.h > + * > + * Interface for High-speed serial driver for NVIDIA Tegra SoCs. > + * > + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. > + * > + * Author: Laxman Dewangan <ldewangan@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef _SERIAL_TEGRA_H_ > +#define _SERIAL_TEGRA_H_ > + > +struct tegra_uart_platform_data { > + int dma_req_sel; > + int port_number; > + unsigned enable_modem_interrupt:1; > +}; > + > +#endif /* _SERIAL_TEGRA_H_ */ > + > -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html