diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2010-06-17 14:16:19 +0530 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-06-18 14:35:48 -0700 |
commit | 98e7f2cfa2fa0a62bf09e04a90719f04a8472588 (patch) | |
tree | 19cc88c9af6bd86a3844b055226294a62c763200 | |
parent | dcb48435e04dba5aafe87cd2ebaa1e0f09bf90cb (diff) |
[arm/tegra]serial: Power control api for bluesleep power management.
Adding apis for following function to hooking up tegra serial driver with
bluesleep power management:
- Clock off.
- Clock on.
- Setting flow control to desired state.
- Checking for tx fifo status.
Following 4 state of uart state machine is developed to achieve this:
UART_CLOSED, UART_OPENED, UART_SUSPEND, UART_CLOCK_OFF.
The transitions of states are as follows:
UART_CLOSED: the init state on which resource is allocated but not
opened by client or when device is closed.
UART_OPENED: Able to do data transfer.
CLOSED to OPENED by opening the port.
CLOCK_OFF to OPENED by calling function tegra_uart_request_clock_on().
SUSPEND to OPENED by calling resume().
UART_CLOCK_OFF: The controller clock is disabled and so no data transfer
can happen. At this state, controller is not ready for
deep power down.
OPENED to CLOCK_OFF by calling tegra_uart_request_clock_off().
Can not go to this state from CLOSED and SUSPEND.
UART_SUSPEND: The controller is in suspended state and ready for deep power down.
UART_CLOCK_OFF to SUSPEND:
OPENED to SUSPEND.
Change-Id: Ib0c40547665181cadd172840be9aed2cc18ba448
Reviewed-on: http://git-master/r/2819
Reviewed-by: Anantha Idapalapati <aidapalapati@nvidia.com>
Tested-by: Anantha Idapalapati <aidapalapati@nvidia.com>
Reviewed-by: Udaykumar Rameshchan Raval <uraval@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/serial.h | 15 | ||||
-rw-r--r-- | drivers/serial/tegra_hsuart.c | 94 |
2 files changed, 107 insertions, 2 deletions
diff --git a/arch/arm/mach-tegra/include/mach/serial.h b/arch/arm/mach-tegra/include/mach/serial.h index c77afa190998..fb4c92a9902c 100644 --- a/arch/arm/mach-tegra/include/mach/serial.h +++ b/arch/arm/mach-tegra/include/mach/serial.h @@ -24,6 +24,7 @@ #include <linux/types.h> #include <linux/serial_8250.h> +#include <linux/serial_core.h> #include <mach/pinmux.h> @@ -33,4 +34,18 @@ struct tegra_serial_platform_data { int nr_pins; }; +/* Switch off the clock of the uart controller. */ +void tegra_uart_request_clock_off(struct uart_port *uport); + +/* Switch on the clock of the uart controller */ +void tegra_uart_request_clock_on(struct uart_port *uport); + +/* Set the modem control signals state of uart controller. */ +void tegra_uart_set_mctrl(struct uart_port *uport, unsigned int mctrl); + +/* Return the status of the transmit fifo whether empty or not. + * Return 0 if tx fifo is not empty. + * Return TIOCSER_TEMT if tx fifo is empty */ +int tegra_uart_is_tx_empty(struct uart_port *uport); + #endif diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 77fc79037b62..39a6035e9955 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -75,6 +75,11 @@ const int dma_req_sel[] = { #define TEGRA_UART_MIN_DMA 16 #define TEGRA_UART_FIFO_SIZE 8 +#define TEGRA_UART_CLOSED 0 +#define TEGRA_UART_OPENED 1 +#define TEGRA_UART_CLOCK_OFF 2 +#define TEGRA_UART_SUSPEND 3 + struct tegra_uart_port { struct uart_port uport; char port_name[32]; @@ -112,6 +117,7 @@ struct tegra_uart_port { bool use_tx_dma; bool rx_timeout; + int uart_state; }; static inline u8 uart_readb(struct tegra_uart_port *t, unsigned long reg) @@ -539,6 +545,7 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t) t->baud = 0; if (t->pinmux) tegra_pinmux_config_tristate_table(t->pinmux, t->nr_pins, TEGRA_TRI_TRISTATE); + t->uart_state = TEGRA_UART_CLOSED; } static void tegra_uart_free_rx_dma(struct tegra_uart_port *t) @@ -659,6 +666,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) t->ier_shadow = ier; uart_writeb(t, ier, UART_IER); + t->uart_state = TEGRA_UART_OPENED; dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n"); return 0; } @@ -1123,10 +1131,18 @@ static int tegra_uart_suspend(struct platform_device *pdev, pm_message_t state) dev_err(t->uport.dev, "tegra_uart_suspend called\n"); u = &t->uport; + + /* Enable clock before calling suspend so that controller + register can be accessible */ + if (t->uart_state == TEGRA_UART_CLOCK_OFF) { + clk_enable(t->clk); + t->uart_state = TEGRA_UART_OPENED; + } + uart_suspend_port(&tegra_uart_driver, u); if (t->pinmux) tegra_pinmux_config_tristate_table(t->pinmux, t->nr_pins, TEGRA_TRI_TRISTATE); - + t->uart_state = TEGRA_UART_SUSPEND; return 0; } @@ -1142,7 +1158,11 @@ static int tegra_uart_resume(struct platform_device *pdev) dev_err(t->uport.dev, "tegra_uart_resume called\n"); if (t->pinmux) tegra_pinmux_config_tristate_table(t->pinmux, t->nr_pins, TEGRA_TRI_NORMAL); - uart_resume_port(&tegra_uart_driver, u); + + if (t->uart_state == TEGRA_UART_SUSPEND) { + uart_resume_port(&tegra_uart_driver, u); + t->uart_state = TEGRA_UART_OPENED; + } return 0; } @@ -1217,12 +1237,82 @@ static int __init tegra_uart_probe(struct platform_device *pdev) pr_info("Registered UART port %s%d\n", tegra_uart_driver.dev_name, u->line); + t->uart_state = TEGRA_UART_CLOSED; return ret; fail: kfree(t); return -ENODEV; } +/* Switch off the clock of the uart controller. */ +void tegra_uart_request_clock_off(struct uart_port *uport) +{ + unsigned long flags; + struct tegra_uart_port *t; + + dev_vdbg(uport->dev, "tegra_uart_request_clock_off"); + + t = container_of(uport, struct tegra_uart_port, uport); + spin_lock_irqsave(&uport->lock, flags); + if (t->uart_state == TEGRA_UART_OPENED) { + clk_disable(t->clk); + t->uart_state = TEGRA_UART_CLOCK_OFF; + } + spin_unlock_irqrestore(&uport->lock, flags); + return; +} + +/* Switch on the clock of the uart controller */ +void tegra_uart_request_clock_on(struct uart_port *uport) +{ + unsigned long flags; + struct tegra_uart_port *t; + + t = container_of(uport, struct tegra_uart_port, uport); + spin_lock_irqsave(&uport->lock, flags); + if (t->uart_state == TEGRA_UART_CLOCK_OFF) { + clk_enable(t->clk); + t->uart_state = TEGRA_UART_OPENED; + } + spin_unlock_irqrestore(&uport->lock, flags); + return; +} + +/* Set the modem control signals state of uart controller. */ +void tegra_uart_set_mctrl(struct uart_port *uport, unsigned int mctrl) +{ + unsigned long flags; + unsigned char mcr; + struct tegra_uart_port *t; + + t = container_of(uport, struct tegra_uart_port, uport); + spin_lock_irqsave(&uport->lock, flags); + mcr = t->mcr_shadow; + if (mctrl & TIOCM_RTS) { + t->rts_active = true; + set_rts(t, true); + } else { + t->rts_active = false; + set_rts(t, false); + } + + if (mctrl & TIOCM_DTR) + set_dtr(t, true); + else + set_dtr(t, false); + spin_unlock_irqrestore(&uport->lock, flags); + return; +} + +/* Return the status of the transmit fifo whether empty or not. + * Return 0 if tx fifo is not empty. + * Return TIOCSER_TEMT if tx fifo is empty. + */ +int tegra_uart_is_tx_empty(struct uart_port *uport) +{ + return tegra_tx_empty(uport); +} + static int __init tegra_uart_init(void) { int ret; |