From patchwork Mon Apr 13 23:03:21 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Norris X-Patchwork-Id: 6212301 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id C576DBF4A6 for ; Mon, 13 Apr 2015 23:04:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C0818202E9 for ; Mon, 13 Apr 2015 23:04:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 93862202DD for ; Mon, 13 Apr 2015 23:04:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751848AbbDMXEI (ORCPT ); Mon, 13 Apr 2015 19:04:08 -0400 Received: from mail-pa0-f47.google.com ([209.85.220.47]:33521 "EHLO mail-pa0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751524AbbDMXEH (ORCPT ); Mon, 13 Apr 2015 19:04:07 -0400 Received: by paboj16 with SMTP id oj16so117211650pab.0; Mon, 13 Apr 2015 16:04:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=qTdO+wc6/5/AumjxqdKirXHhX4+FN418LHMkw36j1xA=; b=MDNAjmZvLAfr39IcZjoJ5PO24zf68iUIMdAyWvrQley/zieTy/nJFIq3ZGsMPgMonB sQK0WsLqnWEvMw58v5Pl5DxnMbW5vyYmelCOob5i0kLuBih5NmeFQ70lXUM9ZsxYL/xd Y1X/OMxorDF4oM0aClJoa75gUbuNJ/lhTOlhdW2K9u1dLQCjR4MXhz26a9dkgc1/UOhT cAaeBdz4jQgsbDnyB+lz/nqGnmaxN7h3qSFxqYWcAgvN9n78A15tVVi0FjtNE+SGETGS N8fXHV002J6QKVJ7y7E3XQBAi1mkJA7SgVnTh59gYVQD5qkxlukEo9ZHlFvEcOB+V8tv cm7g== X-Received: by 10.70.89.138 with SMTP id bo10mr31291152pdb.146.1428966247045; Mon, 13 Apr 2015 16:04:07 -0700 (PDT) Received: from ld-irv-0074.broadcom.com (5520-maca-inet1-outside.broadcom.com. [216.31.211.11]) by mx.google.com with ESMTPSA id kn7sm8388849pab.10.2015.04.13.16.04.05 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 13 Apr 2015 16:04:06 -0700 (PDT) From: Brian Norris To: Michael Turquette Cc: Brian Norris , Florian Fainelli , Jim Quinlan , Gregory Fong , Stephen Boyd , linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Sascha Hauer , Tomi Valkeinen , u.kleine-koenig@pengutronix.de Subject: [PATCH] clk: divider: handle integer overflow when dividing large clock rates Date: Mon, 13 Apr 2015 16:03:21 -0700 Message-Id: <1428966201-23365-1-git-send-email-computersforpeace@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_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 On 32-bit architectures, 'unsigned long' (the type used to hold clock rates, in Hz) is often only 32 bits wide. DIV_ROUND_UP() (as used in, e.g., commit b11d282dbea2 "clk: divider: fix rate calculation for fractional rates") can yield an integer overflow on clock rates that are not (by themselves) too large to fit in 32 bits, because it performs addition before the division. See for example: DIV_ROUND_UP(3000000000, 1500000000) = (3.0G + 1.5G - 1) / 1.5G = OVERFLOW / 1.5G This patch fixes such cases by always promoting the dividend to 64-bits (unsigned long long) before doing the division. While this patch does not resolve the issue with large clock rates across the common clock framework nor address the problems with doing full 64-bit arithmetic on a 32-bit architecture, it does fix some issues seen when using clock dividers on a 3GHz reference clock to produce a 1.5GHz CPU clock for an ARMv7 Brahma B15 SoC. Signed-off-by: Brian Norris Reference: lkml.kernel.org/g/20150413201433.GQ32500@ld-irv-0074 --- I'll admit I only compile-tested this particular patch. I have tested a version of this patch on top of a few backports on an older kernel, and everything works fine. Unforunately, some of my SoC's clock drivers still rely on out-of-tree code. drivers/clk/clk-divider.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 25006a8bb8e6..2928bc361506 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -24,7 +24,7 @@ * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared * enable - clk_enable only ensures that parents are enabled - * rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor) + * rate - rate is adjustable. clk->rate = ceiling(parent->rate / divisor) * parent - fixed parent. No clk_set_parent support */ @@ -127,7 +127,7 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, return parent_rate; } - return DIV_ROUND_UP(parent_rate, div); + return DIV_ROUND_UP_ULL((u64)parent_rate, div); } EXPORT_SYMBOL_GPL(divider_recalc_rate); @@ -205,7 +205,7 @@ static int _div_round_up(const struct clk_div_table *table, unsigned long parent_rate, unsigned long rate, unsigned long flags) { - int div = DIV_ROUND_UP(parent_rate, rate); + int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); if (flags & CLK_DIVIDER_POWER_OF_TWO) div = __roundup_pow_of_two(div); @@ -222,7 +222,7 @@ static int _div_round_closest(const struct clk_div_table *table, int up, down; unsigned long up_rate, down_rate; - up = DIV_ROUND_UP(parent_rate, rate); + up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); down = parent_rate / rate; if (flags & CLK_DIVIDER_POWER_OF_TWO) { @@ -233,8 +233,8 @@ static int _div_round_closest(const struct clk_div_table *table, down = _round_down_table(table, down); } - up_rate = DIV_ROUND_UP(parent_rate, up); - down_rate = DIV_ROUND_UP(parent_rate, down); + up_rate = DIV_ROUND_UP_ULL((u64)parent_rate, up); + down_rate = DIV_ROUND_UP_ULL((u64)parent_rate, down); return (rate - up_rate) <= (down_rate - rate) ? up : down; } @@ -313,7 +313,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), rate * i); - now = DIV_ROUND_UP(parent_rate, i); + now = DIV_ROUND_UP_ULL((u64)parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv = i; best = now; @@ -337,7 +337,7 @@ long divider_round_rate(struct clk_hw *hw, unsigned long rate, div = clk_divider_bestdiv(hw, rate, prate, table, width, flags); - return DIV_ROUND_UP(*prate, div); + return DIV_ROUND_UP_ULL((u64)*prate, div); } EXPORT_SYMBOL_GPL(divider_round_rate); @@ -352,7 +352,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, bestdiv = readl(divider->reg) >> divider->shift; bestdiv &= div_mask(divider->width); bestdiv = _get_div(divider->table, bestdiv, divider->flags); - return DIV_ROUND_UP(*prate, bestdiv); + return DIV_ROUND_UP_ULL((u64)*prate, bestdiv); } return divider_round_rate(hw, rate, prate, divider->table, @@ -365,7 +365,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, { unsigned int div, value; - div = DIV_ROUND_UP(parent_rate, rate); + div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); if (!_is_valid_div(table, div, flags)) return -EINVAL;