xref: /openbmc/u-boot/drivers/spi/mvebu_a3700_spi.c (revision ca70cbabdcd19bf157ae4fa984559b126071ccff)
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(&reg->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(&reg->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(&reg->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, &reg->dout);
1093fda4ef3SStefan Roese 
1103fda4ef3SStefan Roese 		if (din) {
11148263504SÁlvaro Fernández Rojas 			ret = wait_for_bit_le32(&reg->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(&reg->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(&reg->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(&reg->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, &reg->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(&reg->cfg, MVEBU_SPI_A3700_CLK_POL);
2093fda4ef3SStefan Roese 	else
2103fda4ef3SStefan Roese 		clrbits_le32(&reg->cfg, MVEBU_SPI_A3700_CLK_POL);
2113fda4ef3SStefan Roese 	if (mode & SPI_CPHA)
2123fda4ef3SStefan Roese 		setbits_le32(&reg->cfg, MVEBU_SPI_A3700_CLK_PHA);
2133fda4ef3SStefan Roese 	else
2143fda4ef3SStefan Roese 		clrbits_le32(&reg->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(&reg->cfg);
2343fda4ef3SStefan Roese 	writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, &reg->cfg);
23548263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&reg->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, &reg->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