xref: /linux/drivers/spi/spi-falcon.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26cd3c7e2SThomas Langer /*
36cd3c7e2SThomas Langer  *
46cd3c7e2SThomas Langer  *  Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
56cd3c7e2SThomas Langer  */
66cd3c7e2SThomas Langer 
76cd3c7e2SThomas Langer #include <linux/module.h>
86cd3c7e2SThomas Langer #include <linux/device.h>
96cd3c7e2SThomas Langer #include <linux/platform_device.h>
106cd3c7e2SThomas Langer #include <linux/spi/spi.h>
116cd3c7e2SThomas Langer #include <linux/delay.h>
126cd3c7e2SThomas Langer #include <linux/of.h>
136cd3c7e2SThomas Langer #include <linux/of_platform.h>
146cd3c7e2SThomas Langer 
156cd3c7e2SThomas Langer #include <lantiq_soc.h>
166cd3c7e2SThomas Langer 
176cd3c7e2SThomas Langer #define DRV_NAME		"sflash-falcon"
186cd3c7e2SThomas Langer 
196cd3c7e2SThomas Langer #define FALCON_SPI_XFER_BEGIN	(1 << 0)
206cd3c7e2SThomas Langer #define FALCON_SPI_XFER_END	(1 << 1)
216cd3c7e2SThomas Langer 
226cd3c7e2SThomas Langer /* Bus Read Configuration Register0 */
236cd3c7e2SThomas Langer #define BUSRCON0		0x00000010
246cd3c7e2SThomas Langer /* Bus Write Configuration Register0 */
256cd3c7e2SThomas Langer #define BUSWCON0		0x00000018
266cd3c7e2SThomas Langer /* Serial Flash Configuration Register */
276cd3c7e2SThomas Langer #define SFCON			0x00000080
286cd3c7e2SThomas Langer /* Serial Flash Time Register */
296cd3c7e2SThomas Langer #define SFTIME			0x00000084
306cd3c7e2SThomas Langer /* Serial Flash Status Register */
316cd3c7e2SThomas Langer #define SFSTAT			0x00000088
326cd3c7e2SThomas Langer /* Serial Flash Command Register */
336cd3c7e2SThomas Langer #define SFCMD			0x0000008C
346cd3c7e2SThomas Langer /* Serial Flash Address Register */
356cd3c7e2SThomas Langer #define SFADDR			0x00000090
366cd3c7e2SThomas Langer /* Serial Flash Data Register */
376cd3c7e2SThomas Langer #define SFDATA			0x00000094
386cd3c7e2SThomas Langer /* Serial Flash I/O Control Register */
396cd3c7e2SThomas Langer #define SFIO			0x00000098
406cd3c7e2SThomas Langer /* EBU Clock Control Register */
416cd3c7e2SThomas Langer #define EBUCC			0x000000C4
426cd3c7e2SThomas Langer 
436cd3c7e2SThomas Langer /* Dummy Phase Length */
446cd3c7e2SThomas Langer #define SFCMD_DUMLEN_OFFSET	16
456cd3c7e2SThomas Langer #define SFCMD_DUMLEN_MASK	0x000F0000
466cd3c7e2SThomas Langer /* Chip Select */
476cd3c7e2SThomas Langer #define SFCMD_CS_OFFSET		24
486cd3c7e2SThomas Langer #define SFCMD_CS_MASK		0x07000000
496cd3c7e2SThomas Langer /* field offset */
506cd3c7e2SThomas Langer #define SFCMD_ALEN_OFFSET	20
516cd3c7e2SThomas Langer #define SFCMD_ALEN_MASK		0x00700000
526cd3c7e2SThomas Langer /* SCK Rise-edge Position */
536cd3c7e2SThomas Langer #define SFTIME_SCKR_POS_OFFSET	8
546cd3c7e2SThomas Langer #define SFTIME_SCKR_POS_MASK	0x00000F00
556cd3c7e2SThomas Langer /* SCK Period */
566cd3c7e2SThomas Langer #define SFTIME_SCK_PER_OFFSET	0
576cd3c7e2SThomas Langer #define SFTIME_SCK_PER_MASK	0x0000000F
586cd3c7e2SThomas Langer /* SCK Fall-edge Position */
596cd3c7e2SThomas Langer #define SFTIME_SCKF_POS_OFFSET	12
606cd3c7e2SThomas Langer #define SFTIME_SCKF_POS_MASK	0x0000F000
616cd3c7e2SThomas Langer /* Device Size */
626cd3c7e2SThomas Langer #define SFCON_DEV_SIZE_A23_0	0x03000000
636cd3c7e2SThomas Langer #define SFCON_DEV_SIZE_MASK	0x0F000000
646cd3c7e2SThomas Langer /* Read Data Position */
656cd3c7e2SThomas Langer #define SFTIME_RD_POS_MASK	0x000F0000
666cd3c7e2SThomas Langer /* Data Output */
676cd3c7e2SThomas Langer #define SFIO_UNUSED_WD_MASK	0x0000000F
686cd3c7e2SThomas Langer /* Command Opcode mask */
696cd3c7e2SThomas Langer #define SFCMD_OPC_MASK		0x000000FF
706cd3c7e2SThomas Langer /* dlen bytes of data to write */
716cd3c7e2SThomas Langer #define SFCMD_DIR_WRITE		0x00000100
726cd3c7e2SThomas Langer /* Data Length offset */
736cd3c7e2SThomas Langer #define SFCMD_DLEN_OFFSET	9
746cd3c7e2SThomas Langer /* Command Error */
756cd3c7e2SThomas Langer #define SFSTAT_CMD_ERR		0x20000000
766cd3c7e2SThomas Langer /* Access Command Pending */
776cd3c7e2SThomas Langer #define SFSTAT_CMD_PEND		0x00400000
786cd3c7e2SThomas Langer /* Frequency set to 100MHz. */
796cd3c7e2SThomas Langer #define EBUCC_EBUDIV_SELF100	0x00000001
806cd3c7e2SThomas Langer /* Serial Flash */
816cd3c7e2SThomas Langer #define BUSRCON0_AGEN_SERIAL_FLASH	0xF0000000
826cd3c7e2SThomas Langer /* 8-bit multiplexed */
836cd3c7e2SThomas Langer #define BUSRCON0_PORTW_8_BIT_MUX	0x00000000
846cd3c7e2SThomas Langer /* Serial Flash */
856cd3c7e2SThomas Langer #define BUSWCON0_AGEN_SERIAL_FLASH	0xF0000000
866cd3c7e2SThomas Langer /* Chip Select after opcode */
876cd3c7e2SThomas Langer #define SFCMD_KEEP_CS_KEEP_SELECTED	0x00008000
886cd3c7e2SThomas Langer 
896cd3c7e2SThomas Langer #define CLOCK_100M	100000000
906cd3c7e2SThomas Langer #define CLOCK_50M	50000000
916cd3c7e2SThomas Langer 
926cd3c7e2SThomas Langer struct falcon_sflash {
936cd3c7e2SThomas Langer 	u32 sfcmd; /* for caching of opcode, direction, ... */
94*45d95911SYang Yingliang 	struct spi_controller *host;
956cd3c7e2SThomas Langer };
966cd3c7e2SThomas Langer 
falcon_sflash_xfer(struct spi_device * spi,struct spi_transfer * t,unsigned long flags)976cd3c7e2SThomas Langer int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
986cd3c7e2SThomas Langer 		unsigned long flags)
996cd3c7e2SThomas Langer {
1006cd3c7e2SThomas Langer 	struct device *dev = &spi->dev;
101*45d95911SYang Yingliang 	struct falcon_sflash *priv = spi_controller_get_devdata(spi->controller);
1026cd3c7e2SThomas Langer 	const u8 *txp = t->tx_buf;
1036cd3c7e2SThomas Langer 	u8 *rxp = t->rx_buf;
1046cd3c7e2SThomas Langer 	unsigned int bytelen = ((8 * t->len + 7) / 8);
1056cd3c7e2SThomas Langer 	unsigned int len, alen, dumlen;
1066cd3c7e2SThomas Langer 	u32 val;
1076cd3c7e2SThomas Langer 	enum {
1086cd3c7e2SThomas Langer 		state_init,
1096cd3c7e2SThomas Langer 		state_command_prepare,
1106cd3c7e2SThomas Langer 		state_write,
1116cd3c7e2SThomas Langer 		state_read,
1126cd3c7e2SThomas Langer 		state_disable_cs,
1136cd3c7e2SThomas Langer 		state_end
1146cd3c7e2SThomas Langer 	} state = state_init;
1156cd3c7e2SThomas Langer 
1166cd3c7e2SThomas Langer 	do {
1176cd3c7e2SThomas Langer 		switch (state) {
1186cd3c7e2SThomas Langer 		case state_init: /* detect phase of upper layer sequence */
1196cd3c7e2SThomas Langer 		{
1206cd3c7e2SThomas Langer 			/* initial write ? */
1216cd3c7e2SThomas Langer 			if (flags & FALCON_SPI_XFER_BEGIN) {
1226cd3c7e2SThomas Langer 				if (!txp) {
1236cd3c7e2SThomas Langer 					dev_err(dev,
1246cd3c7e2SThomas Langer 						"BEGIN without tx data!\n");
1256cd3c7e2SThomas Langer 					return -ENODATA;
1266cd3c7e2SThomas Langer 				}
1276cd3c7e2SThomas Langer 				/*
1286cd3c7e2SThomas Langer 				 * Prepare the parts of the sfcmd register,
1296cd3c7e2SThomas Langer 				 * which should not change during a sequence!
1306cd3c7e2SThomas Langer 				 * Only exception are the length fields,
1316cd3c7e2SThomas Langer 				 * especially alen and dumlen.
1326cd3c7e2SThomas Langer 				 */
1336cd3c7e2SThomas Langer 
1349e264f3fSAmit Kumar Mahapatra via Alsa-devel 				priv->sfcmd = ((spi_get_chipselect(spi, 0)
1356cd3c7e2SThomas Langer 						<< SFCMD_CS_OFFSET)
1366cd3c7e2SThomas Langer 					       & SFCMD_CS_MASK);
1376cd3c7e2SThomas Langer 				priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED;
1386cd3c7e2SThomas Langer 				priv->sfcmd |= *txp;
1396cd3c7e2SThomas Langer 				txp++;
1406cd3c7e2SThomas Langer 				bytelen--;
1416cd3c7e2SThomas Langer 				if (bytelen) {
1426cd3c7e2SThomas Langer 					/*
1436cd3c7e2SThomas Langer 					 * more data:
1446cd3c7e2SThomas Langer 					 * maybe address and/or dummy
1456cd3c7e2SThomas Langer 					 */
1466cd3c7e2SThomas Langer 					state = state_command_prepare;
1476cd3c7e2SThomas Langer 					break;
1486cd3c7e2SThomas Langer 				} else {
1496cd3c7e2SThomas Langer 					dev_dbg(dev, "write cmd %02X\n",
1506cd3c7e2SThomas Langer 						priv->sfcmd & SFCMD_OPC_MASK);
1516cd3c7e2SThomas Langer 				}
1526cd3c7e2SThomas Langer 			}
1536cd3c7e2SThomas Langer 			/* continued write ? */
1546cd3c7e2SThomas Langer 			if (txp && bytelen) {
1556cd3c7e2SThomas Langer 				state = state_write;
1566cd3c7e2SThomas Langer 				break;
1576cd3c7e2SThomas Langer 			}
1586cd3c7e2SThomas Langer 			/* read data? */
1596cd3c7e2SThomas Langer 			if (rxp && bytelen) {
1606cd3c7e2SThomas Langer 				state = state_read;
1616cd3c7e2SThomas Langer 				break;
1626cd3c7e2SThomas Langer 			}
1636cd3c7e2SThomas Langer 			/* end of sequence? */
1646cd3c7e2SThomas Langer 			if (flags & FALCON_SPI_XFER_END)
1656cd3c7e2SThomas Langer 				state = state_disable_cs;
1666cd3c7e2SThomas Langer 			else
1676cd3c7e2SThomas Langer 				state = state_end;
1686cd3c7e2SThomas Langer 			break;
1696cd3c7e2SThomas Langer 		}
1706cd3c7e2SThomas Langer 		/* collect tx data for address and dummy phase */
1716cd3c7e2SThomas Langer 		case state_command_prepare:
1726cd3c7e2SThomas Langer 		{
1736cd3c7e2SThomas Langer 			/* txp is valid, already checked */
1746cd3c7e2SThomas Langer 			val = 0;
1756cd3c7e2SThomas Langer 			alen = 0;
1766cd3c7e2SThomas Langer 			dumlen = 0;
1776cd3c7e2SThomas Langer 			while (bytelen > 0) {
1786cd3c7e2SThomas Langer 				if (alen < 3) {
1796cd3c7e2SThomas Langer 					val = (val << 8) | (*txp++);
1806cd3c7e2SThomas Langer 					alen++;
1816cd3c7e2SThomas Langer 				} else if ((dumlen < 15) && (*txp == 0)) {
1826cd3c7e2SThomas Langer 					/*
1836cd3c7e2SThomas Langer 					 * assume dummy bytes are set to 0
1846cd3c7e2SThomas Langer 					 * from upper layer
1856cd3c7e2SThomas Langer 					 */
1866cd3c7e2SThomas Langer 					dumlen++;
1876cd3c7e2SThomas Langer 					txp++;
1886cd3c7e2SThomas Langer 				} else {
1896cd3c7e2SThomas Langer 					break;
1906cd3c7e2SThomas Langer 				}
1916cd3c7e2SThomas Langer 				bytelen--;
1926cd3c7e2SThomas Langer 			}
1936cd3c7e2SThomas Langer 			priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK);
1946cd3c7e2SThomas Langer 			priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) |
1956cd3c7e2SThomas Langer 					 (dumlen << SFCMD_DUMLEN_OFFSET);
1966cd3c7e2SThomas Langer 			if (alen > 0)
1976cd3c7e2SThomas Langer 				ltq_ebu_w32(val, SFADDR);
1986cd3c7e2SThomas Langer 
1996cd3c7e2SThomas Langer 			dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n",
2006cd3c7e2SThomas Langer 				priv->sfcmd & SFCMD_OPC_MASK,
2016cd3c7e2SThomas Langer 				alen, val, dumlen);
2026cd3c7e2SThomas Langer 
2036cd3c7e2SThomas Langer 			if (bytelen > 0) {
2046cd3c7e2SThomas Langer 				/* continue with write */
2056cd3c7e2SThomas Langer 				state = state_write;
2066cd3c7e2SThomas Langer 			} else if (flags & FALCON_SPI_XFER_END) {
2076cd3c7e2SThomas Langer 				/* end of sequence? */
2086cd3c7e2SThomas Langer 				state = state_disable_cs;
2096cd3c7e2SThomas Langer 			} else {
2106cd3c7e2SThomas Langer 				/*
2116cd3c7e2SThomas Langer 				 * go to end and expect another
2126cd3c7e2SThomas Langer 				 * call (read or write)
2136cd3c7e2SThomas Langer 				 */
2146cd3c7e2SThomas Langer 				state = state_end;
2156cd3c7e2SThomas Langer 			}
2166cd3c7e2SThomas Langer 			break;
2176cd3c7e2SThomas Langer 		}
2186cd3c7e2SThomas Langer 		case state_write:
2196cd3c7e2SThomas Langer 		{
2206cd3c7e2SThomas Langer 			/* txp still valid */
2216cd3c7e2SThomas Langer 			priv->sfcmd |= SFCMD_DIR_WRITE;
2226cd3c7e2SThomas Langer 			len = 0;
2236cd3c7e2SThomas Langer 			val = 0;
2246cd3c7e2SThomas Langer 			do {
2256cd3c7e2SThomas Langer 				if (bytelen--)
2266cd3c7e2SThomas Langer 					val |= (*txp++) << (8 * len++);
2276cd3c7e2SThomas Langer 				if ((flags & FALCON_SPI_XFER_END)
2286cd3c7e2SThomas Langer 				    && (bytelen == 0)) {
2296cd3c7e2SThomas Langer 					priv->sfcmd &=
2306cd3c7e2SThomas Langer 						~SFCMD_KEEP_CS_KEEP_SELECTED;
2316cd3c7e2SThomas Langer 				}
2326cd3c7e2SThomas Langer 				if ((len == 4) || (bytelen == 0)) {
2336cd3c7e2SThomas Langer 					ltq_ebu_w32(val, SFDATA);
2346cd3c7e2SThomas Langer 					ltq_ebu_w32(priv->sfcmd
2356cd3c7e2SThomas Langer 						| (len<<SFCMD_DLEN_OFFSET),
2366cd3c7e2SThomas Langer 						SFCMD);
2376cd3c7e2SThomas Langer 					len = 0;
2386cd3c7e2SThomas Langer 					val = 0;
2396cd3c7e2SThomas Langer 					priv->sfcmd &= ~(SFCMD_ALEN_MASK
2406cd3c7e2SThomas Langer 							 | SFCMD_DUMLEN_MASK);
2416cd3c7e2SThomas Langer 				}
2426cd3c7e2SThomas Langer 			} while (bytelen);
2436cd3c7e2SThomas Langer 			state = state_end;
2446cd3c7e2SThomas Langer 			break;
2456cd3c7e2SThomas Langer 		}
2466cd3c7e2SThomas Langer 		case state_read:
2476cd3c7e2SThomas Langer 		{
2486cd3c7e2SThomas Langer 			/* read data */
2496cd3c7e2SThomas Langer 			priv->sfcmd &= ~SFCMD_DIR_WRITE;
2506cd3c7e2SThomas Langer 			do {
2516cd3c7e2SThomas Langer 				if ((flags & FALCON_SPI_XFER_END)
2526cd3c7e2SThomas Langer 				    && (bytelen <= 4)) {
2536cd3c7e2SThomas Langer 					priv->sfcmd &=
2546cd3c7e2SThomas Langer 						~SFCMD_KEEP_CS_KEEP_SELECTED;
2556cd3c7e2SThomas Langer 				}
2566cd3c7e2SThomas Langer 				len = (bytelen > 4) ? 4 : bytelen;
2576cd3c7e2SThomas Langer 				bytelen -= len;
2586cd3c7e2SThomas Langer 				ltq_ebu_w32(priv->sfcmd
2596cd3c7e2SThomas Langer 					| (len << SFCMD_DLEN_OFFSET), SFCMD);
2606cd3c7e2SThomas Langer 				priv->sfcmd &= ~(SFCMD_ALEN_MASK
2616cd3c7e2SThomas Langer 						 | SFCMD_DUMLEN_MASK);
2626cd3c7e2SThomas Langer 				do {
2636cd3c7e2SThomas Langer 					val = ltq_ebu_r32(SFSTAT);
2646cd3c7e2SThomas Langer 					if (val & SFSTAT_CMD_ERR) {
2656cd3c7e2SThomas Langer 						/* reset error status */
2666cd3c7e2SThomas Langer 						dev_err(dev, "SFSTAT: CMD_ERR");
2676cd3c7e2SThomas Langer 						dev_err(dev, " (%x)\n", val);
2686cd3c7e2SThomas Langer 						ltq_ebu_w32(SFSTAT_CMD_ERR,
2696cd3c7e2SThomas Langer 							SFSTAT);
2706cd3c7e2SThomas Langer 						return -EBADE;
2716cd3c7e2SThomas Langer 					}
2726cd3c7e2SThomas Langer 				} while (val & SFSTAT_CMD_PEND);
2736cd3c7e2SThomas Langer 				val = ltq_ebu_r32(SFDATA);
2746cd3c7e2SThomas Langer 				do {
2756cd3c7e2SThomas Langer 					*rxp = (val & 0xFF);
2766cd3c7e2SThomas Langer 					rxp++;
2776cd3c7e2SThomas Langer 					val >>= 8;
2786cd3c7e2SThomas Langer 					len--;
2796cd3c7e2SThomas Langer 				} while (len);
2806cd3c7e2SThomas Langer 			} while (bytelen);
2816cd3c7e2SThomas Langer 			state = state_end;
2826cd3c7e2SThomas Langer 			break;
2836cd3c7e2SThomas Langer 		}
2846cd3c7e2SThomas Langer 		case state_disable_cs:
2856cd3c7e2SThomas Langer 		{
2866cd3c7e2SThomas Langer 			priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED;
2876cd3c7e2SThomas Langer 			ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET),
2886cd3c7e2SThomas Langer 				SFCMD);
2896cd3c7e2SThomas Langer 			val = ltq_ebu_r32(SFSTAT);
2906cd3c7e2SThomas Langer 			if (val & SFSTAT_CMD_ERR) {
2916cd3c7e2SThomas Langer 				/* reset error status */
2926cd3c7e2SThomas Langer 				dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val);
2936cd3c7e2SThomas Langer 				ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT);
2946cd3c7e2SThomas Langer 				return -EBADE;
2956cd3c7e2SThomas Langer 			}
2966cd3c7e2SThomas Langer 			state = state_end;
2976cd3c7e2SThomas Langer 			break;
2986cd3c7e2SThomas Langer 		}
2996cd3c7e2SThomas Langer 		case state_end:
3006cd3c7e2SThomas Langer 			break;
3016cd3c7e2SThomas Langer 		}
3026cd3c7e2SThomas Langer 	} while (state != state_end);
3036cd3c7e2SThomas Langer 
3046cd3c7e2SThomas Langer 	return 0;
3056cd3c7e2SThomas Langer }
3066cd3c7e2SThomas Langer 
falcon_sflash_setup(struct spi_device * spi)3076cd3c7e2SThomas Langer static int falcon_sflash_setup(struct spi_device *spi)
3086cd3c7e2SThomas Langer {
3096cd3c7e2SThomas Langer 	unsigned int i;
3106cd3c7e2SThomas Langer 	unsigned long flags;
3116cd3c7e2SThomas Langer 
3126cd3c7e2SThomas Langer 	spin_lock_irqsave(&ebu_lock, flags);
3136cd3c7e2SThomas Langer 
3146cd3c7e2SThomas Langer 	if (spi->max_speed_hz >= CLOCK_100M) {
3156cd3c7e2SThomas Langer 		/* set EBU clock to 100 MHz */
3166cd3c7e2SThomas Langer 		ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC);
3176cd3c7e2SThomas Langer 		i = 1; /* divider */
3186cd3c7e2SThomas Langer 	} else {
3196cd3c7e2SThomas Langer 		/* set EBU clock to 50 MHz */
3206cd3c7e2SThomas Langer 		ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC);
3216cd3c7e2SThomas Langer 
3226cd3c7e2SThomas Langer 		/* search for suitable divider */
3236cd3c7e2SThomas Langer 		for (i = 1; i < 7; i++) {
3246cd3c7e2SThomas Langer 			if (CLOCK_50M / i <= spi->max_speed_hz)
3256cd3c7e2SThomas Langer 				break;
3266cd3c7e2SThomas Langer 		}
3276cd3c7e2SThomas Langer 	}
3286cd3c7e2SThomas Langer 
3296cd3c7e2SThomas Langer 	/* setup period of serial clock */
3306cd3c7e2SThomas Langer 	ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK
3316cd3c7e2SThomas Langer 		     | SFTIME_SCKR_POS_MASK
3326cd3c7e2SThomas Langer 		     | SFTIME_SCK_PER_MASK,
3336cd3c7e2SThomas Langer 		     (i << SFTIME_SCKR_POS_OFFSET)
3346cd3c7e2SThomas Langer 		     | (i << (SFTIME_SCK_PER_OFFSET + 1)),
3356cd3c7e2SThomas Langer 		     SFTIME);
3366cd3c7e2SThomas Langer 
3376cd3c7e2SThomas Langer 	/*
3386cd3c7e2SThomas Langer 	 * set some bits of unused_wd, to not trigger HOLD/WP
3396cd3c7e2SThomas Langer 	 * signals on non QUAD flashes
3406cd3c7e2SThomas Langer 	 */
3416cd3c7e2SThomas Langer 	ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO);
3426cd3c7e2SThomas Langer 
3436cd3c7e2SThomas Langer 	ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX,
3446cd3c7e2SThomas Langer 			BUSRCON0);
3456cd3c7e2SThomas Langer 	ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0);
3466cd3c7e2SThomas Langer 	/* set address wrap around to maximum for 24-bit addresses */
3476cd3c7e2SThomas Langer 	ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON);
3486cd3c7e2SThomas Langer 
3496cd3c7e2SThomas Langer 	spin_unlock_irqrestore(&ebu_lock, flags);
3506cd3c7e2SThomas Langer 
3516cd3c7e2SThomas Langer 	return 0;
3526cd3c7e2SThomas Langer }
3536cd3c7e2SThomas Langer 
falcon_sflash_xfer_one(struct spi_controller * host,struct spi_message * m)354*45d95911SYang Yingliang static int falcon_sflash_xfer_one(struct spi_controller *host,
3556cd3c7e2SThomas Langer 					struct spi_message *m)
3566cd3c7e2SThomas Langer {
357*45d95911SYang Yingliang 	struct falcon_sflash *priv = spi_controller_get_devdata(host);
3586cd3c7e2SThomas Langer 	struct spi_transfer *t;
3596cd3c7e2SThomas Langer 	unsigned long spi_flags;
3606cd3c7e2SThomas Langer 	unsigned long flags;
3616cd3c7e2SThomas Langer 	int ret = 0;
3626cd3c7e2SThomas Langer 
3636cd3c7e2SThomas Langer 	priv->sfcmd = 0;
3646cd3c7e2SThomas Langer 	m->actual_length = 0;
3656cd3c7e2SThomas Langer 
3666cd3c7e2SThomas Langer 	spi_flags = FALCON_SPI_XFER_BEGIN;
3676cd3c7e2SThomas Langer 	list_for_each_entry(t, &m->transfers, transfer_list) {
3686cd3c7e2SThomas Langer 		if (list_is_last(&t->transfer_list, &m->transfers))
3696cd3c7e2SThomas Langer 			spi_flags |= FALCON_SPI_XFER_END;
3706cd3c7e2SThomas Langer 
3716cd3c7e2SThomas Langer 		spin_lock_irqsave(&ebu_lock, flags);
3726cd3c7e2SThomas Langer 		ret = falcon_sflash_xfer(m->spi, t, spi_flags);
3736cd3c7e2SThomas Langer 		spin_unlock_irqrestore(&ebu_lock, flags);
3746cd3c7e2SThomas Langer 
3756cd3c7e2SThomas Langer 		if (ret)
3766cd3c7e2SThomas Langer 			break;
3776cd3c7e2SThomas Langer 
3786cd3c7e2SThomas Langer 		m->actual_length += t->len;
3796cd3c7e2SThomas Langer 
380a886010cSAlexandru Ardelean 		WARN_ON(t->delay.value || t->cs_change);
3816cd3c7e2SThomas Langer 		spi_flags = 0;
3826cd3c7e2SThomas Langer 	}
3836cd3c7e2SThomas Langer 
3846cd3c7e2SThomas Langer 	m->status = ret;
385*45d95911SYang Yingliang 	spi_finalize_current_message(host);
3866cd3c7e2SThomas Langer 
3876cd3c7e2SThomas Langer 	return 0;
3886cd3c7e2SThomas Langer }
3896cd3c7e2SThomas Langer 
falcon_sflash_probe(struct platform_device * pdev)390fd4a319bSGrant Likely static int falcon_sflash_probe(struct platform_device *pdev)
3916cd3c7e2SThomas Langer {
3926cd3c7e2SThomas Langer 	struct falcon_sflash *priv;
393*45d95911SYang Yingliang 	struct spi_controller *host;
3946cd3c7e2SThomas Langer 	int ret;
3956cd3c7e2SThomas Langer 
396*45d95911SYang Yingliang 	host = spi_alloc_host(&pdev->dev, sizeof(*priv));
397*45d95911SYang Yingliang 	if (!host)
3986cd3c7e2SThomas Langer 		return -ENOMEM;
3996cd3c7e2SThomas Langer 
400*45d95911SYang Yingliang 	priv = spi_controller_get_devdata(host);
401*45d95911SYang Yingliang 	priv->host = host;
4026cd3c7e2SThomas Langer 
403*45d95911SYang Yingliang 	host->mode_bits = SPI_MODE_3;
404*45d95911SYang Yingliang 	host->flags = SPI_CONTROLLER_HALF_DUPLEX;
405*45d95911SYang Yingliang 	host->setup = falcon_sflash_setup;
406*45d95911SYang Yingliang 	host->transfer_one_message = falcon_sflash_xfer_one;
407*45d95911SYang Yingliang 	host->dev.of_node = pdev->dev.of_node;
4086cd3c7e2SThomas Langer 
409*45d95911SYang Yingliang 	ret = devm_spi_register_controller(&pdev->dev, host);
4106cd3c7e2SThomas Langer 	if (ret)
411*45d95911SYang Yingliang 		spi_controller_put(host);
4126cd3c7e2SThomas Langer 	return ret;
4136cd3c7e2SThomas Langer }
4146cd3c7e2SThomas Langer 
4156cd3c7e2SThomas Langer static const struct of_device_id falcon_sflash_match[] = {
4166cd3c7e2SThomas Langer 	{ .compatible = "lantiq,sflash-falcon" },
4176cd3c7e2SThomas Langer 	{},
4186cd3c7e2SThomas Langer };
4196cd3c7e2SThomas Langer MODULE_DEVICE_TABLE(of, falcon_sflash_match);
4206cd3c7e2SThomas Langer 
4216cd3c7e2SThomas Langer static struct platform_driver falcon_sflash_driver = {
4226cd3c7e2SThomas Langer 	.probe	= falcon_sflash_probe,
4236cd3c7e2SThomas Langer 	.driver = {
4246cd3c7e2SThomas Langer 		.name	= DRV_NAME,
4256cd3c7e2SThomas Langer 		.of_match_table = falcon_sflash_match,
4266cd3c7e2SThomas Langer 	}
4276cd3c7e2SThomas Langer };
4286cd3c7e2SThomas Langer 
4296cd3c7e2SThomas Langer module_platform_driver(falcon_sflash_driver);
4306cd3c7e2SThomas Langer 
4316cd3c7e2SThomas Langer MODULE_LICENSE("GPL");
4326cd3c7e2SThomas Langer MODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver");
433