On Mon, 27 Jun 2022 23:43:25 -0400 Jesse Taube <mr.bossman...@gmail.com> wrote:
Hi Jesse, > On 6/27/22 20:31, Andre Przywara wrote: > > On Tue, 3 May 2022 22:20:35 +0100 > > Andre Przywara <andre.przyw...@arm.com> wrote: > > > > Hi, > > > >> As George rightfully pointed out [1], the spi-sunxi driver programs the > >> speed and mode settings only when the respective functions are called, > >> but this gets lost over a call to release_bus(). That asserts the > >> reset line, thus forces each SPI register back to its default value. > >> Adding to that, trying to program SPI_CCR and SPI_TCR might be pointless > >> in the first place, when the reset line is still asserted (before > >> claim_bus()), so those setting won't apply most of the time. In reality > >> I see two nested claim_bus() calls for the first use, so settings between > >> the two would work (for instance for the initial "sf probe"). However > >> later on the speed setting is not programmed into the hardware anymore. > > > > So this issue was addressed with patches by both George (earlier, in a > > different way) and Qianfan (later, in a very similar way). > > > > Can someone (Jagan?) please have a look at this change from the U-Boot > > SPI perspective? And also the other changes in this series? > > I pushed them to the sunxi/next branch: > > https://source.denx.de/u-boot/custodians/u-boot-sunxi/-/commits/next/ > > > > So can people please test this and report whether this now works as > > expected? > > I'm very confused I have forgotten much about this patch set. > I'm going to test it, but why has it only been merged now? Because no one reviewed it, or even replied to it - hence my email. And apparently it was even broken. The code quality in U-Boot is not really great, as the problems fixed with this series and the other problems emerged lately ([1]) demonstrate. So I am very reluctant to take any patches without a meaningful review, or at least without confirmation of their functionality - regardless of the author. For instance I just found a bug in my patch 4/7 ... And I didn't really merge them yet, I just pushed them to the sunxi/next branch, to expose them more widely and encourage testing - which apparently worked out as planned ;-) Cheers, Andre [1] https://lore.kernel.org/u-boot/20220711165215.218e2...@donnerap.cambridge.arm.com/ > >> So far we get away with that default frequency, because that is a rather > >> tame 24 MHz, which most SPI flash chips can handle just fine. > >> > >> Move the actual register programming into a separate function, and use > >> .set_speed and .set_mode just to set the variables in our priv structure. > >> Then we only call this new function in claim_bus(), when we are sure > >> that register accesses actually work and are preserved. > >> > >> [1] https://lore.kernel.org/u-boot/20210725231636.879913-17...@yifangu.com/ > >> > >> Signed-off-by: Andre Przywara <andre.przyw...@arm.com> > >> Reported-by: George Hilliard <thirtythreefo...@gmail.com> > >> --- > >> drivers/spi/spi-sunxi.c | 95 ++++++++++++++++++++++------------------- > >> 1 file changed, 52 insertions(+), 43 deletions(-) > >> > >> diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c > >> index b6cd7ddafad..d6b2dd09514 100644 > >> --- a/drivers/spi/spi-sunxi.c > >> +++ b/drivers/spi/spi-sunxi.c > >> @@ -221,6 +221,56 @@ err_ahb: > >> return ret; > >> } > >> > >> +static void sun4i_spi_set_speed_mode(struct udevice *dev) > >> +{ > >> + struct sun4i_spi_priv *priv = dev_get_priv(dev); > >> + unsigned int div; > >> + u32 reg; > >> + > >> + /* > >> + * Setup clock divider. > >> + * > >> + * 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)) > >> + * Or we can use CDR2, which is calculated with the formula: > >> + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) > >> + * Whether we use the former or the latter is set through the > >> + * DRS bit. > >> + * > >> + * First try CDR2, and if we can't reach the expected > >> + * frequency, fall back to CDR1. > >> + */ > >> + > >> + div = SUN4I_SPI_MAX_RATE / (2 * priv->freq); > >> + reg = readl(SPI_REG(priv, SPI_CCR)); > >> + > >> + if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { > >> + if (div > 0) > >> + div--; > >> + > >> + reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS); > >> + reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; > >> + } else { > >> + div = __ilog2(SUN4I_SPI_MAX_RATE) - __ilog2(priv->freq); > >> + reg &= ~((SUN4I_CLK_CTL_CDR1_MASK << 8) | SUN4I_CLK_CTL_DRS); > >> + reg |= SUN4I_CLK_CTL_CDR1(div); > >> + } > >> + > >> + writel(reg, SPI_REG(priv, SPI_CCR)); > >> + > >> + reg = readl(SPI_REG(priv, SPI_TCR)); > >> + reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA)); > >> + > >> + if (priv->mode & SPI_CPOL) > >> + reg |= SPI_BIT(priv, SPI_TCR_CPOL); > >> + > >> + if (priv->mode & SPI_CPHA) > >> + reg |= SPI_BIT(priv, SPI_TCR_CPHA); > >> + > >> + writel(reg, SPI_REG(priv, SPI_TCR)); > >> +} > >> + > >> static int sun4i_spi_claim_bus(struct udevice *dev) > >> { > >> struct sun4i_spi_priv *priv = dev_get_priv(dev->parent); > >> @@ -240,6 +290,8 @@ static int sun4i_spi_claim_bus(struct udevice *dev) > >> setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) | > >> SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW)); > >> > >> + sun4i_spi_set_speed_mode(dev->parent); > >> + > >> return 0; > >> } > >> > >> @@ -329,46 +381,14 @@ static int sun4i_spi_set_speed(struct udevice *dev, > >> uint speed) > >> { > >> struct sun4i_spi_plat *plat = dev_get_plat(dev); > >> struct sun4i_spi_priv *priv = dev_get_priv(dev); > >> - unsigned int div; > >> - u32 reg; > >> > >> if (speed > plat->max_hz) > >> speed = plat->max_hz; > >> > >> if (speed < SUN4I_SPI_MIN_RATE) > >> speed = SUN4I_SPI_MIN_RATE; > >> - /* > >> - * Setup clock divider. > >> - * > >> - * 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)) > >> - * Or we can use CDR2, which is calculated with the formula: > >> - * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) > >> - * Whether we use the former or the latter is set through the > >> - * DRS bit. > >> - * > >> - * First try CDR2, and if we can't reach the expected > >> - * frequency, fall back to CDR1. > >> - */ > >> - > >> - div = SUN4I_SPI_MAX_RATE / (2 * speed); > >> - reg = readl(SPI_REG(priv, SPI_CCR)); > >> - > >> - if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { > >> - if (div > 0) > >> - div--; > >> - > >> - reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS); > >> - reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; > >> - } else { > >> - div = __ilog2(SUN4I_SPI_MAX_RATE) - __ilog2(speed); > >> - reg &= ~((SUN4I_CLK_CTL_CDR1_MASK << 8) | SUN4I_CLK_CTL_DRS); > >> - reg |= SUN4I_CLK_CTL_CDR1(div); > >> - } > >> > >> priv->freq = speed; > >> - writel(reg, SPI_REG(priv, SPI_CCR)); > >> > >> return 0; > >> } > >> @@ -376,19 +396,8 @@ static int sun4i_spi_set_speed(struct udevice *dev, > >> uint speed) > >> static int sun4i_spi_set_mode(struct udevice *dev, uint mode) > >> { > >> struct sun4i_spi_priv *priv = dev_get_priv(dev); > >> - u32 reg; > >> - > >> - reg = readl(SPI_REG(priv, SPI_TCR)); > >> - reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA)); > >> - > >> - if (mode & SPI_CPOL) > >> - reg |= SPI_BIT(priv, SPI_TCR_CPOL); > >> - > >> - if (mode & SPI_CPHA) > >> - reg |= SPI_BIT(priv, SPI_TCR_CPHA); > >> > >> priv->mode = mode; > >> - writel(reg, SPI_REG(priv, SPI_TCR)); > >> > >> return 0; > >> } > > >