From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.5 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CE293C433F5 for ; Fri, 17 Sep 2021 12:07:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B64D161241 for ; Fri, 17 Sep 2021 12:07:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243496AbhIQMJC (ORCPT ); Fri, 17 Sep 2021 08:09:02 -0400 Received: from esa.microchip.iphmx.com ([68.232.153.233]:16048 "EHLO esa.microchip.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243766AbhIQMIg (ORCPT ); Fri, 17 Sep 2021 08:08:36 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1631880435; x=1663416435; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=so0cjzgv6AL0M8IG4SX9vQHUIt6VZtxCRdJ4zCgycSQ=; b=NtaAda8kfb9pTQ+RibRNOJL2pkSNrJKcqToM2IEU6IiOHf+sY/QSvUcU MVRft2ITvm250TvJptKKZveBaAG0vhox83mbYN+YL/LyYeWr8hXijsyMj /kaFY+IrJj5aUOT4/cJIzf74lUS9kIO+Zq60WyqGu1H7Gx5uIP8PE4VUM F5AtNtpw9OiVWsFzkcnFaK2QDzeTOiDaXzAvqofXvH6GZNgc12+5iQZRd PzQOMUyuf2ysZtambtZMEoYdFajdjj572QaCl1enflumNpkMdoVdoFWmO vnSSYsdV6JFJ8D8/OMp6AhW6Spxo/671hJOwOt++0+lmzaCI6H7+/U3He w==; IronPort-SDR: Xx7EC0U9zsI/jW+sIa7H58JAsgNLXFJsrksSMNizRxkU+lQKfs18gk8ssRfWOFFcKmJ8hksll/ 4injne5GXJh4jPTzO/xdqkpaVEI4VBeoQLdxaSeQ5UF1IVJAnm3cEsArOqxr6C0cGUTrx4gEcY l9i3ode2zGtxDjwV/8TPfblDBEu+Ztfj5eXXm/NtK2LF06UMDf0TbDvlWqePuWzRdo/g9s9WD+ EVQJrCle2SKaC926lFB3IuFgv4s6Njx3y45TQerKgyO6Nyg+fnAmScCNLGLbii8oOxVCFGK9VJ vIX/R6eur/8/qwyb15kVHIrn X-IronPort-AV: E=Sophos;i="5.85,301,1624345200"; d="scan'208";a="136356788" Received: from smtpout.microchip.com (HELO email.microchip.com) ([198.175.253.82]) by esa5.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 17 Sep 2021 05:07:14 -0700 Received: from chn-vm-ex03.mchp-main.com (10.10.85.151) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.14; Fri, 17 Sep 2021 05:07:12 -0700 Received: from rob-dk-mpu01.microchip.com (10.10.115.15) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server id 15.1.2176.14 via Frontend Transport; Fri, 17 Sep 2021 05:07:10 -0700 From: Claudiu Beznea To: , , , , CC: , , , Claudiu Beznea Subject: [PATCH v3 11/17] clk: at91: clk-sam9x60-pll: add notifier for div part of PLL Date: Fri, 17 Sep 2021 15:06:36 +0300 Message-ID: <20210917120642.8993-12-claudiu.beznea@microchip.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20210917120642.8993-1-claudiu.beznea@microchip.com> References: <20210917120642.8993-1-claudiu.beznea@microchip.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org SAM9X60's PLL which is also part of SAMA7G5 is composed of 2 parts: one fractional part and one divider. On SAMA7G5 the CPU PLL could be changed at run-time to implement DVFS. The hardware clock tree on SAMA7G5 for CPU PLL is as follows: +---- div1 ----------------> cpuck | FRAC PLL ---> DIV PLL -+-> prescaler ---> div0 ---> mck0 The div1 block is not implemented in Linux; on prescaler block it has been discovered a bug on some scenarios and will be removed from Linux in next commits. Thus, the final clock tree that will be used in Linux will be as follows: +-----------> cpuck | FRAC PLL ---> DIV PLL -+-> div0 ---> mck0 It has been proposed in [1] to not introduce a new CPUFreq driver but to overload the proper clock drivers with proper operation such that cpufreq-dt to be used. To accomplish this DIV PLL and div0 implement clock notifiers which applies safe dividers before FRAC PLL is changed. The current commit treats only the DIV PLL by adding a notifier that sets a safe divider on PRE_RATE_CHANGE events. The safe divider is provided by initialization clock code (sama7g5.c) based on the OPP located for device. The div0 is treated in next commits (to keep the changes as clean as possible). [1] https://lore.kernel.org/lkml/20210105104426.4tmgc2l3vyicwedd@vireshk-i7/ Signed-off-by: Claudiu Beznea --- drivers/clk/at91/clk-sam9x60-pll.c | 102 ++++++++++++++++++++++------- drivers/clk/at91/pmc.h | 3 +- drivers/clk/at91/sam9x60.c | 6 +- drivers/clk/at91/sama7g5.c | 42 +++++++++++- 4 files changed, 124 insertions(+), 29 deletions(-) diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c index a73d7c96ce1d..d757003004cb 100644 --- a/drivers/clk/at91/clk-sam9x60-pll.c +++ b/drivers/clk/at91/clk-sam9x60-pll.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -47,12 +48,15 @@ struct sam9x60_div { struct sam9x60_pll_core core; struct at91_clk_pms pms; u8 div; + u8 safe_div; }; #define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw) #define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core) #define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core) +static struct sam9x60_div *notifier_div; + static inline bool sam9x60_pll_ready(struct regmap *regmap, int id) { unsigned int status; @@ -329,6 +333,26 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = { .restore_context = sam9x60_frac_pll_restore_context, }; +/* This function should be called with spinlock acquired. */ +static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div, + bool enable) +{ + struct regmap *regmap = core->regmap; + u32 ena_msk = enable ? core->layout->endiv_mask : 0; + u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0; + + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, + core->layout->div_mask | ena_msk, + (div << core->layout->div_shift) | ena_val); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); + + while (!sam9x60_pll_ready(regmap, core->id)) + cpu_relax(); +} + static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) { struct sam9x60_div *div = to_sam9x60_div(core); @@ -346,17 +370,7 @@ static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) if (!!(val & core->layout->endiv_mask) && cdiv == div->div) goto unlock; - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, - core->layout->div_mask | core->layout->endiv_mask, - (div->div << core->layout->div_shift) | - (1 << core->layout->endiv_shift)); - - regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, - AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, - AT91_PMC_PLL_UPDT_UPDATE | core->id); - - while (!sam9x60_pll_ready(regmap, core->id)) - cpu_relax(); + sam9x60_div_pll_set_div(core, div->div, 1); unlock: spin_unlock_irqrestore(core->lock, flags); @@ -502,16 +516,7 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate, if (cdiv == div->div) goto unlock; - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, - core->layout->div_mask, - (div->div << core->layout->div_shift)); - - regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, - AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, - AT91_PMC_PLL_UPDT_UPDATE | core->id); - - while (!sam9x60_pll_ready(regmap, core->id)) - cpu_relax(); + sam9x60_div_pll_set_div(core, div->div, 0); unlock: spin_unlock_irqrestore(core->lock, irqflags); @@ -538,6 +543,48 @@ static void sam9x60_div_pll_restore_context(struct clk_hw *hw) sam9x60_div_pll_set(core); } +static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier, + unsigned long code, void *data) +{ + struct sam9x60_div *div = notifier_div; + struct sam9x60_pll_core core = div->core; + struct regmap *regmap = core.regmap; + unsigned long irqflags; + u32 val, cdiv; + int ret = NOTIFY_DONE; + + if (code != PRE_RATE_CHANGE) + return ret; + + /* + * We switch to safe divider to avoid overclocking of other domains + * feed by us while the frac PLL (our parent) is changed. + */ + div->div = div->safe_div; + + spin_lock_irqsave(core.lock, irqflags); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + core.id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); + cdiv = (val & core.layout->div_mask) >> core.layout->div_shift; + + /* Stop if nothing changed. */ + if (cdiv == div->safe_div) + goto unlock; + + sam9x60_div_pll_set_div(&core, div->div, 0); + ret = NOTIFY_OK; + +unlock: + spin_unlock_irqrestore(core.lock, irqflags); + + return ret; +} + +static struct notifier_block sam9x60_div_pll_notifier = { + .notifier_call = sam9x60_div_pll_notifier_fn, +}; + static const struct clk_ops sam9x60_div_pll_ops = { .prepare = sam9x60_div_pll_prepare, .unprepare = sam9x60_div_pll_unprepare, @@ -647,7 +694,8 @@ struct clk_hw * __init sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, const char *name, const char *parent_name, u8 id, const struct clk_pll_characteristics *characteristics, - const struct clk_pll_layout *layout, u32 flags) + const struct clk_pll_layout *layout, u32 flags, + u32 safe_div) { struct sam9x60_div *div; struct clk_hw *hw; @@ -656,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, unsigned int val; int ret; - if (id > PLL_MAX_ID || !lock) + /* We only support one changeable PLL. */ + if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div)) return ERR_PTR(-EINVAL); + if (safe_div >= PLL_DIV_MAX) + safe_div = PLL_DIV_MAX - 1; + div = kzalloc(sizeof(*div), GFP_KERNEL); if (!div) return ERR_PTR(-ENOMEM); @@ -678,6 +730,7 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, div->core.layout = layout; div->core.regmap = regmap; div->core.lock = lock; + div->safe_div = safe_div; spin_lock_irqsave(div->core.lock, irqflags); @@ -693,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, if (ret) { kfree(div); hw = ERR_PTR(ret); + } else if (div->safe_div) { + notifier_div = div; + clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier); } return hw; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 45df094498ce..207ecccef29f 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -214,7 +214,8 @@ struct clk_hw * __init sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, const char *name, const char *parent_name, u8 id, const struct clk_pll_characteristics *characteristics, - const struct clk_pll_layout *layout, u32 flags); + const struct clk_pll_layout *layout, u32 flags, + u32 safe_div); struct clk_hw * __init sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock, diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c index 5f6fa89571b7..5c264185f261 100644 --- a/drivers/clk/at91/sam9x60.c +++ b/drivers/clk/at91/sam9x60.c @@ -242,7 +242,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) * This feeds CPU. It should not * be disabled. */ - CLK_IS_CRITICAL | CLK_SET_RATE_GATE); + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0); if (IS_ERR(hw)) goto err_free; @@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) &pll_div_layout, CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | - CLK_SET_RATE_PARENT); + CLK_SET_RATE_PARENT, 0); if (IS_ERR(hw)) goto err_free; @@ -279,7 +279,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) hw = at91_clk_register_master_div(regmap, "masterck_div", "masterck_pres", &sam9x60_master_layout, &mck_characteristics, &mck_lock, - CLK_SET_RATE_GATE); + CLK_SET_RATE_GATE, 0); if (IS_ERR(hw)) goto err_free; diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c index 970135e19a75..076c8ca6ae06 100644 --- a/drivers/clk/at91/sama7g5.c +++ b/drivers/clk/at91/sama7g5.c @@ -9,7 +9,9 @@ */ #include #include +#include #include +#include #include #include @@ -127,6 +129,8 @@ static const struct clk_pll_characteristics pll_characteristics = { * @t: clock type * @f: clock flags * @eid: export index in sama7g5->chws[] array + * @safe_div: true if intermediate divider need to be set on PRE_RATE_CHANGE + * notification */ static const struct { const char *n; @@ -136,6 +140,7 @@ static const struct { unsigned long f; u8 t; u8 eid; + u8 safe_div; } sama7g5_plls[][PLL_ID_MAX] = { [PLL_ID_CPU] = { { .n = "cpupll_fracck", @@ -156,7 +161,8 @@ static const struct { .t = PLL_TYPE_DIV, /* This feeds CPU. It should not be disabled. */ .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .eid = PMC_CPUPLL, }, + .eid = PMC_CPUPLL, + .safe_div = 1, }, }, [PLL_ID_SYS] = { @@ -871,6 +877,32 @@ static const struct clk_pcr_layout sama7g5_pcr_layout = { .pid_mask = GENMASK(6, 0), }; +static u32 __init sama7g5_get_cpupll_safe_div(void) +{ + struct dev_pm_opp *opp; + struct device *dev; + unsigned long min_rate, max_rate; + + /* We are single core. */ + dev = get_cpu_device(0); + if (!dev) + return 0; + + /* + * Find max and min frequency to determine the best safe divider for + * CPUPLL DIV. + */ + opp = dev_pm_opp_find_freq_floor(dev, &min_rate); + if (IS_ERR(opp)) + return 0; + + opp = dev_pm_opp_find_freq_ceil(dev, &max_rate); + if (IS_ERR(opp)) + return 0; + + return DIV_ROUND_UP_ULL(max_rate, min_rate); +} + static void __init sama7g5_pmc_setup(struct device_node *np) { const char *td_slck_name, *md_slck_name, *mainxtal_name; @@ -880,6 +912,7 @@ static void __init sama7g5_pmc_setup(struct device_node *np) int alloc_mem_size = 0; struct regmap *regmap; struct clk_hw *hw; + u32 safe_div; bool bypass; int i, j; @@ -962,12 +995,17 @@ static void __init sama7g5_pmc_setup(struct device_node *np) break; case PLL_TYPE_DIV: + if (sama7g5_plls[i][j].safe_div) + safe_div = sama7g5_get_cpupll_safe_div(); + else + safe_div = 0; + hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, sama7g5_plls[i][j].n, sama7g5_plls[i][j].p, i, sama7g5_plls[i][j].c, sama7g5_plls[i][j].l, - sama7g5_plls[i][j].f); + sama7g5_plls[i][j].f, safe_div); break; default: -- 2.25.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-17.1 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15C09C433F5 for ; Fri, 17 Sep 2021 12:13:56 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D549361100 for ; Fri, 17 Sep 2021 12:13:55 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org D549361100 Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=microchip.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=hLUjLS00TLbcgtJW5tWUoVSFx9eB9bC+D6FSJ97echs=; b=ID9S0R+Ux4+H9B qUl99PIf+5sbJamUsO/XDnCJ07RdTUgjFbH5nsL3BQedJkGaWLOEq3JUMvpqIuR3T4g8GZwpYY92F nkTHz4bu7B9c6ihVoXZ6RodkKBNU8bRjmbCcy4Crh39zFkUVsDYKPNCao9yIYQ2msfq39iGaEDkXo XPA3z83a3R+DHsoP7eWGJtJXo4QsHt1tR2OBUYq5SFUyFc/LpA4j2XajrdWV0EpC2IPCr0KcdNIut yxtAhzD4m4k8+MgTTaf5Y6PIxQEr8PnHT1Ch/zlkbRLe/KEWQYHPFKW84e7FgpguDp3GwMhdvojog 3jOfQWiE/3kCnGCzBlfQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mRCi8-00DxyR-6z; Fri, 17 Sep 2021 12:11:21 +0000 Received: from esa.microchip.iphmx.com ([68.232.153.233]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mRCeA-00Dw4h-Rw for linux-arm-kernel@lists.infradead.org; Fri, 17 Sep 2021 12:07:17 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1631880435; x=1663416435; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=so0cjzgv6AL0M8IG4SX9vQHUIt6VZtxCRdJ4zCgycSQ=; b=NtaAda8kfb9pTQ+RibRNOJL2pkSNrJKcqToM2IEU6IiOHf+sY/QSvUcU MVRft2ITvm250TvJptKKZveBaAG0vhox83mbYN+YL/LyYeWr8hXijsyMj /kaFY+IrJj5aUOT4/cJIzf74lUS9kIO+Zq60WyqGu1H7Gx5uIP8PE4VUM F5AtNtpw9OiVWsFzkcnFaK2QDzeTOiDaXzAvqofXvH6GZNgc12+5iQZRd PzQOMUyuf2ysZtambtZMEoYdFajdjj572QaCl1enflumNpkMdoVdoFWmO vnSSYsdV6JFJ8D8/OMp6AhW6Spxo/671hJOwOt++0+lmzaCI6H7+/U3He w==; IronPort-SDR: Xx7EC0U9zsI/jW+sIa7H58JAsgNLXFJsrksSMNizRxkU+lQKfs18gk8ssRfWOFFcKmJ8hksll/ 4injne5GXJh4jPTzO/xdqkpaVEI4VBeoQLdxaSeQ5UF1IVJAnm3cEsArOqxr6C0cGUTrx4gEcY l9i3ode2zGtxDjwV/8TPfblDBEu+Ztfj5eXXm/NtK2LF06UMDf0TbDvlWqePuWzRdo/g9s9WD+ EVQJrCle2SKaC926lFB3IuFgv4s6Njx3y45TQerKgyO6Nyg+fnAmScCNLGLbii8oOxVCFGK9VJ vIX/R6eur/8/qwyb15kVHIrn X-IronPort-AV: E=Sophos;i="5.85,301,1624345200"; d="scan'208";a="136356788" Received: from smtpout.microchip.com (HELO email.microchip.com) ([198.175.253.82]) by esa5.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 17 Sep 2021 05:07:14 -0700 Received: from chn-vm-ex03.mchp-main.com (10.10.85.151) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.14; Fri, 17 Sep 2021 05:07:12 -0700 Received: from rob-dk-mpu01.microchip.com (10.10.115.15) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server id 15.1.2176.14 via Frontend Transport; Fri, 17 Sep 2021 05:07:10 -0700 From: Claudiu Beznea To: , , , , CC: , , , Claudiu Beznea Subject: [PATCH v3 11/17] clk: at91: clk-sam9x60-pll: add notifier for div part of PLL Date: Fri, 17 Sep 2021 15:06:36 +0300 Message-ID: <20210917120642.8993-12-claudiu.beznea@microchip.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20210917120642.8993-1-claudiu.beznea@microchip.com> References: <20210917120642.8993-1-claudiu.beznea@microchip.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210917_050715_038384_7AA13F73 X-CRM114-Status: GOOD ( 27.94 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org SAM9X60's PLL which is also part of SAMA7G5 is composed of 2 parts: one fractional part and one divider. On SAMA7G5 the CPU PLL could be changed at run-time to implement DVFS. The hardware clock tree on SAMA7G5 for CPU PLL is as follows: +---- div1 ----------------> cpuck | FRAC PLL ---> DIV PLL -+-> prescaler ---> div0 ---> mck0 The div1 block is not implemented in Linux; on prescaler block it has been discovered a bug on some scenarios and will be removed from Linux in next commits. Thus, the final clock tree that will be used in Linux will be as follows: +-----------> cpuck | FRAC PLL ---> DIV PLL -+-> div0 ---> mck0 It has been proposed in [1] to not introduce a new CPUFreq driver but to overload the proper clock drivers with proper operation such that cpufreq-dt to be used. To accomplish this DIV PLL and div0 implement clock notifiers which applies safe dividers before FRAC PLL is changed. The current commit treats only the DIV PLL by adding a notifier that sets a safe divider on PRE_RATE_CHANGE events. The safe divider is provided by initialization clock code (sama7g5.c) based on the OPP located for device. The div0 is treated in next commits (to keep the changes as clean as possible). [1] https://lore.kernel.org/lkml/20210105104426.4tmgc2l3vyicwedd@vireshk-i7/ Signed-off-by: Claudiu Beznea --- drivers/clk/at91/clk-sam9x60-pll.c | 102 ++++++++++++++++++++++------- drivers/clk/at91/pmc.h | 3 +- drivers/clk/at91/sam9x60.c | 6 +- drivers/clk/at91/sama7g5.c | 42 +++++++++++- 4 files changed, 124 insertions(+), 29 deletions(-) diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c index a73d7c96ce1d..d757003004cb 100644 --- a/drivers/clk/at91/clk-sam9x60-pll.c +++ b/drivers/clk/at91/clk-sam9x60-pll.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -47,12 +48,15 @@ struct sam9x60_div { struct sam9x60_pll_core core; struct at91_clk_pms pms; u8 div; + u8 safe_div; }; #define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw) #define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core) #define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core) +static struct sam9x60_div *notifier_div; + static inline bool sam9x60_pll_ready(struct regmap *regmap, int id) { unsigned int status; @@ -329,6 +333,26 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = { .restore_context = sam9x60_frac_pll_restore_context, }; +/* This function should be called with spinlock acquired. */ +static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div, + bool enable) +{ + struct regmap *regmap = core->regmap; + u32 ena_msk = enable ? core->layout->endiv_mask : 0; + u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0; + + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, + core->layout->div_mask | ena_msk, + (div << core->layout->div_shift) | ena_val); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); + + while (!sam9x60_pll_ready(regmap, core->id)) + cpu_relax(); +} + static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) { struct sam9x60_div *div = to_sam9x60_div(core); @@ -346,17 +370,7 @@ static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) if (!!(val & core->layout->endiv_mask) && cdiv == div->div) goto unlock; - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, - core->layout->div_mask | core->layout->endiv_mask, - (div->div << core->layout->div_shift) | - (1 << core->layout->endiv_shift)); - - regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, - AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, - AT91_PMC_PLL_UPDT_UPDATE | core->id); - - while (!sam9x60_pll_ready(regmap, core->id)) - cpu_relax(); + sam9x60_div_pll_set_div(core, div->div, 1); unlock: spin_unlock_irqrestore(core->lock, flags); @@ -502,16 +516,7 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate, if (cdiv == div->div) goto unlock; - regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, - core->layout->div_mask, - (div->div << core->layout->div_shift)); - - regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, - AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, - AT91_PMC_PLL_UPDT_UPDATE | core->id); - - while (!sam9x60_pll_ready(regmap, core->id)) - cpu_relax(); + sam9x60_div_pll_set_div(core, div->div, 0); unlock: spin_unlock_irqrestore(core->lock, irqflags); @@ -538,6 +543,48 @@ static void sam9x60_div_pll_restore_context(struct clk_hw *hw) sam9x60_div_pll_set(core); } +static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier, + unsigned long code, void *data) +{ + struct sam9x60_div *div = notifier_div; + struct sam9x60_pll_core core = div->core; + struct regmap *regmap = core.regmap; + unsigned long irqflags; + u32 val, cdiv; + int ret = NOTIFY_DONE; + + if (code != PRE_RATE_CHANGE) + return ret; + + /* + * We switch to safe divider to avoid overclocking of other domains + * feed by us while the frac PLL (our parent) is changed. + */ + div->div = div->safe_div; + + spin_lock_irqsave(core.lock, irqflags); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + core.id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); + cdiv = (val & core.layout->div_mask) >> core.layout->div_shift; + + /* Stop if nothing changed. */ + if (cdiv == div->safe_div) + goto unlock; + + sam9x60_div_pll_set_div(&core, div->div, 0); + ret = NOTIFY_OK; + +unlock: + spin_unlock_irqrestore(core.lock, irqflags); + + return ret; +} + +static struct notifier_block sam9x60_div_pll_notifier = { + .notifier_call = sam9x60_div_pll_notifier_fn, +}; + static const struct clk_ops sam9x60_div_pll_ops = { .prepare = sam9x60_div_pll_prepare, .unprepare = sam9x60_div_pll_unprepare, @@ -647,7 +694,8 @@ struct clk_hw * __init sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, const char *name, const char *parent_name, u8 id, const struct clk_pll_characteristics *characteristics, - const struct clk_pll_layout *layout, u32 flags) + const struct clk_pll_layout *layout, u32 flags, + u32 safe_div) { struct sam9x60_div *div; struct clk_hw *hw; @@ -656,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, unsigned int val; int ret; - if (id > PLL_MAX_ID || !lock) + /* We only support one changeable PLL. */ + if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div)) return ERR_PTR(-EINVAL); + if (safe_div >= PLL_DIV_MAX) + safe_div = PLL_DIV_MAX - 1; + div = kzalloc(sizeof(*div), GFP_KERNEL); if (!div) return ERR_PTR(-ENOMEM); @@ -678,6 +730,7 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, div->core.layout = layout; div->core.regmap = regmap; div->core.lock = lock; + div->safe_div = safe_div; spin_lock_irqsave(div->core.lock, irqflags); @@ -693,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, if (ret) { kfree(div); hw = ERR_PTR(ret); + } else if (div->safe_div) { + notifier_div = div; + clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier); } return hw; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 45df094498ce..207ecccef29f 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -214,7 +214,8 @@ struct clk_hw * __init sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, const char *name, const char *parent_name, u8 id, const struct clk_pll_characteristics *characteristics, - const struct clk_pll_layout *layout, u32 flags); + const struct clk_pll_layout *layout, u32 flags, + u32 safe_div); struct clk_hw * __init sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock, diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c index 5f6fa89571b7..5c264185f261 100644 --- a/drivers/clk/at91/sam9x60.c +++ b/drivers/clk/at91/sam9x60.c @@ -242,7 +242,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) * This feeds CPU. It should not * be disabled. */ - CLK_IS_CRITICAL | CLK_SET_RATE_GATE); + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0); if (IS_ERR(hw)) goto err_free; @@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) &pll_div_layout, CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | - CLK_SET_RATE_PARENT); + CLK_SET_RATE_PARENT, 0); if (IS_ERR(hw)) goto err_free; @@ -279,7 +279,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) hw = at91_clk_register_master_div(regmap, "masterck_div", "masterck_pres", &sam9x60_master_layout, &mck_characteristics, &mck_lock, - CLK_SET_RATE_GATE); + CLK_SET_RATE_GATE, 0); if (IS_ERR(hw)) goto err_free; diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c index 970135e19a75..076c8ca6ae06 100644 --- a/drivers/clk/at91/sama7g5.c +++ b/drivers/clk/at91/sama7g5.c @@ -9,7 +9,9 @@ */ #include #include +#include #include +#include #include #include @@ -127,6 +129,8 @@ static const struct clk_pll_characteristics pll_characteristics = { * @t: clock type * @f: clock flags * @eid: export index in sama7g5->chws[] array + * @safe_div: true if intermediate divider need to be set on PRE_RATE_CHANGE + * notification */ static const struct { const char *n; @@ -136,6 +140,7 @@ static const struct { unsigned long f; u8 t; u8 eid; + u8 safe_div; } sama7g5_plls[][PLL_ID_MAX] = { [PLL_ID_CPU] = { { .n = "cpupll_fracck", @@ -156,7 +161,8 @@ static const struct { .t = PLL_TYPE_DIV, /* This feeds CPU. It should not be disabled. */ .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .eid = PMC_CPUPLL, }, + .eid = PMC_CPUPLL, + .safe_div = 1, }, }, [PLL_ID_SYS] = { @@ -871,6 +877,32 @@ static const struct clk_pcr_layout sama7g5_pcr_layout = { .pid_mask = GENMASK(6, 0), }; +static u32 __init sama7g5_get_cpupll_safe_div(void) +{ + struct dev_pm_opp *opp; + struct device *dev; + unsigned long min_rate, max_rate; + + /* We are single core. */ + dev = get_cpu_device(0); + if (!dev) + return 0; + + /* + * Find max and min frequency to determine the best safe divider for + * CPUPLL DIV. + */ + opp = dev_pm_opp_find_freq_floor(dev, &min_rate); + if (IS_ERR(opp)) + return 0; + + opp = dev_pm_opp_find_freq_ceil(dev, &max_rate); + if (IS_ERR(opp)) + return 0; + + return DIV_ROUND_UP_ULL(max_rate, min_rate); +} + static void __init sama7g5_pmc_setup(struct device_node *np) { const char *td_slck_name, *md_slck_name, *mainxtal_name; @@ -880,6 +912,7 @@ static void __init sama7g5_pmc_setup(struct device_node *np) int alloc_mem_size = 0; struct regmap *regmap; struct clk_hw *hw; + u32 safe_div; bool bypass; int i, j; @@ -962,12 +995,17 @@ static void __init sama7g5_pmc_setup(struct device_node *np) break; case PLL_TYPE_DIV: + if (sama7g5_plls[i][j].safe_div) + safe_div = sama7g5_get_cpupll_safe_div(); + else + safe_div = 0; + hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, sama7g5_plls[i][j].n, sama7g5_plls[i][j].p, i, sama7g5_plls[i][j].c, sama7g5_plls[i][j].l, - sama7g5_plls[i][j].f); + sama7g5_plls[i][j].f, safe_div); break; default: -- 2.25.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel