u-boot.lists.denx.de archive mirror
 help / color / mirror / Atom feed
From: Andre Przywara <andre.przywara@arm.com>
To: Jagan Teki <jagan@amarulasolutions.com>
Cc: Jesse Taube <mr.bossman075@gmail.com>,
	Icenowy Zheng <icenowy@aosc.io>, Yifan Gu <me@yifangu.com>,
	Giulio Benetti <giulio.benetti@benettiengineering.com>,
	George Hilliard <thirtythreeforty@gmail.com>,
	Samuel Holland <samuel@sholland.org>,
	Jernej Skrabec <jernej.skrabec@gmail.com>,
	linux-sunxi@lists.linux.dev, u-boot@lists.denx.de
Subject: [PATCH 2/7] spi: sunxi: refactor SPI speed/mode programming
Date: Tue,  3 May 2022 22:20:35 +0100	[thread overview]
Message-ID: <20220503212040.27884-3-andre.przywara@arm.com> (raw)
In-Reply-To: <20220503212040.27884-1-andre.przywara@arm.com>

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 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-me@yifangu.com/

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reported-by: George Hilliard <thirtythreeforty@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;
 }
-- 
2.35.3


  parent reply	other threads:[~2022-05-03 21:21 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-03 21:20 [PATCH 0/7] sunxi: F1C100s: enable MMC and SPI in U-Boot proper Andre Przywara
2022-05-03 21:20 ` [PATCH 1/7] clk: sunxi: implement clock driver for suniv f1c100s Andre Przywara
2022-05-24 16:10   ` Andre Przywara
2022-05-03 21:20 ` Andre Przywara [this message]
2022-06-28  0:31   ` [PATCH 2/7] spi: sunxi: refactor SPI speed/mode programming Andre Przywara
2022-06-28  3:43     ` Jesse Taube
2022-07-18 10:17       ` Andre Przywara
2022-06-30  3:25     ` Jesse Taube
2022-05-03 21:20 ` [PATCH 3/7] spi: sunxi: improve SPI clock calculation Andre Przywara
2022-05-03 21:20 ` [PATCH 4/7] spi: sunxi: Add support for F1C100s SPI controller Andre Przywara
2022-05-03 21:20 ` [PATCH 5/7] sunxi: F1C100s: update DT files from Linux Andre Przywara
2022-05-05 11:26   ` Jesse Taube
2022-05-24 16:11   ` Andre Przywara
2022-05-03 21:20 ` [PATCH 6/7] Revert "sunxi: f1c100s: Drop SYSRESET to enable reset functionality" Andre Przywara
2022-05-24 16:11   ` Andre Przywara
2022-05-03 21:20 ` [PATCH 7/7] sunxi: licheepi_nano: enable SPI flash Andre Przywara

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220503212040.27884-3-andre.przywara@arm.com \
    --to=andre.przywara@arm.com \
    --cc=giulio.benetti@benettiengineering.com \
    --cc=icenowy@aosc.io \
    --cc=jagan@amarulasolutions.com \
    --cc=jernej.skrabec@gmail.com \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=me@yifangu.com \
    --cc=mr.bossman075@gmail.com \
    --cc=samuel@sholland.org \
    --cc=thirtythreeforty@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).