Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp534700pxj; Thu, 10 Jun 2021 06:52:27 -0700 (PDT) X-Google-Smtp-Source: ABdhPJymX4b87wR1Pq+JQqi65IBM/LbMCOZ1aBww0JGH/0Dss6Jtn5V93BzeCIs0KZJHuz3/I2oB X-Received: by 2002:a17:906:1d11:: with SMTP id n17mr4552728ejh.215.1623333146843; Thu, 10 Jun 2021 06:52:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1623333146; cv=none; d=google.com; s=arc-20160816; b=G2fpDyvsJlrFhyXg6bXlD3g2qj5YVeIKtCrDi2zKxBv18+mxtZq5HVkJJis9hQNfX9 eaTTEAD3t0lpKNDMTieeRae2kkO+34j/UYbzvfaiWqRRLIwjVKy7MF/e4O6arw0sP61K UZFEsqMbmG4B1Tw5tClPRY1KMys0ZonMGe5gsWfUTN1QXM2VXV8THGkf4NaSNPEr5mMM E/SQPoLTLercAmH25txL3HemVL5CewOuJnD9MVG6/OJkf6vlXay1kzLQz1b9BcvSZDPs 0rv9MumKThIekcCSqfcCnywiyenN/hAYcAL9CwkViF1DblAGU6JQHFMrOP5Sbo3cY1E6 lmFA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:content-language :in-reply-to:mime-version:user-agent:date:message-id:from:references :cc:to:subject:dkim-signature; bh=zGTTzhaUVC9nEHP/Db6ADLqp7ZdTZ+d9X5zLPlCiQr0=; b=yJ4CoTAqhxJPRmrbR5WQ+TiAD0drp1tWrzlmQpHSItTT9rTwVxGa/MJi1K+RPt7eXA O4xxP1L9+NtdGj3hDNiAnoFu7BbpHMPvq7qckzl999xjmmJN6k3xIyZMCAAlLV4eQTAL bJayctF4N2EpyvnF8Xv0rLt8uBdmlOOR2sBN3GsUFySNcYRPbRWZq4AeDs7hE/M3Qwm5 d7+ilHBQY//GBu4x9laQbY0vX3VvSN2RLQKC6qPd9t8YHCwSbao8V9D4FZSh+sTQzxqY TwmDFnDoYHhBVu31wgPqbjbSjIsvbXgUzk7W5ahABTlQny/iToVTllB15cTNppnKFZQ1 SiDA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@foss.st.com header.s=selector1 header.b=YIF+QBQ2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=foss.st.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id f24si2417463ejz.234.2021.06.10.06.52.02; Thu, 10 Jun 2021 06:52:26 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@foss.st.com header.s=selector1 header.b=YIF+QBQ2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=foss.st.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230506AbhFJNvz (ORCPT + 99 others); Thu, 10 Jun 2021 09:51:55 -0400 Received: from mx08-00178001.pphosted.com ([91.207.212.93]:6008 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S230289AbhFJNvy (ORCPT ); Thu, 10 Jun 2021 09:51:54 -0400 Received: from pps.filterd (m0046660.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.16.0.43/8.16.0.43) with SMTP id 15ADhRwN021302; Thu, 10 Jun 2021 15:49:35 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foss.st.com; h=subject : to : cc : references : from : message-id : date : mime-version : in-reply-to : content-type : content-transfer-encoding; s=selector1; bh=zGTTzhaUVC9nEHP/Db6ADLqp7ZdTZ+d9X5zLPlCiQr0=; b=YIF+QBQ2c/v7OxdrIKKkqw0OuY0CD7LcgvZ879YHcoejO/Jl27ZIurDHvQ6vPu466cWn DLZeUNRVTkr1THn/i3SFR53h231ey6rDns8aSWo0Av0/EPiUec5T6k+cd93Hu+D6fm+k xeXA8Nr8BkVbTZrk/5ENJLBqBt7HySukRRYSHoLYugRtT+P2GXVjotAH6iB1i+WEVlxh 6g72LbACgAGpOICCsjBrTSUYdtq8YTYmslfgVWgPCJxamzKvcwHeGybCxmDtBY9wHhcb KF/21t8kmTTCR6R6saLq4Epmi7l8DZNWYHvoKrqJqLrIn7KrsETffV4xvrbt+7w5vF99 Aw== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com with ESMTP id 392xq7y349-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 10 Jun 2021 15:49:35 +0200 Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id C536810002A; Thu, 10 Jun 2021 15:49:33 +0200 (CEST) Received: from Webmail-eu.st.com (sfhdag2node3.st.com [10.75.127.6]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id A5D022291B4; Thu, 10 Jun 2021 15:49:33 +0200 (CEST) Received: from lmecxl0573.lme.st.com (10.75.127.46) by SFHDAG2NODE3.st.com (10.75.127.6) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Thu, 10 Jun 2021 15:49:33 +0200 Subject: Re: [PATCH v2 1/2] clocksource: arm_global_timer: implement rate compensation whenever source clock changes To: Daniel Lezcano , Andrea Merello , CC: Patrice Chotard , , , Michal Simek , =?UTF-8?Q?S=c3=b6ren_Brinkmann?= References: <20210406130045.15491-1-andrea.merello@gmail.com> <20210406130045.15491-2-andrea.merello@gmail.com> <0d33db1f-8af1-1519-aba1-3e46afa4cf4c@linaro.org> From: Patrice CHOTARD Message-ID: <1a7d339e-4ac4-86c2-a66b-3741781d8618@foss.st.com> Date: Thu, 10 Jun 2021 15:49:32 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.1 MIME-Version: 1.0 In-Reply-To: <0d33db1f-8af1-1519-aba1-3e46afa4cf4c@linaro.org> Content-Type: text/plain; charset="utf-8" Content-Language: en-US Content-Transfer-Encoding: 8bit X-Originating-IP: [10.75.127.46] X-ClientProxiedBy: SFHDAG1NODE1.st.com (10.75.127.1) To SFHDAG2NODE3.st.com (10.75.127.6) X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.391,18.0.761 definitions=2021-06-10_07:2021-06-10,2021-06-10 signatures=0 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Daniel Thanks for pinging me, i forget this patch. On 6/10/21 3:01 PM, Daniel Lezcano wrote: > > Hi Patrice, > > do you have any comment about these changes ? > > > On 06/04/2021 15:00, Andrea Merello wrote: >> This patch adds rate change notification support for the parent clock; >> should that clock change, then we try to adjust the our prescaler in order >> to compensate (i.e. we adjust to still get the same timer frequency). >> >> This is loosely based on what it's done in timer-cadence-ttc. timer-sun51, >> mips-gic-timer and smp_twd.c also seem to look at their parent clock rate >> and to perform some kind of adjustment whenever needed. >> >> In this particular case we have only one single counter and prescaler for >> all clocksource, clockevent and timer_delay, and we just update it for all >> (i.e. we don't let it go and call clockevents_update_freq() to notify to >> the kernel that our rate has changed). >> >> Note that, there is apparently no other way to fixup things, because once >> we call register_current_timer_delay(), specifying the timer rate, it seems >> that that rate is not supposed to change ever. >> >> In order for this mechanism to work, we have to make assumptions about how >> much the initial clock is supposed to eventually decrease from the initial >> one, and set our initial prescaler to a value that we can eventually >> decrease enough to compensate. We provide an option in KConfig for this. >> >> In case we end up in a situation in which we are not able to compensate the >> parent clock change, we fail returning NOTIFY_BAD. >> >> This fixes a real-world problem with Zynq arch not being able to use this >> driver and CPU_FREQ at the same time (because ARM global timer is fed by >> the CPU clock, which may keep changing when CPU_FREQ is enabled). >> >> Signed-off-by: Andrea Merello >> Cc: Patrice Chotard >> Cc: linux-kernel@vger.kernel.org >> Cc: linux-arm-kernel@lists.infradead.org >> Cc: Michal Simek >> Cc: Sören Brinkmann >> --- >> drivers/clocksource/Kconfig | 13 +++ >> drivers/clocksource/arm_global_timer.c | 122 +++++++++++++++++++++++-- >> 2 files changed, 125 insertions(+), 10 deletions(-) >> >> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig >> index 39aa21d01e05..19fc5f8883e0 100644 >> --- a/drivers/clocksource/Kconfig >> +++ b/drivers/clocksource/Kconfig >> @@ -358,6 +358,19 @@ config ARM_GLOBAL_TIMER >> help >> This option enables support for the ARM global timer unit. >> >> +config ARM_GT_INITIAL_PRESCALER_VAL >> + int "ARM global timer initial prescaler value" >> + default 1 >> + depends on ARM_GLOBAL_TIMER >> + help >> + When the ARM global timer initializes, its current rate is declared >> + to the kernel and maintained forever. Should it's parent clock >> + change, the driver tries to fix the timer's internal prescaler. >> + On some machs (i.e. Zynq) the initial prescaler value thus poses >> + bounds about how much the parent clock is allowed to decrease or >> + increase wrt the initial clock value. >> + This affects CPU_FREQ max delta from the initial frequency. >> + >> config ARM_TIMER_SP804 >> bool "Support for Dual Timer SP804 module" if COMPILE_TEST >> depends on GENERIC_SCHED_CLOCK && CLKDEV_LOOKUP >> diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c >> index 88b2d38a7a61..60a8047fd32e 100644 >> --- a/drivers/clocksource/arm_global_timer.c >> +++ b/drivers/clocksource/arm_global_timer.c >> @@ -31,6 +31,10 @@ >> #define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */ >> #define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */ >> #define GT_CONTROL_AUTO_INC BIT(3) /* banked */ >> +#define GT_CONTROL_PRESCALER_SHIFT 8 >> +#define GT_CONTROL_PRESCALER_MAX 0xF >> +#define GT_CONTROL_PRESCALER_MASK (GT_CONTROL_PRESCALER_MAX << \ >> + GT_CONTROL_PRESCALER_SHIFT) >> >> #define GT_INT_STATUS 0x0c >> #define GT_INT_STATUS_EVENT_FLAG BIT(0) >> @@ -39,6 +43,7 @@ >> #define GT_COMP1 0x14 >> #define GT_AUTO_INC 0x18 >> >> +#define MAX_F_ERR 50 >> /* >> * We are expecting to be clocked by the ARM peripheral clock. >> * >> @@ -46,7 +51,8 @@ >> * the units for all operations. >> */ >> static void __iomem *gt_base; >> -static unsigned long gt_clk_rate; >> +struct notifier_block gt_clk_rate_change_nb; >> +static u32 gt_psv_new, gt_psv_bck, gt_target_rate; >> static int gt_ppi; >> static struct clock_event_device __percpu *gt_evt; >> >> @@ -96,7 +102,10 @@ static void gt_compare_set(unsigned long delta, int periodic) >> unsigned long ctrl; >> >> counter += delta; >> - ctrl = GT_CONTROL_TIMER_ENABLE; >> + ctrl = readl(gt_base + GT_CONTROL); >> + ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE | >> + GT_CONTROL_AUTO_INC | GT_CONTROL_AUTO_INC); >> + ctrl |= GT_CONTROL_TIMER_ENABLE; >> writel_relaxed(ctrl, gt_base + GT_CONTROL); >> writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0); >> writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1); >> @@ -123,7 +132,7 @@ static int gt_clockevent_shutdown(struct clock_event_device *evt) >> >> static int gt_clockevent_set_periodic(struct clock_event_device *evt) >> { >> - gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1); >> + gt_compare_set(DIV_ROUND_CLOSEST(gt_target_rate, HZ), 1); >> return 0; >> } >> >> @@ -177,7 +186,7 @@ static int gt_starting_cpu(unsigned int cpu) >> clk->cpumask = cpumask_of(cpu); >> clk->rating = 300; >> clk->irq = gt_ppi; >> - clockevents_config_and_register(clk, gt_clk_rate, >> + clockevents_config_and_register(clk, gt_target_rate, >> 1, 0xffffffff); >> enable_percpu_irq(clk->irq, IRQ_TYPE_NONE); >> return 0; >> @@ -232,9 +241,28 @@ static struct delay_timer gt_delay_timer = { >> .read_current_timer = gt_read_long, >> }; >> >> +static void gt_write_presc(u32 psv) >> +{ >> + u32 reg; >> + >> + reg = readl(gt_base + GT_CONTROL); >> + reg &= ~GT_CONTROL_PRESCALER_MASK; >> + reg |= psv << GT_CONTROL_PRESCALER_SHIFT; >> + writel(reg, gt_base + GT_CONTROL); >> +} >> + >> +static u32 gt_read_presc(void) >> +{ >> + u32 reg; >> + >> + reg = readl(gt_base + GT_CONTROL); >> + reg &= GT_CONTROL_PRESCALER_MASK; >> + return reg >> GT_CONTROL_PRESCALER_SHIFT; >> +} >> + >> static void __init gt_delay_timer_init(void) >> { >> - gt_delay_timer.freq = gt_clk_rate; >> + gt_delay_timer.freq = gt_target_rate; >> register_current_timer_delay(>_delay_timer); >> } >> >> @@ -243,18 +271,81 @@ static int __init gt_clocksource_init(void) >> writel(0, gt_base + GT_CONTROL); >> writel(0, gt_base + GT_COUNTER0); >> writel(0, gt_base + GT_COUNTER1); >> - /* enables timer on all the cores */ >> - writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); >> + /* set prescaler and enable timer on all the cores */ >> + writel(((CONFIG_ARM_GT_INITIAL_PRESCALER_VAL - 1) << >> + GT_CONTROL_PRESCALER_SHIFT) >> + | GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); >> >> #ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK >> - sched_clock_register(gt_sched_clock_read, 64, gt_clk_rate); >> + sched_clock_register(gt_sched_clock_read, 64, gt_target_rate); >> #endif >> - return clocksource_register_hz(>_clocksource, gt_clk_rate); >> + return clocksource_register_hz(>_clocksource, gt_target_rate); >> +} >> + >> +static int gt_clk_rate_change_cb(struct notifier_block *nb, >> + unsigned long event, void *data) >> +{ >> + struct clk_notifier_data *ndata = data; >> + >> + switch (event) { >> + case PRE_RATE_CHANGE: >> + { >> + int psv; >> + >> + psv = DIV_ROUND_CLOSEST(ndata->new_rate, >> + gt_target_rate); >> + >> + if (abs(gt_target_rate - (ndata->new_rate / psv)) > MAX_F_ERR) >> + return NOTIFY_BAD; >> + >> + psv--; >> + >> + /* prescaler within legal range? */ >> + if (psv < 0 || psv > GT_CONTROL_PRESCALER_MAX) >> + return NOTIFY_BAD; >> + >> + /* >> + * store timer clock ctrl register so we can restore it in case >> + * of an abort. >> + */ >> + gt_psv_bck = gt_read_presc(); >> + gt_psv_new = psv; >> + /* scale down: adjust divider in post-change notification */ >> + if (ndata->new_rate < ndata->old_rate) >> + return NOTIFY_DONE; >> + >> + /* scale up: adjust divider now - before frequency change */ >> + gt_write_presc(psv); >> + break; >> + } >> + case POST_RATE_CHANGE: >> + /* scale up: pre-change notification did the adjustment */ >> + if (ndata->new_rate > ndata->old_rate) >> + return NOTIFY_OK; >> + >> + /* scale down: adjust divider now - after frequency change */ >> + gt_write_presc(gt_psv_new); >> + break; >> + >> + case ABORT_RATE_CHANGE: >> + /* we have to undo the adjustment in case we scale up */ >> + if (ndata->new_rate < ndata->old_rate) >> + return NOTIFY_OK; >> + >> + /* restore original register value */ >> + gt_write_presc(gt_psv_bck); >> + break; >> + default: >> + return NOTIFY_DONE; >> + } >> + >> + return NOTIFY_DONE; >> } >> >> static int __init global_timer_of_register(struct device_node *np) >> { >> struct clk *gt_clk; >> + static unsigned long gt_clk_rate; >> int err = 0; >> >> /* >> @@ -292,11 +383,20 @@ static int __init global_timer_of_register(struct device_node *np) >> } >> >> gt_clk_rate = clk_get_rate(gt_clk); >> + gt_target_rate = gt_clk_rate / CONFIG_ARM_GT_INITIAL_PRESCALER_VAL; >> + gt_clk_rate_change_nb.notifier_call = >> + gt_clk_rate_change_cb; >> + err = clk_notifier_register(gt_clk, >_clk_rate_change_nb); >> + if (err) { >> + pr_warn("Unable to register clock notifier\n"); >> + goto out_clk; >> + } >> + >> gt_evt = alloc_percpu(struct clock_event_device); >> if (!gt_evt) { >> pr_warn("global-timer: can't allocate memory\n"); >> err = -ENOMEM; >> - goto out_clk; >> + goto out_clk_nb; >> } >> >> err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt, >> @@ -326,6 +426,8 @@ static int __init global_timer_of_register(struct device_node *np) >> free_percpu_irq(gt_ppi, gt_evt); >> out_free: >> free_percpu(gt_evt); >> +out_clk_nb: >> + clk_notifier_unregister(gt_clk, >_clk_rate_change_nb); >> out_clk: >> clk_disable_unprepare(gt_clk); >> out_unmap: >> > > Reviewed-by: Patrice Chotard Thanks Patrice