From patchwork Thu Aug 6 20:51:10 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hermann Kraus X-Patchwork-Id: 6962741 Return-Path: X-Original-To: patchwork-linux-spi@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 E72D8C05AC for ; Thu, 6 Aug 2015 20:51:19 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EF7EB203EC for ; Thu, 6 Aug 2015 20:51:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 05A8120437 for ; Thu, 6 Aug 2015 20:51:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932393AbbHFUvQ (ORCPT ); Thu, 6 Aug 2015 16:51:16 -0400 Received: from mail-wi0-f182.google.com ([209.85.212.182]:36987 "EHLO mail-wi0-f182.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932392AbbHFUvN (ORCPT ); Thu, 6 Aug 2015 16:51:13 -0400 Received: by wibhh20 with SMTP id hh20so40152864wib.0 for ; Thu, 06 Aug 2015 13:51:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=content-type:to:cc:subject:references:date:mime-version :content-transfer-encoding:from:message-id:in-reply-to:user-agent; bh=TTqvJgbpujy55Fi8zHRiqQ77yacJpyIVhFDqoFjOuiI=; b=oJhOthg4Dmx4D40rHZCyrRn91QGHYscVLkU3vBx38YIgda8FpZRxHDfOY9VzsAvY5K kYKdMjWPUWu+THigXguSUEgDr2uFnQGt2QJAhmMxBfBkHKLg7CTgj2QIgiOPASO8hj6V CDQxP1VZJlpnGf9Q7wlSWJomIaAoxP0r/kESxMeKYDKtzJGKG5MBelhGOb0EbkvcIpGJ 6oWBc0eDm/BWs/IYBQwbpNusth0VfcGEJT7oxSZ/eMaa6mMBMP/BfSQfRABixD763WSc MLaUTqmn8doTQ9zrNVLv6ZUgeU9Zxv/wP4xCdPoS4DNc+1U7DOtzXB+th+w642zn3KsW yAhw== X-Received: by 10.194.58.71 with SMTP id o7mr7431230wjq.82.1438894272433; Thu, 06 Aug 2015 13:51:12 -0700 (PDT) Received: from localhost ([2a02:810d:2cbf:f6d8:1d80:814:f3ea:604e]) by smtp.gmail.com with ESMTPSA id dz4sm5067100wib.17.2015.08.06.13.51.11 (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 06 Aug 2015 13:51:11 -0700 (PDT) To: "Mark Brown" Cc: linux-spi@vger.kernel.org, linux-arm-kernel@lists.infradead.org, "Maxime Ripard" Subject: Re: [PATCH v2] spi: spi-sun4i, spi-sun6i: Fix bit rate calculation References: <20150804171931.GZ20873@sirena.org.uk> Date: Thu, 06 Aug 2015 22:51:10 +0200 MIME-Version: 1.0 From: "Hermann Kraus" Message-ID: In-Reply-To: <20150804171931.GZ20873@sirena.org.uk> User-Agent: Opera Mail/12.16 (Linux) Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, 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 Use requested bit rate instead of maximum possible and correctly calculate the divider. There are two different maximum bitrates. "max_speed_hz" which is the maximum for a given SPI device and "speed_hz" in "struct spi_transfer" which is the rate used for this transfer. "speed_hz" must always be used as the actual transfer rate. The divider must be calculated by calling ilog2 only once, because this function truncates the fractional part. Calling it twice increases the error so much that certain bit rates which are available from the hardware can't be reached by the kernel. The result of this calculation must be checked to fit into the register size instead of being truncated silently. Signed-off-by: Hermann Kraus --- Changes to v1: Unconditionally use tfr->speed_hz as requested by Mark Brown. drivers/spi/spi-sun4i.c | 15 +++++++++++---- drivers/spi/spi-sun6i.c | 15 +++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) @@ -236,14 +236,21 @@ static int sun6i_spi_transfer_one(struct spi_master *master, * First try CDR2, and if we can't reach the expected * frequency, fall back to CDR1. */ - div = mclk_rate / (2 * spi->max_speed_hz); + div = mclk_rate / (2 * tfr->speed_hz); if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { if (div > 0) div--; reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS; } else { - div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + /* ilog2 truncates the fractional part. Therefore we don't + * subtract 1 from the result. */ + div = ilog2(mclk_rate / tfr->speed_hz + 1); + if (div & ~SUN6I_CLK_CTL_CDR1_MASK) { + /* Can't reach low enough bit rate. */ + ret = -EINVAL; + goto out; + } reg = SUN6I_CLK_CTL_CDR1(div); } diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index fbb0a4d..729d70b 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -229,8 +229,8 @@ static int sun4i_spi_transfer_one(struct spi_master *master, /* Ensure that we have a parent clock fast enough */ mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * spi->max_speed_hz)) { - clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); mclk_rate = clk_get_rate(sspi->mclk); } @@ -248,14 +248,21 @@ static int sun4i_spi_transfer_one(struct spi_master *master, * First try CDR2, and if we can't reach the expected * frequency, fall back to CDR1. */ - div = mclk_rate / (2 * spi->max_speed_hz); + div = mclk_rate / (2 * tfr->speed_hz); if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { if (div > 0) div--; reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; } else { - div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + /* ilog2 truncates the fractional part. Therefore we don't + * subtract 1 from the result. */ + div = ilog2(mclk_rate / tfr->speed_hz + 1); + if (div & ~SUN4I_CLK_CTL_CDR1_MASK) { + /* Can't reach low enough bit rate. */ + ret = -EINVAL; + goto out; + } reg = SUN4I_CLK_CTL_CDR1(div); } diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index ac48f59..2d3dcda 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -217,8 +217,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, /* Ensure that we have a parent clock fast enough */ mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * spi->max_speed_hz)) { - clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); mclk_rate = clk_get_rate(sspi->mclk); }