[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