Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753314AbcDZTjz (ORCPT ); Tue, 26 Apr 2016 15:39:55 -0400 Received: from anholt.net ([50.246.234.109]:55225 "EHLO anholt.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752758AbcDZTjx (ORCPT ); Tue, 26 Apr 2016 15:39:53 -0400 From: Eric Anholt To: Michael Turquette , Stephen Boyd Cc: linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Stephen Warren , Lee Jones , Martin Sperl , Eric Anholt Subject: [PATCH 2/2] clk: bcm2835: Skip PLLC clocks when deciding on a new clock parent Date: Tue, 26 Apr 2016 12:39:45 -0700 Message-Id: <1461699585-6649-2-git-send-email-eric@anholt.net> X-Mailer: git-send-email 2.8.0.rc3 In-Reply-To: <1461699585-6649-1-git-send-email-eric@anholt.net> References: <1461699585-6649-1-git-send-email-eric@anholt.net> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 2343 Lines: 69 If the firmware had set up a clock to source from PLLC, go along with it. But if we're looking for a new parent, we don't want to switch it to PLLC because the firmware will force PLLC (and thus the AXI bus clock) to different frequencies during over-temp/under-voltage, without notification to Linux. On my system, this moves the Linux-enabled HDMI state machine and DSI1 escape clock over to plld_per from pllc_per. EMMC still ends up on pllc_per, because the firmware had set it up to use that. Signed-off-by: Eric Anholt Fixes: 41691b8862e2 ("clk: bcm2835: Add support for programming the audio domain clocks") --- drivers/clk/bcm/clk-bcm2835.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 14f3066194ac..870c5745d649 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1035,16 +1035,28 @@ static int bcm2835_clock_set_rate(struct clk_hw *hw, return 0; } +static bool +bcm2835_clk_is_pllc(struct clk_hw *hw) +{ + if (!hw) + return false; + + return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0; +} + 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; unsigned long prate, best_prate = 0; size_t i; u32 div; + current_parent_is_pllc = bcm2835_clk_is_pllc(clk_hw_get_parent(hw)); + /* * Select parent clock that results in the closest but lower rate */ @@ -1052,6 +1064,17 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw, parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; + + /* + * Don't choose a PLLC-derived clock as our parent + * unless it had been manually set that way. PLLC's + * frequency gets adjusted by the firmware due to + * over-temp or under-voltage conditions, without + * prior notification to our clock consumer. + */ + 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); -- 2.8.0.rc3