From patchwork Mon Apr 14 02:24:10 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dongsheng Wang X-Patchwork-Id: 338808 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 C8AEE1401A7 for ; Mon, 14 Apr 2014 12:27:49 +1000 (EST) Received: from ch1outboundpool.messaging.microsoft.com (ch1ehsobe003.messaging.microsoft.com [216.32.181.183]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 9D82C140164 for ; Mon, 14 Apr 2014 12:26:47 +1000 (EST) Received: from mail117-ch1-R.bigfish.com (10.43.68.249) by CH1EHSOBE001.bigfish.com (10.43.70.51) with Microsoft SMTP Server id 14.1.225.22; Mon, 14 Apr 2014 02:26:09 +0000 Received: from mail117-ch1 (localhost [127.0.0.1]) by mail117-ch1-R.bigfish.com (Postfix) with ESMTP id 2282E100288 for ; Mon, 14 Apr 2014 02:26:09 +0000 (UTC) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-SpamScore: 3 X-BigFish: VS3(zzzz1f42h2148h1ee6h1de0h1fdah2073h2146h1202h1e76h2189h1d1ah1d2ah21bch1fc6h208chzz1de098h8275bh1de097hz2dh2a8h839hd24he5bhf0ah1288h12a5h12a9h12bdh12e5h137ah139eh13b6h1441h1504h1537h162dh1631h1758h1898h18e1h1946h19b5h1ad9h1b0ah1b2fh2222h224fh1fb3h1d0ch1d2eh1d3fh1dfeh1dffh1e23h1fe8h1ff5h2218h2216h226dh22d0h24afh2327h2336h2438h2461h2487h24d7h2516h2545h255eh25cch25f6h2605h268bh26d3h10bek1155h) Received: from mail117-ch1 (localhost.localdomain [127.0.0.1]) by mail117-ch1 (MessageSwitch) id 1397442366683643_31983; Mon, 14 Apr 2014 02:26:06 +0000 (UTC) Received: from CH1EHSMHS008.bigfish.com (snatpool1.int.messaging.microsoft.com [10.43.68.244]) by mail117-ch1.bigfish.com (Postfix) with ESMTP id 97FEA3A0253; Mon, 14 Apr 2014 02:26:06 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by CH1EHSMHS008.bigfish.com (10.43.70.8) with Microsoft SMTP Server (TLS) id 14.16.227.3; Mon, 14 Apr 2014 02:26:01 +0000 Received: from tx30smr01.am.freescale.net (10.81.153.31) by 039-SN1MMR1-003.039d.mgd.msft.net (10.84.1.16) with Microsoft SMTP Server (TLS) id 14.3.174.2; Mon, 14 Apr 2014 02:26:33 +0000 Received: from titan.am.freescale.net (udp143770uds.ap.freescale.net [10.192.208.233] (may be forged)) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id s3E2QRxO010945; Sun, 13 Apr 2014 19:26:31 -0700 From: Dongsheng Wang To: Subject: [PATCH 2/2] fsl/mpic_timer: make mpic_timer to support deep sleep feature Date: Mon, 14 Apr 2014 10:24:10 +0800 Message-ID: <1397442250-14886-2-git-send-email-dongsheng.wang@freescale.com> X-Mailer: git-send-email 1.8.5 In-Reply-To: <1397442250-14886-1-git-send-email-dongsheng.wang@freescale.com> References: <1397442250-14886-1-git-send-email-dongsheng.wang@freescale.com> MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-FOPE-CONNECTOR: Id%0$Dn%*$RO%0$TLS%0$FQDN%$TlsDn% X-FOPE-CONNECTOR: Id%0$Dn%FREESCALE.MAIL.ONMICROSOFT.COM$RO%1$TLS%0$FQDN%$TlsDn% 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 when system going to deep sleep. Add suspend function to switch timer time before system going to deep sleep, and recovery the time after resume from deep sleep. Signed-off-by: Wang Dongsheng diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c index 9d9b062..737a53d 100644 --- a/arch/powerpc/sysdev/mpic_timer.c +++ b/arch/powerpc/sysdev/mpic_timer.c @@ -11,6 +11,7 @@ * option) any later version. */ +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include +#include #include #define FSL_GLOBAL_TIMER 0x1 @@ -71,8 +74,10 @@ struct timer_group_priv { struct timer_regs __iomem *regs; struct mpic_timer timer[TIMERS_PER_GROUP]; struct list_head node; + unsigned long idle; unsigned int timerfreq; - unsigned int idle; + unsigned int suspended_timerfreq; + unsigned int resume_timerfreq; unsigned int flags; spinlock_t lock; void __iomem *group_tcr; @@ -88,6 +93,7 @@ static struct cascade_priv cascade_timer[] = { }; static LIST_HEAD(timer_group_list); +static int switch_freq_flag; static void convert_ticks_to_time(struct timer_group_priv *priv, const u64 ticks, struct timeval *time) @@ -423,6 +429,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) { + pr_err("mpic timer: Missing clockgen device node.\n"); + + return; + } + + of_property_read_u32(np, "clock-frequency", &priv->suspended_timerfreq); + of_node_put(np); +} + +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 +470,15 @@ static int timer_group_get_freq(struct device_node *np, &priv->timerfreq); of_node_put(dn); } + + /* + * For deep sleep, if system going to deep sleep, + * timer freq will be changed. + */ + if (need_to_switch_freq()) { + timer_group_get_suspended_freq(priv); + switch_freq_flag = 1; + } } if (priv->timerfreq <= 0) @@ -445,6 +487,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 +607,190 @@ 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, &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 (!switch_freq_flag) + continue; + + if (!priv->suspended_timerfreq) { + pr_warn("Mpic timer will not be accurate.\n"); + 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 (!switch_freq_flag) + continue; + + 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 +800,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, };