[RFC PATCH 10/13] spi: dw: Use a mux to access registers

Sean Anderson seanga2 at gmail.com
Fri Feb 5 05:39:20 CET 2021


To enter XIP mode, the xip_en signal must be asserted. The exact method of
setting xip_en is integration-specific, but on the K210 (and Baikal-T1) it
is set by a bit in a system configuration register. To handle this, use a
mux to select the state of xip_en before every access to control registers.

Signed-off-by: Sean Anderson <seanga2 at gmail.com>
---

 drivers/spi/designware_spi.c | 133 ++++++++++++++++++++++++++++++++++-
 1 file changed, 130 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index c41c5b4982..6f74a471e3 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -19,6 +19,7 @@
 #include <fdtdec.h>
 #include <log.h>
 #include <malloc.h>
+#include <mux.h>
 #include <reset.h>
 #include <spi.h>
 #include <spi-mem.h>
@@ -209,6 +210,7 @@ struct dw_spi_priv {
 	struct clk clk;
 	struct reset_ctl_bulk resets;
 	struct gpio_desc cs_gpio;	/* External chip-select gpio */
+	struct mux_control *mux;	/* XIP mode mux */
 
 	void __iomem *regs;
 	fdt_size_t regs_size;
@@ -225,6 +227,7 @@ struct dw_spi_priv {
 	unsigned int freq;		/* Default frequency */
 	unsigned int mode;
 
+	u32 mux_xip_state;		/* Mux state to enable XIP mode */
 	u32 fifo_len;			/* depth of the FIFO buffer */
 
 	int bits_per_word;
@@ -346,11 +349,79 @@ static int dw_spi_of_to_plat(struct udevice *bus)
 	return request_gpio_cs(bus);
 }
 
-/* Restart the controller, disable all interrupts, clean rx fifo */
-static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
+static int dw_spi_mux(struct udevice *dev, bool xip)
 {
+	struct dw_spi_priv *priv = dev_get_priv(dev);
+
+	if (!priv->mux)
+		return 0;
+
+	if (xip && priv->mux_xip_state)
+		return mux_control_select(priv->mux, priv->mux_xip_state);
+	else
+		return mux_control_select(priv->mux, 0);
+}
+
+/*
+ * dw_spi_mux_regs() - Select the control registers using the XIP mux
+ * @dev: The device to mux
+ *
+ * This selects the control registers using the XIP mux, driving the xip_en
+ * signal low. This function must be called before any accesses to control
+ * registers.
+ *
+ * Return: 0 on success or negative error value
+ */
+static inline int dw_spi_mux_regs(struct udevice *dev)
+{
+	return dw_spi_mux(dev, false);
+}
+
+/*
+ * dw_spi_mux_xip() - Select the control registers using the XIP mux
+ * @dev: The device to mux
+ *
+ * This selects XIP mode using the XIP mux, driving the xip_en signal high. This
+ * function must be called before any XIP accesses.
+ *
+ * Return: 0 on success or negative error value
+ */
+static inline int dw_spi_mux_xip(struct udevice *dev)
+{
+	return dw_spi_mux(dev, true);
+}
+
+/*
+ * dw_spi_mux_deselect()
+ * @dev: The device to mux
+ *
+ * This deselects the XIP mux, returning it to its default state. This must be
+ * called after control register or XIP accesses are finished, before other
+ * calls to @dw_spi_mux_regs or @dw_spi_mux_xip.
+ */
+static void dw_spi_mux_deselect(struct udevice *dev)
+{
+	int err;
+	struct dw_spi_priv *priv = dev_get_priv(dev);
+
+	if (!priv->mux)
+		return;
+
+	err = mux_control_deselect(priv->mux);
+	if (err)
+		dev_warn(dev, "could not deselect mux (err %d)\n", err);
+}
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+static int spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
+{
+	int ret;
 	u32 cr0;
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	dw_write(priv, DW_SPI_SSIENR, 0);
 	dw_write(priv, DW_SPI_IMR, 0);
 
@@ -415,6 +486,9 @@ static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
 
 	/* Set receive fifo interrupt level register for clock stretching */
 	dw_write(priv, DW_SPI_RXFTLR, priv->fifo_len - 1);
+
+	dw_spi_mux_deselect(bus);
+	return 0;
 }
 
 /*
@@ -480,6 +554,33 @@ static int dw_spi_reset(struct udevice *bus)
 	return 0;
 }
 
+int dw_spi_get_mux(struct udevice *bus)
+{
+	int ret;
+	struct dw_spi_priv *priv = dev_get_priv(bus);
+
+	ret = mux_get_by_index(bus, 0, &priv->mux);
+	if (ret) {
+		/*
+		 * Return 0 if error due to !CONFIG_MUX or mux
+		 * DT property is not present.
+		 */
+		if (ret == -ENOENT || ret == -ENOTSUPP)
+			return 0;
+
+		dev_warn(bus, "Couldn't get xip mux (error %d)\n", ret);
+		return ret;
+	}
+
+	ret = dev_read_u32(bus, "mux-xip-state", &priv->mux_xip_state);
+	if (ret || priv->mux_xip_state > 1) {
+		dev_warn(bus, "Invalid/missing mux-xip-state property\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int dw_spi_probe(struct udevice *bus)
 {
 	struct dw_spi_plat *plat = dev_get_plat(bus);
@@ -499,6 +600,10 @@ static int dw_spi_probe(struct udevice *bus)
 	if (ret)
 		return ret;
 
+	ret = dw_spi_get_mux(bus);
+	if (ret)
+		return ret;
+
 	/* Currently only bits_per_word == 8 supported */
 	priv->bits_per_word = 8;
 
@@ -506,7 +611,12 @@ static int dw_spi_probe(struct udevice *bus)
 
 	/* Basic HW init */
 	priv->caps = dev_get_driver_data(bus);
-	spi_hw_init(bus, priv);
+	ret = spi_hw_init(bus, priv);
+	if (ret)
+		return ret;
+
+	if (!priv->mux)
+		priv->caps &= DW_SPI_CAP_XIP;
 
 	version = dw_read(priv, DW_SPI_VERSION);
 	dev_dbg(bus,
@@ -713,6 +823,10 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 		return -1;
 	}
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	frames = bitlen / priv->bits_per_word;
 
 	/* Start the transaction if necessary. */
@@ -779,6 +893,8 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	if (flags & SPI_XFER_END)
 		external_cs_manage(dev, true);
 
+	dw_spi_mux_deselect(bus);
+
 	return ret;
 }
 
@@ -831,6 +947,10 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
 	else
 		priv->tmode = CTRLR0_TMOD_TO;
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	cr0 = dw_spi_update_cr0(priv);
 	spi_cr0 = dw_spi_update_spi_cr0(op);
 	dev_dbg(bus, "cr0=%08x spi_cr0=%08x buf=%p len=%u [bytes]\n", cr0,
@@ -891,6 +1011,7 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
 	}
 	dw_write(priv, DW_SPI_SER, 0);
 	external_cs_manage(slave->dev, true);
+	dw_spi_mux_deselect(bus);
 
 	dev_dbg(bus, "%u bytes xfered\n", op->data.nbytes);
 	return ret;
@@ -943,10 +1064,15 @@ static const struct spi_controller_mem_ops dw_spi_mem_ops = {
 
 static int dw_spi_set_speed(struct udevice *bus, uint speed)
 {
+	int ret;
 	struct dw_spi_plat *plat = dev_get_plat(bus);
 	struct dw_spi_priv *priv = dev_get_priv(bus);
 	u16 clk_div;
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	if (speed > plat->frequency)
 		speed = plat->frequency;
 
@@ -960,6 +1086,7 @@ static int dw_spi_set_speed(struct udevice *bus, uint speed)
 
 	/* Enable controller after writing control registers */
 	dw_write(priv, DW_SPI_SSIENR, 1);
+	mux_control_deselect(priv->mux);
 
 	priv->freq = speed;
 	dev_dbg(bus, "speed=%d clk_div=%d\n", priv->freq, clk_div);
-- 
2.29.2



More information about the U-Boot mailing list