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