diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2010-05-23 22:07:01 +0530 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-05-25 18:35:41 -0700 |
commit | 57ac37ef1a0a7840acec8525af4bca6933001c86 (patch) | |
tree | 4ede69fc54f12c2ae297ff6b441c36bc5b3ea2bd | |
parent | 70d6156f3c68b4513925e187b0486617c760a541 (diff) |
tegra serial: adding 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 machine is developed to achieve this:
UART_CLOSED, UART_OPENED, UART_SUSPEND, UART_CLOCK_OFF.
The transitions of state machines are as follows:
UART_CLOSED: the init state on which resource is allocated but not
opened by client or 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.
Tested in whistler.
Change-Id: I24461ac6f20f61e44b49332cdc6b93a01f605fc9
Reviewed-on: http://git-master/r/1377
Reviewed-by: Udaykumar Rameshchan Raval <uraval@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
Tested-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/tegra_serial.h | 40 | ||||
-rw-r--r-- | drivers/serial/tegra_hsuart.c | 111 |
2 files changed, 147 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/include/mach/tegra_serial.h b/arch/arm/mach-tegra/include/mach/tegra_serial.h new file mode 100644 index 000000000000..760e7167524a --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/tegra_serial.h @@ -0,0 +1,40 @@ +/* + * arch/arm/mach-tegra/include/mach/tegra_serial.h + * + * Copyright (c) 2009-2010, 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 Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MACH_TEGRA_SERIAL_H +#define __MACH_TEGRA_SERIAL_H + +#include <linux/serial_core.h> + +/* 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 55f49f8a4c35..b7e6f14953e5 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -46,6 +46,7 @@ #include "ap15/aruart.h" #include "nvrm_interrupt.h" #include "nvrm_power.h" +#include "mach/tegra_serial.h" #if 0 #define function_trace(i) printk(KERN_ERR "%s()%s \n",__func__, (i)?"++":"--") @@ -66,6 +67,11 @@ static int rx_force_pio = 0; #define TEGRA_TX_PIO 2 #define TEGRA_TX_DMA 3 +#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]; @@ -126,6 +132,9 @@ struct tegra_uart_port { int tx_in_progress; int rx_in_progress; + + int uart_state; + }; static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud); @@ -790,6 +799,7 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t) NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE); t->baud = 0; + t->uart_state = TEGRA_UART_CLOSED; } static int tegra_uart_hw_init(struct tegra_uart_port *t) @@ -932,9 +942,9 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_RHR, ENABLE, ier); t->ier_shadow = ier; writeb(ier, t->regs + UART_IER_DLAB_0_0); - spin_unlock(&t->reg_access_lock); + t->uart_state = TEGRA_UART_OPENED; dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n"); return 0; @@ -1437,7 +1447,15 @@ static int tegra_uart_suspend(struct platform_device *pdev, pm_message_t state) u = &t->uport; dev_info(t->uport.dev, "tegra_uart_suspend called \n"); + + /* 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); + t->uart_state = TEGRA_UART_SUSPEND; return 0; } @@ -1452,12 +1470,13 @@ static int tegra_uart_resume(struct platform_device *pdev) u = &t->uport; dev_info(t->uport.dev, "tegra_uart_resume called \n"); - 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; } - - static int __devexit tegra_uart_remove(struct platform_device *pdev) { struct tegra_uart_port *t = platform_get_drvdata(pdev); @@ -1557,6 +1576,7 @@ static int __init tegra_uart_probe(struct platform_device *pdev) dev_info(u->dev, "Registered UART port %s%d\n", tegra_uart_driver.dev_name, u->line); + t->uart_state = TEGRA_UART_CLOSED; return ret; rx_workq_fail: @@ -1569,6 +1589,89 @@ clk_fail: return ret; } +/* 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; + + 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) +{ + struct tegra_uart_port *t; + unsigned long flags; + unsigned char lsr; + unsigned int ret = 0; + + t = container_of(uport, struct tegra_uart_port, uport); + spin_lock_irqsave(&uport->lock, flags); + if (t->tx_in_progress) { + goto end; + } + + lsr = readb(t->regs + UART_LSR_0); + if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) + ret = TIOCSER_TEMT; +end: + spin_unlock_irqrestore(&uport->lock, flags); + return ret; +} + static int __init tegra_uart_init(void) { int ret; |