From patchwork Fri Jan 15 14:21:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sperl X-Patchwork-Id: 8041371 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id DA98A9F6FA for ; Fri, 15 Jan 2016 14:27:01 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EBFB020444 for ; Fri, 15 Jan 2016 14:27:00 +0000 (UTC) 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.kernel.org (Postfix) with ESMTPS id 09C2F20430 for ; Fri, 15 Jan 2016 14:27:00 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1aK5Jd-0004ms-IC; Fri, 15 Jan 2016 14:25:25 +0000 Received: from 212-186-180-163.dynamic.surfer.at ([212.186.180.163] helo=cgate.sperl.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1aK5GG-0000Pn-92; Fri, 15 Jan 2016 14:21:59 +0000 Received: from raspcm.intern.sperl.org (account martin@sperl.org [10.10.10.41] verified) by sperl.org (CommuniGate Pro SMTP 6.1.2) with ESMTPSA id 6391711; Fri, 15 Jan 2016 14:21:15 +0000 From: kernel@martin.sperl.org To: Michael Turquette , Stephen Boyd , Stephen Warren , Lee Jones , Eric Anholt , Remi Pommarel , linux-clk@vger.kernel.org, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH V4 7/7] clk: bcm2835: apply limits on dividers to MASH mode. Date: Fri, 15 Jan 2016 14:21:06 +0000 Message-Id: <1452867667-2447-8-git-send-email-kernel@martin.sperl.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1452867667-2447-1-git-send-email-kernel@martin.sperl.org> References: <1452867667-2447-1-git-send-email-kernel@martin.sperl.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160115_062157_037570_89E5F56F X-CRM114-Status: GOOD ( 12.45 ) X-Spam-Score: -0.9 (/) 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: Martin Sperl MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Martin Sperl There are several limits on the divider as well as effective frequency that apply when a fractional divider with higher order MASH is used. This patch applies all the information about limits that is available at this time and all of which has been tested empirically by Mathias Reichl using primarily the pcm clock. The patch tries to use the "highest" order of MASH support and when it fails it reduces MASH order and retries the test - fall back all the way to integer divide if necessary, where "normal" clamping of limits happens. Note that http://www.aholme.co.uk/Frac2/Mash.htm contains a description of MASH, the author - allegedly - was working for Broadcom at that time. Suggested-by: Mathias Reichl Signed-off-by: Martin Sperl --- drivers/clk/bcm/clk-bcm2835.c | 52 ++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) -- 1.7.10.4 diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 545c21a..637f8ae 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -300,6 +300,7 @@ #define LOCK_TIMEOUT_NS 100000000 #define BCM2835_MAX_FB_RATE 1750000000u +#define BCM2835_MASH_MAX_FREQ 25000000u enum bcm2835_clock_mash_type { MASH_NONE = 0, @@ -1489,7 +1490,9 @@ static divmash bcm2835_clock_choose_div(struct clk_hw *hw, u64 temp = (u64)parent_rate << CM_DIV_FRAC_BITS; enum bcm2835_clock_mash_type mash = MASH_NONE; u64 rem; - u32 div; + u32 div, divi, divf; + const u32 divi_max = BIT(data->int_bits) - 1; + const u32 divi_min = 2; rem = do_div(temp, rate); div = temp; @@ -1499,20 +1502,47 @@ static divmash bcm2835_clock_choose_div(struct clk_hw *hw, div += unused_frac_mask + 1; div &= ~unused_frac_mask; - /* Clamp to the limits. */ + divi = div >> CM_DIV_FRAC_BITS; + divf = div & GENMASK(CM_DIV_FRAC_BITS - 1, 0); - /* divider must be >= 2 */ - div = max_t(u32, div, (2 << CM_DIV_FRAC_BITS)); + /* select mash mode */ + if (data->frac_bits && divf) + mash = data->mash ? data->mash : MASH_FRAC; - /* clamp to max divider allowed - max is integer divider */ - div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, - CM_DIV_FRAC_BITS)); + /* + * handle possible limits for different mash levels with fall-tru + * For offset values see page 105 table 6-32 in + * BCM2835-ARM-Peripherials as well as the errata at: + * http://elinux.org/BCM2835_datasheet_errata#p105_table + */ + switch (mash) { + case MASH_3RD_ORDER: + if ((divi >= divi_min + 3) && + (divi + 4 <= divi_max) && + (parent_rate / (divi - 3) <= BCM2835_MASH_MAX_FREQ)) + return divmash_calc(MASH_3RD_ORDER, div); + /* fall tru if not in bounds */ + case MASH_2ND_ORDER: + if ((divi >= divi_min + 1) && + (divi + 2 <= divi_max) && + (parent_rate / (divi - 1) <= BCM2835_MASH_MAX_FREQ)) + return divmash_calc(MASH_2ND_ORDER, div); + /* fall tru if not in bounds */ + case MASH_FRAC: + if ((divi >= divi_min) && + (divi + 1 <= divi_max)) + return divmash_calc(MASH_FRAC, div); + /* fall tru if not in bounds */ + case MASH_NONE: + default: + break; + } - /* set mash if necessary */ - if (data->frac_bits && (div & GENMASK(CM_DIV_FRAC_BITS - 1, 0))) - mash = data->mash ? data->mash : MASH_FRAC; + /* we apply standard clamping based on divi alone */ + divi = max(divi, divi_min); + divi = min(divi, divi_max); - return divmash_calc(mash, div); + return divmash_calc(MASH_NONE, divi << CM_DIV_FRAC_BITS); } static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,