summaryrefslogtreecommitdiff
path: root/drivers/spi/kirkwood_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/kirkwood_spi.c')
-rw-r--r--drivers/spi/kirkwood_spi.c217
1 files changed, 164 insertions, 53 deletions
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index e7b0982..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(&reg->ctrl, KWSPI_CSN_ACT);
+}
+
+static void _spi_cs_deactivate(struct kwspi_registers *reg)
+{
+ clrbits_le32(&reg->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(&reg->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(&reg->irq_cause, KWSPI_SMEMRDIRQ);
+ writel(tmpdout, &reg->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(&reg->irq_cause) & KWSPI_SMEMRDIRQ) {
+ isread = 1;
+ tmpdin = readl(&reg->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;
@@ -147,76 +225,109 @@ void spi_init(void)
void spi_cs_activate(struct spi_slave *slave)
{
- setbits_le32(&spireg->ctrl, KWSPI_CSN_ACT);
+ _spi_cs_activate(spireg);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
- clrbits_le32(&spireg->ctrl, KWSPI_CSN_ACT);
+ _spi_cs_deactivate(spireg);
}
-int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
- void *din, unsigned long flags)
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
{
- unsigned int tmpdout, tmpdin;
- int tm, isread = 0;
+ return _spi_xfer(spireg, bitlen, dout, din, flags);
+}
- debug("spi_xfer: slave %u:%u dout %p din %p bitlen %u\n",
- slave->bus, slave->cs, dout, din, bitlen);
+#else
- if (flags & SPI_XFER_BEGIN)
- spi_cs_activate(slave);
+/* Here now the DM part */
- /*
- * handle data in 8-bit chunks
- * TBD: 2byte xfer mode to be enabled
- */
- clrsetbits_le32(&spireg->cfg, KWSPI_XFERLEN_MASK, KWSPI_XFERLEN_1BYTE);
+struct mvebu_spi_platdata {
+ struct kwspi_registers *spireg;
+};
- while (bitlen > 4) {
- debug("loopstart bitlen %d\n", bitlen);
- tmpdout = 0;
+struct mvebu_spi_priv {
+ struct kwspi_registers *spireg;
+};
- /* Shift data so it's msb-justified */
- if (dout)
- tmpdout = *(u32 *)dout & 0xff;
+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;
- clrbits_le32(&spireg->irq_cause, KWSPI_SMEMRDIRQ);
- writel(tmpdout, &spireg->dout); /* Write the data out */
- debug("*** spi_xfer: ... %08x written, bitlen %d\n",
- tmpdout, bitlen);
+ /* 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;
- /*
- * 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(&spireg->irq_cause) & KWSPI_SMEMRDIRQ) {
- isread = 1;
- tmpdin = readl(&spireg->din);
- debug("spi_xfer: din %p..%08x read\n",
- din, tmpdin);
+ /* program spi clock prescaler using max_hz */
+ writel(KWSPI_ADRLEN_3BYTE | data, &reg->cfg);
+ debug("data = 0x%08x\n", data);
- 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");
+ return 0;
+}
- debug("loopend bitlen %d\n", bitlen);
- }
+static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
+{
+ return 0;
+}
- if (flags & SPI_XFER_END)
- spi_cs_deactivate(slave);
+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, &reg->ctrl);
+ writel(KWSPI_SMEMRDIRQ, &reg->irq_cause);
+ writel(KWSPI_IRQMASK, &reg->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