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, ... */ 946cd3c7e2SThomas Langer struct spi_master *master; 956cd3c7e2SThomas Langer }; 966cd3c7e2SThomas Langer 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; 1016cd3c7e2SThomas Langer struct falcon_sflash *priv = spi_master_get_devdata(spi->master); 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 1346cd3c7e2SThomas Langer priv->sfcmd = ((spi->chip_select 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 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 3546cd3c7e2SThomas Langer static int falcon_sflash_xfer_one(struct spi_master *master, 3556cd3c7e2SThomas Langer struct spi_message *m) 3566cd3c7e2SThomas Langer { 3576cd3c7e2SThomas Langer struct falcon_sflash *priv = spi_master_get_devdata(master); 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 380*a886010cSAlexandru Ardelean WARN_ON(t->delay.value || t->cs_change); 3816cd3c7e2SThomas Langer spi_flags = 0; 3826cd3c7e2SThomas Langer } 3836cd3c7e2SThomas Langer 3846cd3c7e2SThomas Langer m->status = ret; 385737a7c43SJohn Crispin spi_finalize_current_message(master); 3866cd3c7e2SThomas Langer 3876cd3c7e2SThomas Langer return 0; 3886cd3c7e2SThomas Langer } 3896cd3c7e2SThomas Langer 390fd4a319bSGrant Likely static int falcon_sflash_probe(struct platform_device *pdev) 3916cd3c7e2SThomas Langer { 3926cd3c7e2SThomas Langer struct falcon_sflash *priv; 3936cd3c7e2SThomas Langer struct spi_master *master; 3946cd3c7e2SThomas Langer int ret; 3956cd3c7e2SThomas Langer 3966cd3c7e2SThomas Langer master = spi_alloc_master(&pdev->dev, sizeof(*priv)); 3976cd3c7e2SThomas Langer if (!master) 3986cd3c7e2SThomas Langer return -ENOMEM; 3996cd3c7e2SThomas Langer 4006cd3c7e2SThomas Langer priv = spi_master_get_devdata(master); 4016cd3c7e2SThomas Langer priv->master = master; 4026cd3c7e2SThomas Langer 4036cd3c7e2SThomas Langer master->mode_bits = SPI_MODE_3; 404ec3eaecaSJohn Crispin master->flags = SPI_MASTER_HALF_DUPLEX; 4056cd3c7e2SThomas Langer master->setup = falcon_sflash_setup; 4066cd3c7e2SThomas Langer master->transfer_one_message = falcon_sflash_xfer_one; 4076cd3c7e2SThomas Langer master->dev.of_node = pdev->dev.of_node; 4086cd3c7e2SThomas Langer 409999b6e93SJingoo Han ret = devm_spi_register_master(&pdev->dev, master); 4106cd3c7e2SThomas Langer if (ret) 4116cd3c7e2SThomas Langer spi_master_put(master); 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