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@xxxxxxxxx> > --- > 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:
signature.asc
Description: Digital signature