From patchwork Tue Mar 24 15:43:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Shevchenko X-Patchwork-Id: 6080831 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 C7CB0BF90F for ; Tue, 24 Mar 2015 15:43:38 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A755B2024F for ; Tue, 24 Mar 2015 15:43:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EF8E020254 for ; Tue, 24 Mar 2015 15:43:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753137AbbCXPnd (ORCPT ); Tue, 24 Mar 2015 11:43:33 -0400 Received: from mga02.intel.com ([134.134.136.20]:9996 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753083AbbCXPna (ORCPT ); Tue, 24 Mar 2015 11:43:30 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga101.jf.intel.com with ESMTP; 24 Mar 2015 08:43:25 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.11,458,1422950400"; d="scan'208";a="684885653" Received: from black.fi.intel.com ([10.237.72.86]) by fmsmga001.fm.intel.com with ESMTP; 24 Mar 2015 08:43:24 -0700 Received: by black.fi.intel.com (Postfix, from userid 1003) id BE5925F1; Tue, 24 Mar 2015 17:43:22 +0200 (EET) From: Andy Shevchenko To: Mark Brown , linux-spi@vger.kernel.org, "Chen, Alvin" , Jarkko Nikula Cc: Andy Shevchenko Subject: [PATCH v1 2/2] spi: pxa2xx: replace ugly table by approximation Date: Tue, 24 Mar 2015 17:43:22 +0200 Message-Id: <1427211802-217454-3-git-send-email-andriy.shevchenko@linux.intel.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1427211802-217454-1-git-send-email-andriy.shevchenko@linux.intel.com> References: <1427211802-217454-1-git-send-email-andriy.shevchenko@linux.intel.com> 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, RCVD_IN_DNSWL_HI, 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 The Quark SoC data sheet describes the baud rate setting using fractional divider. The subset of possible values represented by a table suggests that the divisor has one block that could divide by 5. This explains a satan number in some cases in the table Thus, in this particular case the divisor can be evaluated as 5^i * 2^j * 2 * k, where i = [0, 1] j = [0, 23] k = [1, 256] There are few special cases as mentioned in the data sheet, i.e. better form of the clock signal will be in case if DDS_CLK_RATE either 2^n or 2/5. It's also possible to use any value that is less or equal to 0x33333 (1/5/16 = 1/80). All three cases are compared to each other and the one that suits better is chosen by approximation algorithm. Anyone can play with the script [1] that represents the algorithm. [1] https://gist.github.com/06b084488b3629898121 Signed-off-by: Andy Shevchenko --- drivers/spi/spi-pxa2xx.c | 151 ++++++++++++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 62 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index dd56bf5..6212802 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -67,54 +68,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_TX_LOTHRESH_DFLT 160 #define LPSS_TX_HITHRESH_DFLT 224 -struct quark_spi_rate { - u32 bitrate; - u32 dds_clk_rate; - u32 clk_div; -}; - -/* - * 'rate', 'dds', 'clk_div' lookup table, which is defined in - * the Quark SPI datasheet. - */ -static const struct quark_spi_rate quark_spi_rate_table[] = { -/* bitrate, dds_clk_rate, clk_div */ - {50000000, 0x800000, 0}, - {40000000, 0x666666, 0}, - {25000000, 0x400000, 0}, - {20000000, 0x666666, 1}, - {16667000, 0x800000, 2}, - {13333000, 0x666666, 2}, - {12500000, 0x200000, 0}, - {10000000, 0x800000, 4}, - {8000000, 0x666666, 4}, - {6250000, 0x400000, 3}, - {5000000, 0x400000, 4}, - {4000000, 0x666666, 9}, - {3125000, 0x80000, 0}, - {2500000, 0x400000, 9}, - {2000000, 0x666666, 19}, - {1563000, 0x40000, 0}, - {1250000, 0x200000, 9}, - {1000000, 0x400000, 24}, - {800000, 0x666666, 49}, - {781250, 0x20000, 0}, - {625000, 0x200000, 19}, - {500000, 0x400000, 49}, - {400000, 0x666666, 99}, - {390625, 0x10000, 0}, - {250000, 0x400000, 99}, - {200000, 0x666666, 199}, - {195313, 0x8000, 0}, - {125000, 0x100000, 49}, - {100000, 0x200000, 124}, - {50000, 0x100000, 124}, - {25000, 0x80000, 124}, - {10016, 0x20000, 77}, - {5040, 0x20000, 154}, - {1002, 0x8000, 194}, -}; - /* Offset from drv_data->lpss_base */ #define GENERAL_REG 0x08 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) @@ -701,25 +654,99 @@ static irqreturn_t ssp_int(int irq, void *dev_id) } /* - * The Quark SPI data sheet gives a table, and for the given 'rate', - * the 'dds' and 'clk_div' can be found in the table. + * The Quark SPI has an additional 24 bit register to multiply input frequency + * by fractions of 2^24. It also has a divider by 5. + * + * Fssp = Fsys * DDS_CLK_RATE / 2**24 + * Baud rate = Fsclk = Fssp / (2 * (SCR + 1)) + * + * DDS_CLK_RATE either 2^n or 2^n / 5. + * SCR is in range 0 .. 255 + * + * Divisor = 5^i * 2^j * 2 * k + * i = [0, 1] i = 1 iff j = 0 or j > 3 + * j = [0, 23] j = 0 iff i = 1 + * k = [1, 256] + * Special case: j = 0, i = 1: Divisor = 2 / 5 + * + * We can also specify any value of DDS_CLK_RATE that is less or equal 1 / 80, + * i.e. 0x33333. */ -static u32 quark_x1000_set_clk_regvals(u32 rate, u32 *dds, u32 *clk_div) +static unsigned int quark_x1000_get_clk_div(int rate, u32 *dds) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(quark_spi_rate_table); i++) { - if (rate >= quark_spi_rate_table[i].bitrate) { - *dds = quark_spi_rate_table[i].dds_clk_rate; - *clk_div = quark_spi_rate_table[i].clk_div; - return quark_spi_rate_table[i].bitrate; + /* Quark SoC has 200MHz xtal */ + unsigned long xtal = 200000000, fref = xtal / 2; + /* Define two reference frequencies */ + unsigned long fref1 = fref / 2, fref2 = fref * 2 / 5; + /* Keep quots and remainders for different cases here */ + unsigned long q, q1, q2; + unsigned long scale; + long r, r1, r2; + u32 mul, mul1; + + /* Set initial value for DDS_CLK_RATE */ + mul1 = (1 << 24) >> 1; + + /* Calculate initial quot */ + q1 = DIV_ROUND_CLOSEST(fref1, rate); + + /* Scale q1 if it's too big */ + if (q1 > 256) { + /* Scale q1 to range [1, 512] */ + scale = fls_long(q1 - 1); + if (scale > 9) { + q1 >>= scale - 9; + mul1 >>= scale - 9; } + + /* Round the result if we have a remainder */ + q1 += q1 & 1; } - *dds = quark_spi_rate_table[i-1].dds_clk_rate; - *clk_div = quark_spi_rate_table[i-1].clk_div; + /* Decrease DDS_CLK_RATE as much as we can without loss in precision */ + scale = __ffs(q1); + q1 >>= scale; + mul1 >>= scale; + + /* Get the remainder */ + r1 = abs(fref1 / (1 << (24 - fls_long(mul1))) / q1 - rate); + + q2 = DIV_ROUND_CLOSEST(fref2, rate); + r2 = abs(fref2 / q2 - rate); + + /* Choose the best between two */ + if (r2 >= r1 || q2 > 256) { + r = r1; + q = q1; + mul = mul1; + } else { + r = r2; + q = q2; + mul = (1 << 24) * 2 / 5; /* 0x666666 */ + } + + /* In case the divisor is big enough */ + if (fref / rate >= 80) { + u64 fssp; + + /* Calculate initial quot */ + q1 = DIV_ROUND_CLOSEST(fref, rate); + mul1 = (1 << 24) / q1; + + /* Get the remainder */ + fssp = (u64)fref * mul1; + do_div(fssp, 1 << 24); + r1 = abs(fssp - rate); + + /* Choose this one if it suits better */ + if (r1 < r) { + q = 1; + mul = mul1; + } + } - return quark_spi_rate_table[i-1].bitrate; + *dds = mul; + return q - 1; } static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) @@ -742,7 +769,7 @@ static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - quark_x1000_set_clk_regvals(rate, &chip->dds_rate, &clk_div); + clk_div = quark_x1000_get_clk_div(rate, &chip->dds_rate); default: clk_div = ssp_get_clk_div(drv_data, rate); }