From patchwork Fri Dec 22 03:42:05 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dong Aisheng X-Patchwork-Id: 10128747 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id BFCF460390 for ; Fri, 22 Dec 2017 03:42:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A7CB529BE1 for ; Fri, 22 Dec 2017 03:42:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9C65F29EBC; Fri, 22 Dec 2017 03:42:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, DKIM_VALID, FREEMAIL_FROM, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 926CD29BE1 for ; Fri, 22 Dec 2017 03:42:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:In-Reply-To:MIME-Version:References: Message-ID:Subject:To:From:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=1YZojxlCPDOUgAJIDU/8aApb+Y7/tdJKUOm28ZzhB14=; b=VxGpe06/PaSPxq w8xv8v+1Q7XdfdTtGNB02v3FhEygvyI8iC8a/098sU7VoQtCP1w0+03xG5he2LQ4s7KRhXZKvYf2u QT4lmQ+7BYHbzeDRJcqkYOxcKIUvyORj+iiUTxaJCRMPRogotsIwrgJkTNyVurlqkQI6lOpLC+wge tXX+5xOkxrlVfs8sx12gfClEaNeX+DHMS7rvpSRfPyDPKhUx9CFRJHncjLY7oqFJXK7o4kVKDsJVY BYX1o0GYOilB/RuFvHkXveIDnPKRce6BpGXaB3gyzpGwNk9GO5cjQYNWu08z8PiqogQouszFdkK0B 95bQFXHp/BRde77H9C3w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eSEED-0003eC-53; Fri, 22 Dec 2017 03:42:33 +0000 Received: from mail-pl0-x241.google.com ([2607:f8b0:400e:c01::241]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1eSEE7-0003c3-BT for linux-arm-kernel@lists.infradead.org; Fri, 22 Dec 2017 03:42:30 +0000 Received: by mail-pl0-x241.google.com with SMTP id o2so12229169plk.12 for ; Thu, 21 Dec 2017 19:42:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=actSECyW90MBkhGaP/l9D/NjPHYZv5cjgDrJPugZtDY=; b=lrcI46C3NTB4A8JKKlbh+xJA1peVlp/ycKldzQGPJdT3QW9IecG9BcbMTvZUeIYE// kCQ8YtM5AOQBCxIVVY5lJA9sfttIWW1QXj2shqgRLD+E1pqp41dYmMqQZIAdqYZOt88e 12cLH+g56xQeJVDbpwjs2Qb91QAaht2Mxuj5P1CAMxHkcpW7RSCnX9N7/TLt+Qd/Xw6s Id4oSaYKs0ej11+GG2NU+TIZ1vuhFsyH2KIzGXXSeag4aX5ki+8yuZDdoOCrqQD2FI8c 8IFyijQ51XGaVxP6AGTG3DkdIp6VrCufUr7Zrihcyq+hDyu09txLVPLwRvYIlEhVIsrX g1vQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=actSECyW90MBkhGaP/l9D/NjPHYZv5cjgDrJPugZtDY=; b=ok1rowSWTQ0nI3UsyTre8y2tfdaqi0nyxZzCrQBCpT4Rq6WE62PI7eiuzC9ORB7mk0 FzcAMPIc9A+Lyxjq3oElm4DWX/nP07tR4dJvvX52aqh8ZnU4asZolcpB2ohQNqakUgbg bsvlV1sEJ2yFBMlNx1Wsm9FRtZVXI3OA4CY9sLIvfhoGGGGqA287HStcFriYNmpoFbS8 oOeTzoOoCNmxaDonlBvEKJyyERRGpveX4pDnj3SdajNcEMh6kUFZ5fx1X7u+FW4AtMN5 RnG7bFNieeyK9MfdSxRReD/1KqfgupWe0QJZbvUJUL+khwPVBZxUOCb8RdpNBGZoAQNk NPcg== X-Gm-Message-State: AKGB3mJeSDoyMeFC8bg+0VAbj7CK/u6oV739o2X5BxaEatFhIJM+ezyx 78HN67KlH0WNqYmA6NBLCE980Q== X-Google-Smtp-Source: ACJfBosBWYifXATgz7fcWjhJUL8HrWS7+0H9OPJ8p7JsGILORnAAh4slM+rnFBUjBlt/vNiBDyg+0A== X-Received: by 10.84.137.169 with SMTP id 38mr12589223pln.36.1513914136509; Thu, 21 Dec 2017 19:42:16 -0800 (PST) Received: from b29396-OptiPlex-7040 (gate-zmy3.freescale.com. [192.88.167.1]) by smtp.gmail.com with ESMTPSA id t14sm34124594pgr.37.2017.12.21.19.42.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 21 Dec 2017 19:42:15 -0800 (PST) Date: Fri, 22 Dec 2017 11:42:05 +0800 From: Dong Aisheng To: Stephen Boyd Subject: Re: [PATCH V2 01/10] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support Message-ID: <20171222034205.GD24403@b29396-OptiPlex-7040> References: <1499946435-7177-1-git-send-email-aisheng.dong@nxp.com> <1499946435-7177-2-git-send-email-aisheng.dong@nxp.com> <20171102075039.GU30645@codeaurora.org> <20171220142757.GB30461@b29396-OptiPlex-7040> <20171222012401.GB7997@codeaurora.org> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20171222012401.GB7997@codeaurora.org> User-Agent: Mutt/1.5.24 (2015-08-30) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20171221_194227_445127_8888D78E X-CRM114-Status: GOOD ( 38.40 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Dong Aisheng , ping.bai@nxp.com, Anson.Huang@nxp.com, mturquette@baylibre.com, linux-kernel@vger.kernel.org, shawnguo@kernel.org, linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP On Thu, Dec 21, 2017 at 05:24:01PM -0800, Stephen Boyd wrote: > On 12/20, Dong Aisheng wrote: > > On Thu, Nov 02, 2017 at 12:50:39AM -0700, Stephen Boyd wrote: > > > On 07/13, Dong Aisheng wrote: > > > > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > > > > index 9bb472c..55f8c41 100644 > > > > --- a/drivers/clk/clk-divider.c > > > > +++ b/drivers/clk/clk-divider.c > > > > @@ -123,6 +123,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, > > > > struct clk_divider *divider = to_clk_divider(hw); > > > > unsigned int div; > > > > > > > > + if (flags & CLK_DIVIDER_ZERO_GATE && !val) > > > > + return 0; > > > > + > > > > div = _get_div(table, val, flags, divider->width); > > > > if (!div) { > > > > WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO), > > > > @@ -141,8 +144,13 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, > > > > struct clk_divider *divider = to_clk_divider(hw); > > > > unsigned int val; > > > > > > > > - val = clk_readl(divider->reg) >> divider->shift; > > > > - val &= div_mask(divider->width); > > > > + if ((divider->flags & CLK_DIVIDER_ZERO_GATE) && > > > > + !clk_hw_is_enabled(hw)) { > > > > > > This seems racy. Are we holding the register lock here? > > > > > > > Would you please clarify what type of racy you mean? > > I mean a race between clk_enable() and clk_set_rate(). A > clk_enable() can happen while a rate change is going on, and then > the clk_hw_is_enabled() check here would be racing, unless this > driver specifically tries to prevent that from happening by > holding a spinlock somewhere. > Will this race cause real problems as clk_divider_is_enabled is only a register read? And seems either clk_hw_is_enable or __clk_is_enabled is allowed to be called by anywhere currently. So it may be none of calling in set_rate or recalc_rate issue. If they may be race with clk_enable/disable function, seems they may be better protected by the core. And i did see some clarify in Documentation/clk.txt: "The enable lock is a spinlock and is held across calls to the .enable, .disable and .is_enabled operations. Those operations are thus not allowed to sleep, and calls to the clk_enable(), clk_disable() and clk_is_enabled() API functions are allowed in atomic context." Then how about do like below: > > > > Currently it only protects register write between set_rate and enable/disable, > > and other register read are not protected. > > e.g. in recalc_rate and is_enabled. > > If you're holding some lock that is used to protect the register > writes and also the clk from getting enabled/disabled during a > rate change then it's fine. > Yes, all possible register write are protected. Regards Dong Aisheng > > > > And i did see similar users, e.g. > > drivers/clk/sunxi-ng/ccu_mult.c > > Sure. Those could also be broken. I'm not sure. > > > > > Should we still need protect them here? > > > > > > + val = divider->cached_val; > > > > + } else { > > > > + val = clk_readl(divider->reg) >> divider->shift; > > > > + val &= div_mask(divider->width); > > > > + } > > > > > > > > return divider_recalc_rate(hw, parent_rate, val, divider->table, > > > > divider->flags); > > > > @@ -392,6 +400,12 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, > > > > value = divider_get_val(rate, parent_rate, divider->table, > > > > divider->width, divider->flags); > > > > > > > > + if ((divider->flags & CLK_DIVIDER_ZERO_GATE) && > > > > + !clk_hw_is_enabled(hw)) { > > > > > > Same racy comment here. > > > > > > > + divider->cached_val = value; > > > > + return 0; > > > > + } > > > > + > > > > if (divider->lock) > > > > spin_lock_irqsave(divider->lock, flags); > > > > else > > > > @@ -414,10 +428,85 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, > > > > return 0; > > > > } > > > > > > > > +static int clk_divider_enable(struct clk_hw *hw) > > > > +{ > > > > + struct clk_divider *divider = to_clk_divider(hw); > > > > + unsigned long flags = 0; > > > > + u32 val; > > > > + > > > > + if (!(divider->flags & CLK_DIVIDER_ZERO_GATE)) > > > > + return 0; > > > > > > This is not good. We will always jump to these functions on > > > enable/disable for a divider although 99.9% of all dividers that > > > exist won't need to run this code at all. > > > > > > > I absolutely understand this concern. > > > > > Can you please move this logic into your own divider > > > implementation? The flag can be added to the generic layer if > > > necessary but I'd prefer to see this logic kept in the driver > > > that uses it. If we get more than one driver doing the cached > > > divider thing then we can think about moving it to the more > > > generic place like here, but for now we should be able to keep > > > this contained away from the basic types and handled by the > > > quirky driver that needs it. > > > > > > > If only for above issue, how about invent a clk_divider_gate_ops > > to separate the users of normal divider and zero gate divider: > > > > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > > index 4ed516c..b51f3f9 100644 > > --- a/drivers/clk/clk-divider.c > > +++ b/drivers/clk/clk-divider.c > > @@ -125,6 +125,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, > > > > div = _get_div(table, val, flags, divider->width); > > if (!div) { > > + if (flags & CLK_DIVIDER_ZERO_GATE) > > + return 0; > > + > > WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO), > > "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", > > clk_hw_get_name(hw)); > > @@ -148,6 +151,23 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, > > divider->flags); > > } > > > > +static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw, > > + unsigned long parent_rate) > > +{ > > + struct clk_divider *divider = to_clk_divider(hw); > > + unsigned int val; > > + > > + if (!clk_hw_is_enabled(hw)) { > > + val = divider->cached_val; > > + } else { > > + val = clk_readl(divider->reg) >> divider->shift; > > + val &= div_mask(divider->width); > > + } > > + > > + return divider_recalc_rate(hw, parent_rate, val, divider->table, > > + divider->flags); > > +} > > + > > static bool _is_valid_table_div(const struct clk_div_table *table, > > unsigned int div) > > { > > @@ -416,6 +436,89 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, > > return 0; > > } > > > > +static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate, > > + unsigned long parent_rate) > > +{ > > + struct clk_divider *divider = to_clk_divider(hw); > > + int value; > > + > > + if (!clk_hw_is_enabled(hw)) { > > + value = divider_get_val(rate, parent_rate, divider->table, > > + divider->width, divider->flags); > > + if (value < 0) > > + return value; > > + > > + divider->cached_val = value; > > + > > + return 0; > > + } > > + > > + return clk_divider_set_rate(hw, rate, parent_rate); > > +} > > + > > +static int clk_divider_enable(struct clk_hw *hw) > > +{ > > + struct clk_divider *divider = to_clk_divider(hw); > > + unsigned long uninitialized_var(flags); > > + u32 val; > > + > > + if (!divider->cached_val) { > > + pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw)); > > + return -EINVAL; > > + } > > + > > + if (divider->lock) > > + spin_lock_irqsave(divider->lock, flags); > > + else > > + __acquire(divider->lock); > > + > > + /* restore div val */ > > + val = clk_readl(divider->reg); > > + val |= divider->cached_val << divider->shift; > > + clk_writel(val, divider->reg); > > + > > + if (divider->lock) > > + spin_unlock_irqrestore(divider->lock, flags); > > + else > > + __release(divider->lock); > > + > > + return 0; > > +} > > + > > +static void clk_divider_disable(struct clk_hw *hw) > > +{ > > + struct clk_divider *divider = to_clk_divider(hw); > > + unsigned long uninitialized_var(flags); > > + u32 val; > > + > > + if (divider->lock) > > + spin_lock_irqsave(divider->lock, flags); > > + else > > + __acquire(divider->lock); > > + > > + /* store the current div val */ > > + val = clk_readl(divider->reg) >> divider->shift; > > + val &= div_mask(divider->width); > > + divider->cached_val = val; > > + clk_writel(0, divider->reg); > > + > > + if (divider->lock) > > + spin_unlock_irqrestore(divider->lock, flags); > > + else > > + __release(divider->lock); > > +} > > + > > +static int clk_divider_is_enabled(struct clk_hw *hw) > > +{ > > + struct clk_divider *divider = to_clk_divider(hw); > > + u32 val; > > + > > + val = clk_readl(divider->reg) >> divider->shift; > > + val &= div_mask(divider->width); > > + > > + return val ? 1 : 0; > > +} > > + > > const struct clk_ops clk_divider_ops = { > > .recalc_rate = clk_divider_recalc_rate, > > .round_rate = clk_divider_round_rate, > > @@ -423,6 +526,16 @@ const struct clk_ops clk_divider_ops = { > > }; > > EXPORT_SYMBOL_GPL(clk_divider_ops); > > > > +const struct clk_ops clk_divider_gate_ops = { > > + .recalc_rate = clk_divider_gate_recalc_rate, > > + .round_rate = clk_divider_round_rate, > > + .set_rate = clk_divider_gate_set_rate, > > + .enable = clk_divider_enable, > > + .disable = clk_divider_disable, > > + .is_enabled = clk_divider_is_enabled, > > +}; > > +EXPORT_SYMBOL_GPL(clk_divider_gate_ops); > > + > > const struct clk_ops clk_divider_ro_ops = { > > .recalc_rate = clk_divider_recalc_rate, > > .round_rate = clk_divider_round_rate, > > @@ -438,6 +551,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name, > > struct clk_divider *div; > > struct clk_hw *hw; > > struct clk_init_data init; > > + u32 val; > > int ret; > > > > if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { > > @@ -455,6 +569,8 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name, > > init.name = name; > > if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) > > init.ops = &clk_divider_ro_ops; > > + else if (clk_divider_flags & CLK_DIVIDER_ZERO_GATE) > > + init.ops = &clk_divider_gate_ops; > > else > > init.ops = &clk_divider_ops; > > init.flags = flags | CLK_IS_BASIC; > > @@ -470,6 +586,12 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name, > > div->hw.init = &init; > > div->table = table; > > > > + if (div->flags & CLK_DIVIDER_ZERO_GATE) { > > + val = clk_readl(reg) >> shift; > > + val &= div_mask(width); > > + div->cached_val = val; > > + } > > + > > /* register the clock */ > > hw = &div->hw; > > ret = clk_hw_register(dev, hw); > > diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h > > index 7c925e6..5f33b73 100644 > > --- a/include/linux/clk-provider.h > > +++ b/include/linux/clk-provider.h > > @@ -358,6 +358,7 @@ struct clk_div_table { > > * @shift: shift to the divider bit field > > * @width: width of the divider bit field > > * @table: array of value/divider pairs, last entry should have div = 0 > > + * @cached_val: cached div hw value used for CLK_DIVIDER_ZERO_GATE > > * @lock: register lock > > * > > * Clock with an adjustable divider affecting its output frequency. Implements > > @@ -386,6 +387,12 @@ struct clk_div_table { > > * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like CLK_DIVIDER_ONE_BASED > > * except when the value read from the register is zero, the divisor is > > * 2^width of the field. > > + * CLK_DIVIDER_ZERO_GATE - For dividers which are like CLK_DIVIDER_ONE_BASED > > + * when the value read from the register is zero, it means the divisor > > + * is gated. For this case, the cached_val will be used to store the > > + * intermediate div for the normal rate operation, like set_rate/get_rate/ > > + * recalc_rate. When the divider is ungated, the driver will actually > > + * program the hardware to have the requested divider value. > > */ > > struct clk_divider { > > struct clk_hw hw; > > @@ -394,6 +401,7 @@ struct clk_divider { > > u8 width; > > u8 flags; > > const struct clk_div_table *table; > > + u32 cached_val; > > spinlock_t *lock; > > }; > > > > @@ -406,6 +414,7 @@ struct clk_divider { > > #define CLK_DIVIDER_ROUND_CLOSEST BIT(4) > > #define CLK_DIVIDER_READ_ONLY BIT(5) > > #define CLK_DIVIDER_MAX_AT_ZERO BIT(6) > > +#define CLK_DIVIDER_ZERO_GATE BIT(7) > > > > extern const struct clk_ops clk_divider_ops; > > extern const struct clk_ops clk_divider_ro_ops; > > > > Anyway, if you still think it's not proper, i can put it in platform > > driver as you wish, just in the cost of a few duplicated codes. > > Ok. Keeping it in the basic types but split into different ops > path looks good. > > > > > > > + > > > > + if (!divider->cached_val) { > > > > + pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw)); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + if (divider->lock) > > > > + spin_lock_irqsave(divider->lock, flags); > > > > + else > > > > + __acquire(divider->lock); > > > > + > > > > + /* restore div val */ > > > > + val = clk_readl(divider->reg); > > > > + val |= divider->cached_val << divider->shift; > > > > + clk_writel(val, divider->reg); > > > > + > > > > + if (divider->lock) > > > > + spin_unlock_irqrestore(divider->lock, flags); > > > > + else > > > > + __release(divider->lock); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static void clk_divider_disable(struct clk_hw *hw) > > > > +{ > > > > + struct clk_divider *divider = to_clk_divider(hw); > > > > + unsigned long flags = 0; > > > > + u32 val; > > > > + > > > > + if (!(divider->flags & CLK_DIVIDER_ZERO_GATE)) > > > > + return; > > > > + > > > > + if (divider->lock) > > > > + spin_lock_irqsave(divider->lock, flags); > > > > + else > > > > + __acquire(divider->lock); > > > > + > > > > + /* store the current div val */ > > > > + val = clk_readl(divider->reg) >> divider->shift; > > > > + val &= div_mask(divider->width); > > > > + divider->cached_val = val; > > > > + clk_writel(0, divider->reg); > > > > + > > > > + if (divider->lock) > > > > + spin_unlock_irqrestore(divider->lock, flags); > > > > + else > > > > + __release(divider->lock); > > > > +} > > > > + > > > > +static int clk_divider_is_enabled(struct clk_hw *hw) > > > > +{ > > > > + struct clk_divider *divider = to_clk_divider(hw); > > > > + u32 val; > > > > + > > > > + if (!(divider->flags & CLK_DIVIDER_ZERO_GATE)) > > > > + return __clk_get_enable_count(hw->clk); > > > > > > The plan was to delete this API once OMAP stopped using it. > > > clk_hw_is_enabled() doesn't work? > > > > No, it did not work before because clk_hw_is_enabled will result > > in the dead loop by calling .is_enabled() callback again. > > > > That's why __clk_get_enable_count is used instead. > > > > However, with above new patch method, this issue was gone. > > Great! > > > > > > > > > > + > > > > + val = clk_readl(divider->reg) >> divider->shift; > > > > + val &= div_mask(divider->width); > > > > + > > > > + return val ? 1 : 0; > > > > +} > > > > + > > > > const struct clk_ops clk_divider_ops = { > > > > .recalc_rate = clk_divider_recalc_rate, > > > > .round_rate = clk_divider_round_rate, > > > > .set_rate = clk_divider_set_rate, > > > > + .enable = clk_divider_enable, > > > > + .disable = clk_divider_disable, > > > > + .is_enabled = clk_divider_is_enabled, > > > > }; > > > > EXPORT_SYMBOL_GPL(clk_divider_ops); > > > > > > > > @@ -436,6 +525,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name, > > > > struct clk_divider *div; > > > > struct clk_hw *hw; > > > > struct clk_init_data init; > > > > + u32 val; > > > > int ret; > > > > > > > > if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { > > > > @@ -468,6 +558,12 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name, > > > > div->hw.init = &init; > > > > div->table = table; > > > > > > > > + if (div->flags & CLK_DIVIDER_ZERO_GATE) { > > > > + val = clk_readl(reg) >> shift; > > > > + val &= div_mask(width); > > > > + div->cached_val = val; > > > > + } > > > > > > What if it isn't on? Setting cached_val to 0 is ok? > > > > > > > If it isn't on, then the cache_val should be 0. > > And recalc_rate will catch this case and return 0 as there's > > no proper pre-set rate. > > > > Ok. > > -- > Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, > a Linux Foundation Collaborative Project diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 8a1860a..ce5fa96 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -191,6 +191,7 @@ static bool clk_core_is_prepared(struct clk_core *core) static bool clk_core_is_enabled(struct clk_core *core) { + unsigned long flags; bool ret = false; /* @@ -218,7 +219,10 @@ static bool clk_core_is_enabled(struct clk_core *core) } } + flags = clk_enable_lock(); ret = core->ops->is_enabled(core->hw); + clk_enable_unlock(flags); + done: clk_pm_runtime_put(core);