summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2010-05-23 22:07:01 +0530
committerGary King <gking@nvidia.com>2010-05-25 18:35:41 -0700
commit57ac37ef1a0a7840acec8525af4bca6933001c86 (patch)
tree4ede69fc54f12c2ae297ff6b441c36bc5b3ea2bd
parent70d6156f3c68b4513925e187b0486617c760a541 (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.h40
-rw-r--r--drivers/serial/tegra_hsuart.c111
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;