From patchwork Thu Feb 25 17:25:49 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tero Kristo X-Patchwork-Id: 82026 X-Patchwork-Delegate: khilman@deeprootsystems.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1PFe7xl021249 for ; Thu, 25 Feb 2010 15:40:08 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759390Ab0BYPjb (ORCPT ); Thu, 25 Feb 2010 10:39:31 -0500 Received: from smtp.nokia.com ([192.100.122.230]:43796 "EHLO mgw-mx03.nokia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932442Ab0BYPjV (ORCPT ); Thu, 25 Feb 2010 10:39:21 -0500 Received: from esebh105.NOE.Nokia.com (esebh105.ntc.nokia.com [172.21.138.211]) by mgw-mx03.nokia.com (Switch-3.3.3/Switch-3.3.3) with ESMTP id o1PFd8oi004580 for ; Thu, 25 Feb 2010 17:39:17 +0200 Received: from esebh102.NOE.Nokia.com ([172.21.138.183]) by esebh105.NOE.Nokia.com with Microsoft SMTPSVC(6.0.3790.3959); Thu, 25 Feb 2010 17:39:14 +0200 Received: from mgw-sa01.ext.nokia.com ([147.243.1.47]) by esebh102.NOE.Nokia.com over TLS secured channel with Microsoft SMTPSVC(6.0.3790.3959); Thu, 25 Feb 2010 17:39:13 +0200 Received: from localhost.localdomain (sokoban.nmp.nokia.com [172.22.215.13]) by mgw-sa01.ext.nokia.com (Switch-3.3.3/Switch-3.3.3) with ESMTP id o1PFdC1C020210 for ; Thu, 25 Feb 2010 17:39:12 +0200 From: Tero Kristo To: linux-omap@vger.kernel.org Subject: [PATCHv6] OMAP3: Serial: Improved sleep logic Date: Thu, 25 Feb 2010 19:25:49 +0200 Message-Id: <1267118749-1836-1-git-send-email-tero.kristo@nokia.com> X-Mailer: git-send-email 1.5.4.3 In-Reply-To: <> References: <> X-OriginalArrivalTime: 25 Feb 2010 15:39:14.0041 (UTC) FILETIME=[AEC67E90:01CAB630] X-Nokia-AV: Clean Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 25 Feb 2010 15:40:08 +0000 (UTC) diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 5f3035e..06d18f5 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -48,7 +49,10 @@ struct omap_uart_state { int num; int can_sleep; struct timer_list timer; + struct timer_list garbage_timer; + struct work_struct wakeup_work; u32 timeout; + u8 garbage_ignore; void __iomem *wk_st; void __iomem *wk_en; @@ -243,6 +247,11 @@ static inline void omap_uart_save_context(struct omap_uart_state *uart) {} static inline void omap_uart_restore_context(struct omap_uart_state *uart) {} #endif /* CONFIG_PM && CONFIG_ARCH_OMAP3 */ +#ifdef CONFIG_PM +static void omap_uart_smart_idle_enable(struct omap_uart_state *uart, + int enable); +#endif + static inline void omap_uart_enable_clocks(struct omap_uart_state *uart) { if (uart->clocked) @@ -252,6 +261,15 @@ static inline void omap_uart_enable_clocks(struct omap_uart_state *uart) clk_enable(uart->fck); uart->clocked = 1; omap_uart_restore_context(uart); +#ifdef CONFIG_PM + omap_uart_smart_idle_enable(uart, 0); +#endif + + /* Set up garbage timer to ignore RX during first jiffy */ + if (uart->timeout) { + mod_timer(&uart->garbage_timer, jiffies + 1); + uart->garbage_ignore = 1; + } } #ifdef CONFIG_PM @@ -263,6 +281,7 @@ static inline void omap_uart_disable_clocks(struct omap_uart_state *uart) omap_uart_save_context(uart); uart->clocked = 0; + omap_uart_smart_idle_enable(uart, 1); clk_disable(uart->ick); clk_disable(uart->fck); } @@ -320,7 +339,6 @@ static void omap_uart_block_sleep(struct omap_uart_state *uart) { omap_uart_enable_clocks(uart); - omap_uart_smart_idle_enable(uart, 0); uart->can_sleep = 0; if (uart->timeout) mod_timer(&uart->timer, jiffies + uart->timeout); @@ -338,7 +356,6 @@ static void omap_uart_allow_sleep(struct omap_uart_state *uart) if (!uart->clocked) return; - omap_uart_smart_idle_enable(uart, 1); uart->can_sleep = 1; del_timer(&uart->timer); } @@ -350,13 +367,30 @@ static void omap_uart_idle_timer(unsigned long data) omap_uart_allow_sleep(uart); } +static void omap_uart_garbage_timer(unsigned long data) +{ + struct omap_uart_state *uart = (struct omap_uart_state *)data; + + uart->garbage_ignore = 0; +} + +static void omap_uart_wakeup_work(struct work_struct *work) +{ + struct omap_uart_state *uart = + container_of(work, struct omap_uart_state, wakeup_work); + + omap_uart_block_sleep(uart); +} + void omap_uart_prepare_idle(int num) { struct omap_uart_state *uart; list_for_each_entry(uart, &uart_list, node) { if (num == uart->num && uart->can_sleep) { - omap_uart_disable_clocks(uart); + if (serial_read_reg(uart->p, UART_LSR) & + UART_LSR_TEMT) + omap_uart_disable_clocks(uart); return; } } @@ -375,12 +409,12 @@ void omap_uart_resume_idle(int num) u16 p = omap_ctrl_readw(uart->padconf); if (p & OMAP3_PADCONF_WAKEUPEVENT0) - omap_uart_block_sleep(uart); + schedule_work(&uart->wakeup_work); } /* Check for normal UART wakeup */ if (__raw_readl(uart->wk_st) & uart->wk_mask) - omap_uart_block_sleep(uart); + schedule_work(&uart->wakeup_work); return; } } @@ -428,8 +462,18 @@ int omap_uart_can_sleep(void) static irqreturn_t omap_uart_interrupt(int irq, void *dev_id) { struct omap_uart_state *uart = dev_id; + u8 lsr; - omap_uart_block_sleep(uart); + lsr = serial_read_reg(uart->p, UART_LSR); + /* Check for receive interrupt */ + if (lsr & UART_LSR_DR) { + omap_uart_block_sleep(uart); + if (uart->garbage_ignore) { + del_timer(&uart->garbage_timer); + uart->garbage_ignore = 0; + serial_read_reg(uart->p, UART_RX); + } + } return IRQ_NONE; } @@ -443,6 +487,9 @@ static void omap_uart_idle_init(struct omap_uart_state *uart) uart->timeout = DEFAULT_TIMEOUT; setup_timer(&uart->timer, omap_uart_idle_timer, (unsigned long) uart); + setup_timer(&uart->garbage_timer, omap_uart_garbage_timer, + (unsigned long) uart); + INIT_WORK(&uart->wakeup_work, omap_uart_wakeup_work); if (uart->timeout) mod_timer(&uart->timer, jiffies + uart->timeout); omap_uart_smart_idle_enable(uart, 0); @@ -507,15 +554,13 @@ static void omap_uart_idle_init(struct omap_uart_state *uart) void omap_uart_enable_irqs(int enable) { - int ret; struct omap_uart_state *uart; list_for_each_entry(uart, &uart_list, node) { if (enable) - ret = request_irq(uart->p->irq, omap_uart_interrupt, - IRQF_SHARED, "serial idle", (void *)uart); + enable_irq(uart->p->irq); else - free_irq(uart->p->irq, (void *)uart); + disable_irq(uart->p->irq); } }