Re: [PATCH v6 2/3] spi: sun4i: Fix clock calculations to be predictable and never exceed the requested rate

Maxime Ripard
2015-12-27T21:09:53Z

[-- Attachment #1: Type: text/plain, Size: 3416 bytes --]

On Sat, Dec 26, 2015 at 04:53:05PM +0100, Marcus Weseloh wrote:
> This patch fixes multiple problems with the current clock calculations:
>
> 1. The A10/A20 datasheet contains the formula AHB_CLK / (2^(n+1)) to
> calculate SPI_CLK from CDR1, but this formula is wrong. The actual
> formula - determined by analyzing the actual waveforms - is
> AHB_CLK / (2^n).
>
> 2. The divisor calculations for CDR1 and CDR2 both rounded to the
> nearest integer. This could lead to a transfer speed that is higher than
> the requested speed. This patch changes both calculations to always
> round down.
>
> 3. The mclk frequency was only ever increased, never decreased. This could
> lead to unpredictable transfer speeds, depending on the order in which
> transfers with different speeds where serviced by the SPI driver.

These 3 should probably be separate patches (and be Cc'd to stable

> Signed-off-by: Marcus Weseloh <mweseloh42@gmail.com>
> ---
> drivers/spi/spi-sun4i.c | 26 ++++++++++++++++----------
> 1 file changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c
> index f60a6d6..d67e142 100644
> --- a/drivers/spi/spi-sun4i.c
> +++ b/drivers/spi/spi-sun4i.c
> @@ -79,6 +79,9 @@ struct sun4i_spi {
> struct clk *hclk;
> struct clk *mclk;
>
> + int cur_max_speed;
> + int cur_mclk;
> +
> struct completion done;
>
> const u8 *tx_buf;
> @@ -227,11 +230,17 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
>
> sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
>
> - /* Ensure that we have a parent clock fast enough */
> + /*
> + * Ensure that the parent clock is set to twice the max speed
> + * of the spi device (possibly rounded up by the clk driver)
> + */
> mclk_rate = clk_get_rate(sspi->mclk);
> - if (mclk_rate < (2 * tfr->speed_hz)) {
> - clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
> + if (spi->max_speed_hz != sspi->cur_max_speed ||
> + mclk_rate != sspi->cur_mclk) {

Do you need to cache the values? As far as I'm aware, you end up doing
the computation all the time anyway.

> + clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
> mclk_rate = clk_get_rate(sspi->mclk);
> + sspi->cur_mclk = mclk_rate;
> + sspi->cur_max_speed = spi->max_speed_hz;
> }
>
> /*
> @@ -239,7 +248,7 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
> *
> * We have two choices there. Either we can use the clock
> * divide rate 1, which is calculated thanks to this formula:
> - * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
> + * SPI_CLK = MOD_CLK / (2 ^ cdr)
>
> * Or we can use CDR2, which is calculated with the formula:
> * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
> * Wether we use the former or the latter is set through the
> @@ -248,14 +257,11 @@ 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 * tfr->speed_hz);
> - if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
> - if (div > 0)
> - div--;
> -
> + div = DIV_ROUND_UP(mclk_rate, 2 * tfr->speed_hz) - 1;

Isn't it exactly the same thing as mclk_rate / (2 * tfr->speed_hz) ?

Thanks,
Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]