From patchwork Sat Aug 1 22:06:31 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hermann Kraus X-Patchwork-Id: 6922931 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 CC3B4C05AC for ; Sat, 1 Aug 2015 22:06:39 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A417420480 for ; Sat, 1 Aug 2015 22:06:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 840662047C for ; Sat, 1 Aug 2015 22:06:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751463AbbHAWGf (ORCPT ); Sat, 1 Aug 2015 18:06:35 -0400 Received: from mail-wi0-f176.google.com ([209.85.212.176]:38497 "EHLO mail-wi0-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751442AbbHAWGe (ORCPT ); Sat, 1 Aug 2015 18:06:34 -0400 Received: by wibxm9 with SMTP id xm9so71132155wib.1 for ; Sat, 01 Aug 2015 15:06:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=content-type:date:to:subject:mime-version:content-transfer-encoding :from:message-id:user-agent; bh=vB1u4KdlWvtzgdzLm06x5Xpy5AoSqdCMkw0onp6xQiU=; b=FTasYSUvLwpizbeOAcyLdxfBQ2MJm3pVMbyQfHKOjtgpisCwft0Lgo/0Wwlxx9cZYG l3hepAWsWDqjWrBfBPt2ntQGg0i3gkyDgVjSzUlG8G+uS8F2MH6fICoO2BU4kNEaf7cM 0S32n0OPMi48PZlsl2hfEuebjnvqR0+NTyKx7y3oGiNkReN4NUB8JEg6nWDy0KJprPV7 SCYeYuBrfzBoqEdmWUY9UIaM7OJwjUWkkWW0a76jAOot5MjaORn7EMgEHMO+XJr/RcXy sQiDHd/MzPRl2KfDNx9yAU5J2RBoBlzU5FSHA+sWM+q7n51365Soyei7b07aMWjalmaI M9mw== X-Received: by 10.194.179.167 with SMTP id dh7mr20851949wjc.15.1438466793317; Sat, 01 Aug 2015 15:06:33 -0700 (PDT) Received: from localhost ([2a02:810d:2cbf:f6d8:cc87:a7fe:e170:3e63]) by smtp.gmail.com with ESMTPSA id l2sm5117358wib.11.2015.08.01.15.06.32 (version=TLSv1 cipher=RC4-SHA bits=128/128); Sat, 01 Aug 2015 15:06:32 -0700 (PDT) Date: Sun, 02 Aug 2015 00:06:31 +0200 To: linux-spi@vger.kernel.org, linux-arm-kernel@lists.infradead.org, "Mark Brown" , "Maxime Ripard" Subject: [PATCH] spi: spi-sun4i, spi-sun6i: Fix bit rate calculation MIME-Version: 1.0 From: "Hermann Kraus" Message-ID: 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=-7.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. If "speed_hz" is non-zero it must be used. 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 --- drivers/spi/spi-sun4i.c | 16 ++++++++++++---- drivers/spi/spi-sun6i.c | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) if (tfr->len > SUN6I_FIFO_DEPTH) @@ -217,8 +218,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 * speed_hz)) { + clk_set_rate(sspi->mclk, 2 * speed_hz); mclk_rate = clk_get_rate(sspi->mclk); } @@ -236,14 +237,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 * 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 / 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..82d717d 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -173,6 +173,7 @@ static int sun4i_spi_transfer_one(struct spi_master *master, unsigned int tx_len = 0; int ret = 0; u32 reg; + u32 speed_hz = tfr->speed_hz ? : spi->max_speed_hz; /* We don't support transfer larger than the FIFO */ if (tfr->len > SUN4I_FIFO_DEPTH) @@ -229,8 +230,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 * speed_hz)) { + clk_set_rate(sspi->mclk, 2 * speed_hz); mclk_rate = clk_get_rate(sspi->mclk); } @@ -248,14 +249,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 * 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 / 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..c3870be 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -163,6 +163,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, unsigned int tx_len = 0; int ret = 0; u32 reg; + u32 speed_hz = tfr->speed_hz ? : spi->max_speed_hz; /* We don't support transfer larger than the FIFO */