From 1f2b0eaf5293329eaf35742921e1260552313a2d Mon Sep 17 00:00:00 2001 From: Ramalingam C Date: Wed, 11 Jul 2012 12:30:44 +0530 Subject: tty: serial: tegra: IrDA support with tegra_hsuart Adds the IrDA transceiver handling support to tegra_hsuart driver based on the platform data. Bug 999895 Change-Id: Ia475639d97c540d014c7128ef392fa394a5b26ad Signed-off-by: Ramalingam C Reviewed-on: http://git-master/r/114927 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Laxman Dewangan --- drivers/tty/serial/tegra_hsuart.c | 101 +++++++++++++++++++++++++++++++++++++- include/linux/tegra_uart.h | 8 ++- 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/tegra_hsuart.c b/drivers/tty/serial/tegra_hsuart.c index ea20de6ebb41..b2fa281492a7 100644 --- a/drivers/tty/serial/tegra_hsuart.c +++ b/drivers/tty/serial/tegra_hsuart.c @@ -3,7 +3,7 @@ * * High-speed serial driver for NVIDIA Tegra SoCs * - * Copyright (C) 2009-2011 NVIDIA Corporation + * Copyright (C) 2009-2012 NVIDIA Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -59,6 +59,8 @@ #define UART_MCR_CTS_EN 0x20 #define UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \ UART_LSR_PE | UART_LSR_FE) +#define TEGRA_UART_IRDA_CSR 0x8 +#define TEGRA_UART_SIR_ENABLED 0x80 #define TX_FORCE_PIO 0 #define RX_FORCE_PIO 0 @@ -92,6 +94,12 @@ const int dma_req_sel[] = { struct tegra_uart_port { struct uart_port uport; char port_name[32]; + bool is_irda; + int (*irda_init)(void); + void (*irda_start)(void); + int (*irda_mode_switch)(int); + void (*irda_shutdown)(void); + void (*irda_remove)(void); /* Module info */ unsigned long size; @@ -130,6 +138,8 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud); static void do_handle_rx_pio(struct tegra_uart_port *t); static void do_handle_rx_dma(struct tegra_uart_port *t); static void set_rts(struct tegra_uart_port *t, bool active); +static void tegra_start_rx(struct uart_port *u); +static void tegra_stop_rx(struct uart_port *u); static inline u8 uart_readb(struct tegra_uart_port *t, unsigned long reg) { @@ -224,6 +234,8 @@ static void tegra_start_next_tx(struct tegra_uart_port *t) { unsigned long tail; unsigned long count; + unsigned long lsr; + struct uart_port *u = &t->uport; struct circ_buf *xmit; @@ -235,8 +247,22 @@ static void tegra_start_next_tx(struct tegra_uart_port *t) dev_vdbg(t->uport.dev, "+%s %lu %d\n", __func__, count, t->tx_in_progress); - if (count == 0) + if (count == 0) { + if (t->is_irda) { + do { + lsr = uart_readb(t, UART_LSR); + if (lsr & UART_LSR_TEMT) + break; + } while (1); + tegra_start_rx(u); + } goto out; + } + + if (t->is_irda) { + if (t->rx_in_progress) + tegra_stop_rx(u); + } if (!t->use_tx_dma || count < TEGRA_UART_MIN_DMA) tegra_start_pio_tx(t, count); @@ -599,6 +625,42 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) } } +static void tegra_start_rx(struct uart_port *u) +{ + struct tegra_uart_port *t; + unsigned char ier; + + t = container_of(u, struct tegra_uart_port, uport); + + if (t->rts_active) + set_rts(t, true); + + if (!t->rx_in_progress) { + wait_sym_time(t, 1); /* wait a character interval */ + + /* Clear the received Bytes from FIFO */ + tegra_fifo_reset(t, UART_FCR_CLEAR_RCVR); + uart_readb(t, UART_LSR); + ier = 0; + ier |= (UART_IER_RLSI | UART_IER_RTOIE); + if (t->use_rx_dma) + ier |= UART_IER_EORD; + else + ier |= UART_IER_RDI; + t->ier_shadow |= ier; + uart_writeb(t, t->ier_shadow, UART_IER); + + t->rx_in_progress = 1; + + if (t->use_rx_dma && t->rx_dma) + tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req); + + tty_flip_buffer_push(u->state->port.tty); + } + + return; +} + static void tegra_stop_rx(struct uart_port *u) { struct tegra_uart_port *t; @@ -705,6 +767,7 @@ static void tegra_uart_free_rx_dma(struct tegra_uart_port *t) static int tegra_uart_hw_init(struct tegra_uart_port *t) { unsigned char ier; + unsigned char sir; dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n"); @@ -800,6 +863,16 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) t->ier_shadow = ier; uart_writeb(t, ier, UART_IER); + /* + * Tegra UART controller also support SIR PHY + * Enabled IRDA will transmit each zero bit as a short IR pulse. + */ + if (t->is_irda) { + dev_vdbg(t->uport.dev, "SIR enabled\n"); + sir = TEGRA_UART_SIR_ENABLED; + uart_writeb(t, sir, TEGRA_UART_IRDA_CSR); + } + t->uart_state = TEGRA_UART_OPENED; dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n"); return 0; @@ -894,6 +967,9 @@ static int tegra_startup(struct uart_port *u) if (ret) goto fail; + if (t->is_irda && t->irda_start) + t->irda_start(); + pdata = u->dev->platform_data; if (pdata && pdata->is_loopback) t->mcr_shadow |= UART_MCR_LOOP; @@ -920,6 +996,9 @@ static void tegra_shutdown(struct uart_port *u) t = container_of(u, struct tegra_uart_port, uport); dev_vdbg(u->dev, "+tegra_shutdown\n"); + if (t->is_irda && t->irda_shutdown) + t->irda_shutdown(); + tegra_uart_hw_deinit(t); t->rx_in_progress = 0; @@ -1418,6 +1497,7 @@ static int __init tegra_uart_probe(struct platform_device *pdev) { struct tegra_uart_port *t; struct uart_port *u; + struct tegra_uart_platform_data *pdata; struct resource *resource; int ret; char name[64]; @@ -1439,6 +1519,20 @@ static int __init tegra_uart_probe(struct platform_device *pdev) u->type = PORT_TEGRA; u->fifosize = 32; + pdata = u->dev->platform_data; + if (pdata && pdata->is_irda) { + dev_info(&pdev->dev, "Initialized UART %d as SIR PHY\n", + u->line); + t->is_irda = pdata->is_irda; + t->irda_init = pdata->irda_init; + t->irda_start = pdata->irda_start; + t->irda_mode_switch = pdata->irda_mode_switch; + t->irda_shutdown = pdata->irda_shutdown; + t->irda_remove = pdata->irda_remove; + if (t->irda_init) + t->irda_init(); + } + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) { ret = -ENXIO; @@ -1509,6 +1603,9 @@ static int __devexit tegra_uart_remove(struct platform_device *pdev) if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr) pr_err("Invalid Uart instance (%d)\n", pdev->id); + if (t->is_irda && t->irda_remove) + t->irda_remove(); + u = &t->uport; uart_remove_one_port(&tegra_uart_driver, u); diff --git a/include/linux/tegra_uart.h b/include/linux/tegra_uart.h index 38912ea97068..f51b373f8025 100644 --- a/include/linux/tegra_uart.h +++ b/include/linux/tegra_uart.h @@ -1,6 +1,6 @@ /* include/linux/tegra_uart.h * - * Copyright (C) 2011 NVIDIA Corporation + * Copyright (C) 2011-2012 NVIDIA Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,12 @@ struct tegra_uart_platform_data { struct uart_clk_parent *parent_clk_list; int parent_clk_count; bool is_loopback; + bool is_irda; + int (*irda_init)(void); + int (*irda_mode_switch)(int); + void (*irda_start)(void); + void (*irda_shutdown)(void); + void (*irda_remove)(void); }; int tegra_uart_is_tx_empty(struct uart_port *); -- cgit v1.2.3