spi: kirkwood_spi.c: Add driver model support
This patch adds driver model support to the kirkwood SPI driver. Which
is also used on the MVEBU SoC's, now being converted to DM. Non-DM
support is still available for the "older" platforms using this
driver, like kirkwood.
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Valentin Longchamp <valentin.longchamp@keymile.com>
Cc: Luka Perkov <luka.perkov@sartura.hr>
Cc: Jagan Teki <jteki@openedev.com>
Cc: Simon Glass <sjg@chromium.org>
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index 08bf432..7890796 100644
--- a/drivers/spi/kirkwood_spi.c
+++ b/drivers/spi/kirkwood_spi.c
@@ -9,6 +9,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
@@ -18,6 +19,83 @@
#endif
#include <asm/arch-mvebu/spi.h>
+static void _spi_cs_activate(struct kwspi_registers *reg)
+{
+ setbits_le32(®->ctrl, KWSPI_CSN_ACT);
+}
+
+static void _spi_cs_deactivate(struct kwspi_registers *reg)
+{
+ clrbits_le32(®->ctrl, KWSPI_CSN_ACT);
+}
+
+static int _spi_xfer(struct kwspi_registers *reg, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ unsigned int tmpdout, tmpdin;
+ int tm, isread = 0;
+
+ debug("spi_xfer: dout %p din %p bitlen %u\n", dout, din, bitlen);
+
+ if (flags & SPI_XFER_BEGIN)
+ _spi_cs_activate(reg);
+
+ /*
+ * handle data in 8-bit chunks
+ * TBD: 2byte xfer mode to be enabled
+ */
+ clrsetbits_le32(®->cfg, KWSPI_XFERLEN_MASK, KWSPI_XFERLEN_1BYTE);
+
+ while (bitlen > 4) {
+ debug("loopstart bitlen %d\n", bitlen);
+ tmpdout = 0;
+
+ /* Shift data so it's msb-justified */
+ if (dout)
+ tmpdout = *(u32 *)dout & 0xff;
+
+ clrbits_le32(®->irq_cause, KWSPI_SMEMRDIRQ);
+ writel(tmpdout, ®->dout); /* Write the data out */
+ debug("*** spi_xfer: ... %08x written, bitlen %d\n",
+ tmpdout, bitlen);
+
+ /*
+ * Wait for SPI transmit to get out
+ * or time out (1 second = 1000 ms)
+ * The NE event must be read and cleared first
+ */
+ for (tm = 0, isread = 0; tm < KWSPI_TIMEOUT; ++tm) {
+ if (readl(®->irq_cause) & KWSPI_SMEMRDIRQ) {
+ isread = 1;
+ tmpdin = readl(®->din);
+ debug("spi_xfer: din %p..%08x read\n",
+ din, tmpdin);
+
+ if (din) {
+ *((u8 *)din) = (u8)tmpdin;
+ din += 1;
+ }
+ if (dout)
+ dout += 1;
+ bitlen -= 8;
+ }
+ if (isread)
+ break;
+ }
+ if (tm >= KWSPI_TIMEOUT)
+ printf("*** spi_xfer: Time out during SPI transfer\n");
+
+ debug("loopend bitlen %d\n", bitlen);
+ }
+
+ if (flags & SPI_XFER_END)
+ _spi_cs_deactivate(reg);
+
+ return 0;
+}
+
+#ifndef CONFIG_DM_SPI
+
static struct kwspi_registers *spireg =
(struct kwspi_registers *)MVEBU_SPI_BASE;
@@ -145,16 +223,6 @@
{
}
-static void _spi_cs_activate(struct kwspi_registers *reg)
-{
- setbits_le32(®->ctrl, KWSPI_CSN_ACT);
-}
-
-static void _spi_cs_deactivate(struct kwspi_registers *reg)
-{
- clrbits_le32(®->ctrl, KWSPI_CSN_ACT);
-}
-
void spi_cs_activate(struct spi_slave *slave)
{
_spi_cs_activate(spireg);
@@ -165,73 +233,101 @@
_spi_cs_deactivate(spireg);
}
-static int _spi_xfer(struct kwspi_registers *reg, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
-{
- unsigned int tmpdout, tmpdin;
- int tm, isread = 0;
-
- debug("spi_xfer: dout %p din %p bitlen %u\n", dout, din, bitlen);
-
- if (flags & SPI_XFER_BEGIN)
- _spi_cs_activate(reg);
-
- /*
- * handle data in 8-bit chunks
- * TBD: 2byte xfer mode to be enabled
- */
- clrsetbits_le32(®->cfg, KWSPI_XFERLEN_MASK, KWSPI_XFERLEN_1BYTE);
-
- while (bitlen > 4) {
- debug("loopstart bitlen %d\n", bitlen);
- tmpdout = 0;
-
- /* Shift data so it's msb-justified */
- if (dout)
- tmpdout = *(u32 *)dout & 0xff;
-
- clrbits_le32(®->irq_cause, KWSPI_SMEMRDIRQ);
- writel(tmpdout, ®->dout); /* Write the data out */
- debug("*** spi_xfer: ... %08x written, bitlen %d\n",
- tmpdout, bitlen);
-
- /*
- * Wait for SPI transmit to get out
- * or time out (1 second = 1000 ms)
- * The NE event must be read and cleared first
- */
- for (tm = 0, isread = 0; tm < KWSPI_TIMEOUT; ++tm) {
- if (readl(®->irq_cause) & KWSPI_SMEMRDIRQ) {
- isread = 1;
- tmpdin = readl(®->din);
- debug("spi_xfer: din %p..%08x read\n",
- din, tmpdin);
-
- if (din) {
- *((u8 *)din) = (u8)tmpdin;
- din += 1;
- }
- if (dout)
- dout += 1;
- bitlen -= 8;
- }
- if (isread)
- break;
- }
- if (tm >= KWSPI_TIMEOUT)
- printf("*** spi_xfer: Time out during SPI transfer\n");
-
- debug("loopend bitlen %d\n", bitlen);
- }
-
- if (flags & SPI_XFER_END)
- _spi_cs_deactivate(reg);
-
- return 0;
-}
-
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
return _spi_xfer(spireg, bitlen, dout, din, flags);
}
+
+#else
+
+/* Here now the DM part */
+
+struct mvebu_spi_platdata {
+ struct kwspi_registers *spireg;
+};
+
+struct mvebu_spi_priv {
+ struct kwspi_registers *spireg;
+};
+
+static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
+{
+ struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+ struct kwspi_registers *reg = plat->spireg;
+ u32 data;
+
+ /* calculate spi clock prescaller using max_hz */
+ data = ((CONFIG_SYS_TCLK / 2) / hz) + 0x10;
+ data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data;
+ data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;
+
+ /* program spi clock prescaler using max_hz */
+ writel(KWSPI_ADRLEN_3BYTE | data, ®->cfg);
+ debug("data = 0x%08x\n", data);
+
+ return 0;
+}
+
+static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
+{
+ return 0;
+}
+
+static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev->parent;
+ struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+
+ return _spi_xfer(plat->spireg, bitlen, dout, din, flags);
+}
+
+static int mvebu_spi_probe(struct udevice *bus)
+{
+ struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+ struct kwspi_registers *reg = plat->spireg;
+
+ writel(KWSPI_SMEMRDY, ®->ctrl);
+ writel(KWSPI_SMEMRDIRQ, ®->irq_cause);
+ writel(KWSPI_IRQMASK, ®->irq_mask);
+
+ return 0;
+}
+
+static int mvebu_spi_ofdata_to_platdata(struct udevice *bus)
+{
+ struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
+
+ plat->spireg = (struct kwspi_registers *)dev_get_addr(bus);
+
+ return 0;
+}
+
+static const struct dm_spi_ops mvebu_spi_ops = {
+ .xfer = mvebu_spi_xfer,
+ .set_speed = mvebu_spi_set_speed,
+ .set_mode = mvebu_spi_set_mode,
+ /*
+ * cs_info is not needed, since we require all chip selects to be
+ * in the device tree explicitly
+ */
+};
+
+static const struct udevice_id mvebu_spi_ids[] = {
+ { .compatible = "marvell,armada-380-spi" },
+ { .compatible = "marvell,armada-xp-spi" },
+ { }
+};
+
+U_BOOT_DRIVER(mvebu_spi) = {
+ .name = "mvebu_spi",
+ .id = UCLASS_SPI,
+ .of_match = mvebu_spi_ids,
+ .ops = &mvebu_spi_ops,
+ .ofdata_to_platdata = mvebu_spi_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct mvebu_spi_platdata),
+ .priv_auto_alloc_size = sizeof(struct mvebu_spi_priv),
+ .probe = mvebu_spi_probe,
+};
+#endif