183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
23fda4ef3SStefan Roese /*
33fda4ef3SStefan Roese * Copyright (C) 2015 Marvell International Ltd.
43fda4ef3SStefan Roese *
53fda4ef3SStefan Roese * Copyright (C) 2016 Stefan Roese <sr@denx.de>
63fda4ef3SStefan Roese */
73fda4ef3SStefan Roese
83fda4ef3SStefan Roese #include <common.h>
93fda4ef3SStefan Roese #include <dm.h>
103fda4ef3SStefan Roese #include <malloc.h>
113fda4ef3SStefan Roese #include <spi.h>
12*dbbd5bddSMarek Behún #include <clk.h>
133fda4ef3SStefan Roese #include <wait_bit.h>
143fda4ef3SStefan Roese #include <asm/io.h>
153fda4ef3SStefan Roese
163fda4ef3SStefan Roese DECLARE_GLOBAL_DATA_PTR;
173fda4ef3SStefan Roese
183fda4ef3SStefan Roese #define MVEBU_SPI_A3700_XFER_RDY BIT(1)
193fda4ef3SStefan Roese #define MVEBU_SPI_A3700_FIFO_FLUSH BIT(9)
203fda4ef3SStefan Roese #define MVEBU_SPI_A3700_BYTE_LEN BIT(5)
213fda4ef3SStefan Roese #define MVEBU_SPI_A3700_CLK_PHA BIT(6)
223fda4ef3SStefan Roese #define MVEBU_SPI_A3700_CLK_POL BIT(7)
233fda4ef3SStefan Roese #define MVEBU_SPI_A3700_FIFO_EN BIT(17)
243fda4ef3SStefan Roese #define MVEBU_SPI_A3700_SPI_EN_0 BIT(16)
25*dbbd5bddSMarek Behún #define MVEBU_SPI_A3700_CLK_PRESCALE_MASK 0x1f
26*dbbd5bddSMarek Behún
273fda4ef3SStefan Roese
283fda4ef3SStefan Roese /* SPI registers */
293fda4ef3SStefan Roese struct spi_reg {
303fda4ef3SStefan Roese u32 ctrl; /* 0x10600 */
313fda4ef3SStefan Roese u32 cfg; /* 0x10604 */
323fda4ef3SStefan Roese u32 dout; /* 0x10608 */
333fda4ef3SStefan Roese u32 din; /* 0x1060c */
343fda4ef3SStefan Roese };
353fda4ef3SStefan Roese
363fda4ef3SStefan Roese struct mvebu_spi_platdata {
373fda4ef3SStefan Roese struct spi_reg *spireg;
38*dbbd5bddSMarek Behún struct clk clk;
393fda4ef3SStefan Roese };
403fda4ef3SStefan Roese
spi_cs_activate(struct spi_reg * reg,int cs)413fda4ef3SStefan Roese static void spi_cs_activate(struct spi_reg *reg, int cs)
423fda4ef3SStefan Roese {
433fda4ef3SStefan Roese setbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs);
443fda4ef3SStefan Roese }
453fda4ef3SStefan Roese
spi_cs_deactivate(struct spi_reg * reg,int cs)463fda4ef3SStefan Roese static void spi_cs_deactivate(struct spi_reg *reg, int cs)
473fda4ef3SStefan Roese {
483fda4ef3SStefan Roese clrbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs);
493fda4ef3SStefan Roese }
503fda4ef3SStefan Roese
513fda4ef3SStefan Roese /**
523fda4ef3SStefan Roese * spi_legacy_shift_byte() - triggers the real SPI transfer
533fda4ef3SStefan Roese * @bytelen: Indicate how many bytes to transfer.
543fda4ef3SStefan Roese * @dout: Buffer address of what to send.
553fda4ef3SStefan Roese * @din: Buffer address of where to receive.
563fda4ef3SStefan Roese *
573fda4ef3SStefan Roese * This function triggers the real SPI transfer in legacy mode. It
583fda4ef3SStefan Roese * will shift out char buffer from @dout, and shift in char buffer to
593fda4ef3SStefan Roese * @din, if necessary.
603fda4ef3SStefan Roese *
613fda4ef3SStefan Roese * This function assumes that only one byte is shifted at one time.
623fda4ef3SStefan Roese * However, it is not its responisbility to set the transfer type to
633fda4ef3SStefan Roese * one-byte. Also, it does not guarantee that it will work if transfer
643fda4ef3SStefan Roese * type becomes two-byte. See spi_set_legacy() for details.
653fda4ef3SStefan Roese *
663fda4ef3SStefan Roese * In legacy mode, simply write to the SPI_DOUT register will trigger
673fda4ef3SStefan Roese * the transfer.
683fda4ef3SStefan Roese *
693fda4ef3SStefan Roese * If @dout == NULL, which means no actual data needs to be sent out,
703fda4ef3SStefan Roese * then the function will shift out 0x00 in order to shift in data.
713fda4ef3SStefan Roese * The XFER_RDY flag is checked every time before accessing SPI_DOUT
723fda4ef3SStefan Roese * and SPI_DIN register.
733fda4ef3SStefan Roese *
743fda4ef3SStefan Roese * The number of transfers to be triggerred is decided by @bytelen.
753fda4ef3SStefan Roese *
763fda4ef3SStefan Roese * Return: 0 - cool
773fda4ef3SStefan Roese * -ETIMEDOUT - XFER_RDY flag timeout
783fda4ef3SStefan Roese */
spi_legacy_shift_byte(struct spi_reg * reg,unsigned int bytelen,const void * dout,void * din)793fda4ef3SStefan Roese static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen,
803fda4ef3SStefan Roese const void *dout, void *din)
813fda4ef3SStefan Roese {
823fda4ef3SStefan Roese const u8 *dout_8;
833fda4ef3SStefan Roese u8 *din_8;
843fda4ef3SStefan Roese int ret;
853fda4ef3SStefan Roese
863fda4ef3SStefan Roese /* Use 0x00 as dummy dout */
873fda4ef3SStefan Roese const u8 dummy_dout = 0x0;
883fda4ef3SStefan Roese u32 pending_dout = 0x0;
893fda4ef3SStefan Roese
903fda4ef3SStefan Roese /* dout_8: pointer of current dout */
913fda4ef3SStefan Roese dout_8 = dout;
923fda4ef3SStefan Roese /* din_8: pointer of current din */
933fda4ef3SStefan Roese din_8 = din;
943fda4ef3SStefan Roese
953fda4ef3SStefan Roese while (bytelen) {
9648263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®->ctrl,
9748263504SÁlvaro Fernández Rojas MVEBU_SPI_A3700_XFER_RDY,
9848263504SÁlvaro Fernández Rojas true,100, false);
993fda4ef3SStefan Roese if (ret)
1003fda4ef3SStefan Roese return ret;
1013fda4ef3SStefan Roese
1023fda4ef3SStefan Roese if (dout)
1033fda4ef3SStefan Roese pending_dout = (u32)*dout_8;
1043fda4ef3SStefan Roese else
1053fda4ef3SStefan Roese pending_dout = (u32)dummy_dout;
1063fda4ef3SStefan Roese
1073fda4ef3SStefan Roese /* Trigger the xfer */
1083fda4ef3SStefan Roese writel(pending_dout, ®->dout);
1093fda4ef3SStefan Roese
1103fda4ef3SStefan Roese if (din) {
11148263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®->ctrl,
1123fda4ef3SStefan Roese MVEBU_SPI_A3700_XFER_RDY,
1133fda4ef3SStefan Roese true, 100, false);
1143fda4ef3SStefan Roese if (ret)
1153fda4ef3SStefan Roese return ret;
1163fda4ef3SStefan Roese
1173fda4ef3SStefan Roese /* Read what is transferred in */
1183fda4ef3SStefan Roese *din_8 = (u8)readl(®->din);
1193fda4ef3SStefan Roese }
1203fda4ef3SStefan Roese
1213fda4ef3SStefan Roese /* Don't increment the current pointer if NULL */
1223fda4ef3SStefan Roese if (dout)
1233fda4ef3SStefan Roese dout_8++;
1243fda4ef3SStefan Roese if (din)
1253fda4ef3SStefan Roese din_8++;
1263fda4ef3SStefan Roese
1273fda4ef3SStefan Roese bytelen--;
1283fda4ef3SStefan Roese }
1293fda4ef3SStefan Roese
1303fda4ef3SStefan Roese return 0;
1313fda4ef3SStefan Roese }
1323fda4ef3SStefan Roese
mvebu_spi_xfer(struct udevice * dev,unsigned int bitlen,const void * dout,void * din,unsigned long flags)1333fda4ef3SStefan Roese static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
1343fda4ef3SStefan Roese const void *dout, void *din, unsigned long flags)
1353fda4ef3SStefan Roese {
1363fda4ef3SStefan Roese struct udevice *bus = dev->parent;
1373fda4ef3SStefan Roese struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
1383fda4ef3SStefan Roese struct spi_reg *reg = plat->spireg;
1393fda4ef3SStefan Roese unsigned int bytelen;
1403fda4ef3SStefan Roese int ret;
1413fda4ef3SStefan Roese
1423fda4ef3SStefan Roese bytelen = bitlen / 8;
1433fda4ef3SStefan Roese
1443fda4ef3SStefan Roese if (dout && din)
1453fda4ef3SStefan Roese debug("This is a duplex transfer.\n");
1463fda4ef3SStefan Roese
1473fda4ef3SStefan Roese /* Activate CS */
1483fda4ef3SStefan Roese if (flags & SPI_XFER_BEGIN) {
1493fda4ef3SStefan Roese debug("SPI: activate cs.\n");
1503fda4ef3SStefan Roese spi_cs_activate(reg, spi_chip_select(dev));
1513fda4ef3SStefan Roese }
1523fda4ef3SStefan Roese
1533fda4ef3SStefan Roese /* Send and/or receive */
1543fda4ef3SStefan Roese if (dout || din) {
1553fda4ef3SStefan Roese ret = spi_legacy_shift_byte(reg, bytelen, dout, din);
1563fda4ef3SStefan Roese if (ret)
1573fda4ef3SStefan Roese return ret;
1583fda4ef3SStefan Roese }
1593fda4ef3SStefan Roese
1603fda4ef3SStefan Roese /* Deactivate CS */
1613fda4ef3SStefan Roese if (flags & SPI_XFER_END) {
16248263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®->ctrl,
16348263504SÁlvaro Fernández Rojas MVEBU_SPI_A3700_XFER_RDY,
16448263504SÁlvaro Fernández Rojas true, 100, false);
1653fda4ef3SStefan Roese if (ret)
1663fda4ef3SStefan Roese return ret;
1673fda4ef3SStefan Roese
1683fda4ef3SStefan Roese debug("SPI: deactivate cs.\n");
1693fda4ef3SStefan Roese spi_cs_deactivate(reg, spi_chip_select(dev));
1703fda4ef3SStefan Roese }
1713fda4ef3SStefan Roese
1723fda4ef3SStefan Roese return 0;
1733fda4ef3SStefan Roese }
1743fda4ef3SStefan Roese
mvebu_spi_set_speed(struct udevice * bus,uint hz)1753fda4ef3SStefan Roese static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
1763fda4ef3SStefan Roese {
1773fda4ef3SStefan Roese struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
1783fda4ef3SStefan Roese struct spi_reg *reg = plat->spireg;
179*dbbd5bddSMarek Behún u32 data, prescale;
1803fda4ef3SStefan Roese
1813fda4ef3SStefan Roese data = readl(®->cfg);
1823fda4ef3SStefan Roese
183*dbbd5bddSMarek Behún prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz);
184*dbbd5bddSMarek Behún if (prescale > 0x1f)
185*dbbd5bddSMarek Behún prescale = 0x1f;
186*dbbd5bddSMarek Behún else if (prescale > 0xf)
187*dbbd5bddSMarek Behún prescale = 0x10 + (prescale + 1) / 2;
1883fda4ef3SStefan Roese
189*dbbd5bddSMarek Behún data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
190*dbbd5bddSMarek Behún data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
1913fda4ef3SStefan Roese
1923fda4ef3SStefan Roese writel(data, ®->cfg);
1933fda4ef3SStefan Roese
1943fda4ef3SStefan Roese return 0;
1953fda4ef3SStefan Roese }
1963fda4ef3SStefan Roese
mvebu_spi_set_mode(struct udevice * bus,uint mode)1973fda4ef3SStefan Roese static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
1983fda4ef3SStefan Roese {
1993fda4ef3SStefan Roese struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
2003fda4ef3SStefan Roese struct spi_reg *reg = plat->spireg;
2013fda4ef3SStefan Roese
2023fda4ef3SStefan Roese /*
2033fda4ef3SStefan Roese * Set SPI polarity
2043fda4ef3SStefan Roese * 0: Serial interface clock is low when inactive
2053fda4ef3SStefan Roese * 1: Serial interface clock is high when inactive
2063fda4ef3SStefan Roese */
2073fda4ef3SStefan Roese if (mode & SPI_CPOL)
2083fda4ef3SStefan Roese setbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_POL);
2093fda4ef3SStefan Roese else
2103fda4ef3SStefan Roese clrbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_POL);
2113fda4ef3SStefan Roese if (mode & SPI_CPHA)
2123fda4ef3SStefan Roese setbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_PHA);
2133fda4ef3SStefan Roese else
2143fda4ef3SStefan Roese clrbits_le32(®->cfg, MVEBU_SPI_A3700_CLK_PHA);
2153fda4ef3SStefan Roese
2163fda4ef3SStefan Roese return 0;
2173fda4ef3SStefan Roese }
2183fda4ef3SStefan Roese
mvebu_spi_probe(struct udevice * bus)2193fda4ef3SStefan Roese static int mvebu_spi_probe(struct udevice *bus)
2203fda4ef3SStefan Roese {
2213fda4ef3SStefan Roese struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
2223fda4ef3SStefan Roese struct spi_reg *reg = plat->spireg;
2233fda4ef3SStefan Roese u32 data;
2243fda4ef3SStefan Roese int ret;
2253fda4ef3SStefan Roese
2263fda4ef3SStefan Roese /*
2273fda4ef3SStefan Roese * Settings SPI controller to be working in legacy mode, which
2283fda4ef3SStefan Roese * means use only DO pin (I/O 1) for Data Out, and DI pin (I/O 0)
2293fda4ef3SStefan Roese * for Data In.
2303fda4ef3SStefan Roese */
2313fda4ef3SStefan Roese
2323fda4ef3SStefan Roese /* Flush read/write FIFO */
2333fda4ef3SStefan Roese data = readl(®->cfg);
2343fda4ef3SStefan Roese writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg);
23548263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH,
2363fda4ef3SStefan Roese false, 1000, false);
2373fda4ef3SStefan Roese if (ret)
2383fda4ef3SStefan Roese return ret;
2393fda4ef3SStefan Roese
2403fda4ef3SStefan Roese /* Disable FIFO mode */
2413fda4ef3SStefan Roese data &= ~MVEBU_SPI_A3700_FIFO_EN;
2423fda4ef3SStefan Roese
2433fda4ef3SStefan Roese /* Always shift 1 byte at a time */
2443fda4ef3SStefan Roese data &= ~MVEBU_SPI_A3700_BYTE_LEN;
2453fda4ef3SStefan Roese
2463fda4ef3SStefan Roese writel(data, ®->cfg);
2473fda4ef3SStefan Roese
2483fda4ef3SStefan Roese return 0;
2493fda4ef3SStefan Roese }
2503fda4ef3SStefan Roese
mvebu_spi_ofdata_to_platdata(struct udevice * bus)2513fda4ef3SStefan Roese static int mvebu_spi_ofdata_to_platdata(struct udevice *bus)
2523fda4ef3SStefan Roese {
2533fda4ef3SStefan Roese struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
254*dbbd5bddSMarek Behún int ret;
2553fda4ef3SStefan Roese
256a821c4afSSimon Glass plat->spireg = (struct spi_reg *)devfdt_get_addr(bus);
2573fda4ef3SStefan Roese
258*dbbd5bddSMarek Behún ret = clk_get_by_index(bus, 0, &plat->clk);
259*dbbd5bddSMarek Behún if (ret) {
260*dbbd5bddSMarek Behún dev_err(bus, "cannot get clock\n");
261*dbbd5bddSMarek Behún return ret;
262*dbbd5bddSMarek Behún }
263*dbbd5bddSMarek Behún
264*dbbd5bddSMarek Behún return 0;
265*dbbd5bddSMarek Behún }
266*dbbd5bddSMarek Behún
mvebu_spi_remove(struct udevice * bus)267*dbbd5bddSMarek Behún static int mvebu_spi_remove(struct udevice *bus)
268*dbbd5bddSMarek Behún {
269*dbbd5bddSMarek Behún struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
270*dbbd5bddSMarek Behún
271*dbbd5bddSMarek Behún clk_free(&plat->clk);
2723fda4ef3SStefan Roese
2733fda4ef3SStefan Roese return 0;
2743fda4ef3SStefan Roese }
2753fda4ef3SStefan Roese
2763fda4ef3SStefan Roese static const struct dm_spi_ops mvebu_spi_ops = {
2773fda4ef3SStefan Roese .xfer = mvebu_spi_xfer,
2783fda4ef3SStefan Roese .set_speed = mvebu_spi_set_speed,
2793fda4ef3SStefan Roese .set_mode = mvebu_spi_set_mode,
2803fda4ef3SStefan Roese /*
2813fda4ef3SStefan Roese * cs_info is not needed, since we require all chip selects to be
2823fda4ef3SStefan Roese * in the device tree explicitly
2833fda4ef3SStefan Roese */
2843fda4ef3SStefan Roese };
2853fda4ef3SStefan Roese
2863fda4ef3SStefan Roese static const struct udevice_id mvebu_spi_ids[] = {
2873fda4ef3SStefan Roese { .compatible = "marvell,armada-3700-spi" },
2883fda4ef3SStefan Roese { }
2893fda4ef3SStefan Roese };
2903fda4ef3SStefan Roese
2913fda4ef3SStefan Roese U_BOOT_DRIVER(mvebu_spi) = {
2923fda4ef3SStefan Roese .name = "mvebu_spi",
2933fda4ef3SStefan Roese .id = UCLASS_SPI,
2943fda4ef3SStefan Roese .of_match = mvebu_spi_ids,
2953fda4ef3SStefan Roese .ops = &mvebu_spi_ops,
2963fda4ef3SStefan Roese .ofdata_to_platdata = mvebu_spi_ofdata_to_platdata,
2973fda4ef3SStefan Roese .platdata_auto_alloc_size = sizeof(struct mvebu_spi_platdata),
2983fda4ef3SStefan Roese .probe = mvebu_spi_probe,
299*dbbd5bddSMarek Behún .remove = mvebu_spi_remove,
3003fda4ef3SStefan Roese };
301