From patchwork Thu Apr 24 06:12:19 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dongsheng Wang X-Patchwork-Id: 342092 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [127.0.0.1]) by ozlabs.org (Postfix) with ESMTP id 30E561402F3 for ; Thu, 24 Apr 2014 16:16:18 +1000 (EST) Received: from na01-bn1-obe.outbound.protection.outlook.com (mail-bn1blp0189.outbound.protection.outlook.com [207.46.163.189]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 1B4A2140167 for ; Thu, 24 Apr 2014 16:14:48 +1000 (EST) Received: from BLUPR03CA036.namprd03.prod.outlook.com (10.141.30.29) by BL2PR03MB178.namprd03.prod.outlook.com (10.255.230.150) with Microsoft SMTP Server (TLS) id 15.0.921.12; Thu, 24 Apr 2014 06:14:42 +0000 Received: from BY2FFO11FD044.protection.gbl (2a01:111:f400:7c0c::182) by BLUPR03CA036.outlook.office365.com (2a01:111:e400:879::29) with Microsoft SMTP Server (TLS) id 15.0.921.12 via Frontend Transport; Thu, 24 Apr 2014 06:14:42 +0000 Received: from az84smr01.freescale.net (192.88.158.246) by BY2FFO11FD044.mail.protection.outlook.com (10.1.14.229) with Microsoft SMTP Server (TLS) id 15.0.929.8 via Frontend Transport; Thu, 24 Apr 2014 06:14:42 +0000 Received: from titan.ap.freescale.net (udp143770uds.ap.freescale.net [10.192.208.233] (may be forged)) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id s3O6Ed9B028451; Wed, 23 Apr 2014 23:14:40 -0700 From: Dongsheng Wang To: Subject: [PATCH v2 2/2] fsl/mpic_timer: make mpic_timer to support deep sleep feature Date: Thu, 24 Apr 2014 14:12:19 +0800 Message-ID: <1398319939-36348-1-git-send-email-dongsheng.wang@freescale.com> X-Mailer: git-send-email 1.8.5 X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:192.88.158.246; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10009001)(6009001)(428001)(189002)(199002)(88136002)(81342001)(4396001)(81542001)(50226001)(74662001)(48376002)(80022001)(99396002)(92566001)(92726001)(79102001)(50466002)(87936001)(47776003)(89996001)(93916002)(20776003)(31966008)(77982001)(85852003)(74502001)(83072002)(87286001)(77156001)(83322001)(44976005)(19580395003)(19580405001)(77096999)(33646001)(46102001)(62966002)(80976001)(76482001)(36756003)(6806004)(86362001)(50986999)(42866002); DIR:OUT; SFP:1101; SCL:1; SRVR:BL2PR03MB178; H:az84smr01.freescale.net; FPR:285454.1BD8E2BC.ECE7977F.9AD4FBC1.204B9; MLV:sfv; PTR:gate-az5.freescale.com; MX:1; A:1; LANG:en; MIME-Version: 1.0 X-Forefront-PRVS: 01917B1794 Received-SPF: None (: freescale.com does not designate permitted sender hosts) X-OriginatorOrg: freescale.com Cc: linuxppc-dev@lists.ozlabs.org, chenhui.zhao@freescale.com, jason.jin@freescale.com, Wang Dongsheng X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" From: Wang Dongsheng At T104x platfrom the timer clock will be changed from platform_clock to sys_ref_clock when system going to deep sleep. So before system going to deep sleep, we need to change time to adapt to the new frequency that is sys_ref_clock. And after resume from deep sleep, restore the time that based on platform_clock. Signed-off-by: Wang Dongsheng --- *v2* Remove some unnecessary warning message. Remove "switch_freq_flag", it's unnecessary. Modify the description of the patch. diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c index 9d9b062..71ad368 100644 --- a/arch/powerpc/sysdev/mpic_timer.c +++ b/arch/powerpc/sysdev/mpic_timer.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,9 @@ #include #include +#include #include +#include #define FSL_GLOBAL_TIMER 0x1 @@ -72,6 +75,8 @@ struct timer_group_priv { struct mpic_timer timer[TIMERS_PER_GROUP]; struct list_head node; unsigned int timerfreq; + unsigned int suspended_timerfreq; + unsigned int resume_timerfreq; unsigned int idle; unsigned int flags; spinlock_t lock; @@ -423,6 +428,33 @@ struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, } EXPORT_SYMBOL(mpic_request_timer); +static void timer_group_get_suspended_freq(struct timer_group_priv *priv) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-clockgen-2.0"); + if (!np) + return; + + of_property_read_u32(np, "clock-frequency", &priv->suspended_timerfreq); + of_node_put(np); + + if (!priv->suspended_timerfreq) + pr_warn("Mpic timer will not be accurate during deep sleep.\n"); +} + +static int need_to_switch_freq(void) +{ + u32 svr; + + svr = mfspr(SPRN_SVR); + if (SVR_SOC_VER(svr) == SVR_T1040 || + SVR_SOC_VER(svr) == SVR_T1042) + return 1; + + return 0; +} + static int timer_group_get_freq(struct device_node *np, struct timer_group_priv *priv) { @@ -437,6 +469,13 @@ static int timer_group_get_freq(struct device_node *np, &priv->timerfreq); of_node_put(dn); } + + /* + * For deep sleep, if system goes to deep sleep, + * timer freq will be changed. + */ + if (need_to_switch_freq()) + timer_group_get_suspended_freq(priv); } if (priv->timerfreq <= 0) @@ -445,6 +484,7 @@ static int timer_group_get_freq(struct device_node *np, if (priv->flags & FSL_GLOBAL_TIMER) { div = (1 << (MPIC_TIMER_TCR_CLKDIV >> 8)) * 8; priv->timerfreq /= div; + priv->suspended_timerfreq /= div; } return 0; @@ -564,14 +604,182 @@ out: kfree(priv); } +static void mpic_reset_time(struct mpic_timer *handle, struct timeval *bcr_time, + struct timeval *ccr_time) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + + u64 ccr_ticks = 0; + u64 bcr_ticks = 0; + + /* switch bcr time */ + convert_time_to_ticks(priv, bcr_time, &bcr_ticks); + + /* switch ccr time */ + convert_time_to_ticks(priv, ccr_time, &ccr_ticks); + + if (handle->cascade_handle) { + u32 tmp_ticks; + u32 rem_ticks; + + /* reset ccr ticks to bcr */ + tmp_ticks = div_u64_rem(ccr_ticks, MAX_TICKS_CASCADE, + &rem_ticks); + out_be32(&priv->regs[handle->num].gtbcr, + tmp_ticks | TIMER_STOP); + out_be32(&priv->regs[handle->num - 1].gtbcr, rem_ticks); + + /* start timer */ + clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); + + /* reset bcr */ + tmp_ticks = div_u64_rem(bcr_ticks, MAX_TICKS_CASCADE, + &rem_ticks); + out_be32(&priv->regs[handle->num].gtbcr, + tmp_ticks & ~TIMER_STOP); + out_be32(&priv->regs[handle->num - 1].gtbcr, rem_ticks); + } else { + /* reset ccr ticks to bcr */ + out_be32(&priv->regs[handle->num].gtbcr, + ccr_ticks | TIMER_STOP); + /* start timer */ + clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); + /* reset bcr */ + out_be32(&priv->regs[handle->num].gtbcr, + bcr_ticks & ~TIMER_STOP); + } +} + +static void do_switch_time(struct mpic_timer *handle, unsigned int new_freq) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + struct timeval ccr_time; + struct timeval bcr_time; + unsigned int timerfreq; + u32 test_stop; + u64 ticks; + + test_stop = in_be32(&priv->regs[handle->num].gtbcr); + test_stop &= TIMER_STOP; + if (test_stop) + return; + + /* stop timer, prepare reset time */ + setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); + + /* get bcr time */ + if (handle->cascade_handle) { + u32 tmp_ticks; + + tmp_ticks = in_be32(&priv->regs[handle->num].gtbcr); + tmp_ticks &= ~TIMER_STOP; + ticks = ((u64)tmp_ticks & UINT_MAX) * (u64)MAX_TICKS_CASCADE; + tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtbcr); + ticks += tmp_ticks; + } else { + ticks = in_be32(&priv->regs[handle->num].gtbcr); + ticks &= ~TIMER_STOP; + } + convert_ticks_to_time(priv, ticks, &bcr_time); + + /* get ccr time */ + mpic_get_remain_time(handle, &ccr_time); + + /* recalculate timer time */ + timerfreq = priv->timerfreq; + priv->timerfreq = new_freq; + mpic_reset_time(handle, &bcr_time, &ccr_time); + priv->timerfreq = timerfreq; +} + +static void switch_group_timer(struct timer_group_priv *priv, + unsigned int new_freq) +{ + int i, num; + + for (i = 0; i < TIMERS_PER_GROUP; i++) { + num = TIMERS_PER_GROUP - 1 - i; + /* cascade */ + if ((i + 1) < TIMERS_PER_GROUP && + priv->timer[num].cascade_handle) { + do_switch_time(&priv->timer[num], new_freq); + i++; + continue; + } + + if (!test_bit(i, (unsigned long *)&priv->idle)) + do_switch_time(&priv->timer[num], new_freq); + } +} + +static int mpic_timer_suspend(void) +{ + struct timer_group_priv *priv; + suspend_state_t pm_state; + + pm_state = pm_suspend_state(); + + list_for_each_entry(priv, &timer_group_list, node) { + /* timer not be used */ + if (priv->idle == 0xf) + continue; + + switch (pm_state) { + case PM_SUSPEND_STANDBY: + break; + case PM_SUSPEND_MEM: + if (!priv->suspended_timerfreq) + continue; + + /* will switch timers, a set of timer */ + switch_group_timer(priv, priv->suspended_timerfreq); + + /* Software: switch timerfreq to suspended freq */ + priv->resume_timerfreq = priv->timerfreq; + priv->timerfreq = priv->suspended_timerfreq; + break; + default: + break; + } + } + + return 0; +} + static void mpic_timer_resume(void) { struct timer_group_priv *priv; + suspend_state_t pm_state; + + pm_state = pm_suspend_state(); list_for_each_entry(priv, &timer_group_list, node) { /* Init FSL timer hardware */ if (priv->flags & FSL_GLOBAL_TIMER) setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); + + /* timer not be used */ + if (priv->idle == 0xf) + continue; + + switch (pm_state) { + case PM_SUSPEND_STANDBY: + break; + case PM_SUSPEND_MEM: + if (!priv->suspended_timerfreq) + continue; + + /* will switch timers, a set of timer */ + switch_group_timer(priv, priv->resume_timerfreq); + + /* restore timerfreq */ + priv->timerfreq = priv->resume_timerfreq; + break; + default: + break; + } } } @@ -581,6 +789,7 @@ static const struct of_device_id mpic_timer_ids[] = { }; static struct syscore_ops mpic_timer_syscore_ops = { + .suspend = mpic_timer_suspend, .resume = mpic_timer_resume, };