From patchwork Thu Dec 1 21:00:19 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris BREZILLON X-Patchwork-Id: 9456915 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 969C860585 for ; Thu, 1 Dec 2016 21:05:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 89B7128537 for ; Thu, 1 Dec 2016 21:05:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7E4AA28539; Thu, 1 Dec 2016 21:05:12 +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, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 7C70B28537 for ; Thu, 1 Dec 2016 21:05:06 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1cCYTs-0004n6-3E; Thu, 01 Dec 2016 21:01:24 +0000 Received: from mail.free-electrons.com ([62.4.15.54]) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1cCYTL-0003zQ-2s; Thu, 01 Dec 2016 21:00:53 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 8ED0A21D0A; Thu, 1 Dec 2016 22:00:27 +0100 (CET) Received: from bbrezillon.home (LFbn-1-2159-240.w90-76.abo.wanadoo.fr [90.76.216.240]) by mail.free-electrons.com (Postfix) with ESMTPSA id 4366521CF8; Thu, 1 Dec 2016 22:00:27 +0100 (CET) From: Boris Brezillon To: Mike Turquette , Stephen Boyd , linux-clk@vger.kernel.org Subject: [PATCH 1/2] clk: bcm: Support rate change propagation on bcm2835 clocks Date: Thu, 1 Dec 2016 22:00:19 +0100 Message-Id: <1480626020-20031-2-git-send-email-boris.brezillon@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1480626020-20031-1-git-send-email-boris.brezillon@free-electrons.com> References: <1480626020-20031-1-git-send-email-boris.brezillon@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161201_130051_518026_399A602F X-CRM114-Status: GOOD ( 17.75 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Boris Brezillon , Florian Fainelli , Scott Branden , Stephen Warren , Ray Jui , Lee Jones , Eric Anholt , bcm-kernel-feedback-list@broadcom.com, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 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 Some peripheral clocks, like the VEC (Video EnCoder) clock need to be set to a precise rate (in our case 108MHz). With the current implementation, where peripheral clocks are not allowed to forward rate change requests to their parents, it is impossible to match this requirement unless the bootloader has configured things correctly, or a specific rate has been assigned through the DT (with the assigned-clk-rates property). Add a new field to struct bcm2835_clock_data to specify which parent clocks accept rate change propagation, and support set rate propagation in bcm2835_clock_determine_rate(). Signed-off-by: Boris Brezillon Reviewed-by: Eric Anholt --- Note: this approach is still fragile, since it does not prevent 2 clock users from stepping on each other's toes. We could use the clk rate constraints infrastructure, but it's only applicable to leaf clocks in the clock tree (when you apply a constraint, these constraints are not propagated to the clock tree). Another approach would be to lock a rate on a clock, thus preventing other users from changing it. Propagating rate locks would be easier than propagating rate range constraints (see this patch http://code.bulix.org/jwtzf8-107264), but it still requires that really care about a specific clock rate explicitly lock the rate (using clk_set_and_lock_rate()), and this means checking every drivers that have such requirements. --- drivers/clk/bcm/clk-bcm2835.c | 67 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 2acaa77ad482..df96fe6dadab 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -436,6 +436,9 @@ struct bcm2835_clock_data { const char *const *parents; int num_mux_parents; + /* Bitmap encoding which parents accept rate change propagation. */ + unsigned int set_rate_parent; + u32 ctl_reg; u32 div_reg; @@ -1017,10 +1020,60 @@ bcm2835_clk_is_pllc(struct clk_hw *hw) return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0; } +static unsigned long bcm2835_clock_choose_div_and_prate(struct clk_hw *hw, + int parent_idx, + unsigned long rate, + u32 *div, + unsigned long *prate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + unsigned long best_rate; + u32 curdiv, mindiv, maxdiv; + struct clk_hw *parent; + + parent = clk_hw_get_parent_by_index(hw, parent_idx); + + if (!(BIT(parent_idx) & data->set_rate_parent)) { + *prate = clk_hw_get_rate(parent); + *div = bcm2835_clock_choose_div(hw, rate, *prate, true); + + return bcm2835_clock_rate_from_divisor(clock, *prate, + *div); + } + + if (data->frac_bits) + dev_warn(cprman->dev, + "frac bits are not used when propagating rate change"); + + /* clamp to min divider of 2 if we're dealing with a mash clock */ + mindiv = data->is_mash_clock ? 2 : 1; + maxdiv = BIT(data->int_bits) - 1; + + /* TODO: Be smart, and only test a subset of the available divisors. */ + for (curdiv = mindiv; curdiv <= maxdiv; curdiv++) { + unsigned long tmp_rate; + + tmp_rate = clk_hw_round_rate(parent, rate * curdiv); + tmp_rate /= curdiv; + if (curdiv == mindiv || + (tmp_rate > best_rate && tmp_rate <= rate)) + best_rate = tmp_rate; + + if (best_rate == rate) + break; + } + + *div = curdiv << CM_DIV_FRAC_BITS; + *prate = curdiv * best_rate; + + return best_rate; +} + static int bcm2835_clock_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); struct clk_hw *parent, *best_parent = NULL; bool current_parent_is_pllc; unsigned long rate, best_rate = 0; @@ -1048,9 +1101,8 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw, if (bcm2835_clk_is_pllc(parent) && !current_parent_is_pllc) continue; - prate = clk_hw_get_rate(parent); - div = bcm2835_clock_choose_div(hw, req->rate, prate, true); - rate = bcm2835_clock_rate_from_divisor(clock, prate, div); + rate = bcm2835_clock_choose_div_and_prate(hw, i, req->rate, + &div, &prate); if (rate > best_rate && rate <= req->rate) { best_parent = parent; best_prate = prate; @@ -1262,6 +1314,13 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman, init.name = data->name; init.flags = data->flags | CLK_IGNORE_UNUSED; + /* + * Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate + * rate changes on at least of the parents. + */ + if (data->set_rate_parent) + init.flags |= CLK_SET_RATE_PARENT; + if (data->is_vpu_clock) { init.ops = &bcm2835_vpu_clock_clk_ops; } else {