18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <lantiq_soc.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define DRV_NAME "sflash-falcon" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define FALCON_SPI_XFER_BEGIN (1 << 0) 208c2ecf20Sopenharmony_ci#define FALCON_SPI_XFER_END (1 << 1) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Bus Read Configuration Register0 */ 238c2ecf20Sopenharmony_ci#define BUSRCON0 0x00000010 248c2ecf20Sopenharmony_ci/* Bus Write Configuration Register0 */ 258c2ecf20Sopenharmony_ci#define BUSWCON0 0x00000018 268c2ecf20Sopenharmony_ci/* Serial Flash Configuration Register */ 278c2ecf20Sopenharmony_ci#define SFCON 0x00000080 288c2ecf20Sopenharmony_ci/* Serial Flash Time Register */ 298c2ecf20Sopenharmony_ci#define SFTIME 0x00000084 308c2ecf20Sopenharmony_ci/* Serial Flash Status Register */ 318c2ecf20Sopenharmony_ci#define SFSTAT 0x00000088 328c2ecf20Sopenharmony_ci/* Serial Flash Command Register */ 338c2ecf20Sopenharmony_ci#define SFCMD 0x0000008C 348c2ecf20Sopenharmony_ci/* Serial Flash Address Register */ 358c2ecf20Sopenharmony_ci#define SFADDR 0x00000090 368c2ecf20Sopenharmony_ci/* Serial Flash Data Register */ 378c2ecf20Sopenharmony_ci#define SFDATA 0x00000094 388c2ecf20Sopenharmony_ci/* Serial Flash I/O Control Register */ 398c2ecf20Sopenharmony_ci#define SFIO 0x00000098 408c2ecf20Sopenharmony_ci/* EBU Clock Control Register */ 418c2ecf20Sopenharmony_ci#define EBUCC 0x000000C4 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Dummy Phase Length */ 448c2ecf20Sopenharmony_ci#define SFCMD_DUMLEN_OFFSET 16 458c2ecf20Sopenharmony_ci#define SFCMD_DUMLEN_MASK 0x000F0000 468c2ecf20Sopenharmony_ci/* Chip Select */ 478c2ecf20Sopenharmony_ci#define SFCMD_CS_OFFSET 24 488c2ecf20Sopenharmony_ci#define SFCMD_CS_MASK 0x07000000 498c2ecf20Sopenharmony_ci/* field offset */ 508c2ecf20Sopenharmony_ci#define SFCMD_ALEN_OFFSET 20 518c2ecf20Sopenharmony_ci#define SFCMD_ALEN_MASK 0x00700000 528c2ecf20Sopenharmony_ci/* SCK Rise-edge Position */ 538c2ecf20Sopenharmony_ci#define SFTIME_SCKR_POS_OFFSET 8 548c2ecf20Sopenharmony_ci#define SFTIME_SCKR_POS_MASK 0x00000F00 558c2ecf20Sopenharmony_ci/* SCK Period */ 568c2ecf20Sopenharmony_ci#define SFTIME_SCK_PER_OFFSET 0 578c2ecf20Sopenharmony_ci#define SFTIME_SCK_PER_MASK 0x0000000F 588c2ecf20Sopenharmony_ci/* SCK Fall-edge Position */ 598c2ecf20Sopenharmony_ci#define SFTIME_SCKF_POS_OFFSET 12 608c2ecf20Sopenharmony_ci#define SFTIME_SCKF_POS_MASK 0x0000F000 618c2ecf20Sopenharmony_ci/* Device Size */ 628c2ecf20Sopenharmony_ci#define SFCON_DEV_SIZE_A23_0 0x03000000 638c2ecf20Sopenharmony_ci#define SFCON_DEV_SIZE_MASK 0x0F000000 648c2ecf20Sopenharmony_ci/* Read Data Position */ 658c2ecf20Sopenharmony_ci#define SFTIME_RD_POS_MASK 0x000F0000 668c2ecf20Sopenharmony_ci/* Data Output */ 678c2ecf20Sopenharmony_ci#define SFIO_UNUSED_WD_MASK 0x0000000F 688c2ecf20Sopenharmony_ci/* Command Opcode mask */ 698c2ecf20Sopenharmony_ci#define SFCMD_OPC_MASK 0x000000FF 708c2ecf20Sopenharmony_ci/* dlen bytes of data to write */ 718c2ecf20Sopenharmony_ci#define SFCMD_DIR_WRITE 0x00000100 728c2ecf20Sopenharmony_ci/* Data Length offset */ 738c2ecf20Sopenharmony_ci#define SFCMD_DLEN_OFFSET 9 748c2ecf20Sopenharmony_ci/* Command Error */ 758c2ecf20Sopenharmony_ci#define SFSTAT_CMD_ERR 0x20000000 768c2ecf20Sopenharmony_ci/* Access Command Pending */ 778c2ecf20Sopenharmony_ci#define SFSTAT_CMD_PEND 0x00400000 788c2ecf20Sopenharmony_ci/* Frequency set to 100MHz. */ 798c2ecf20Sopenharmony_ci#define EBUCC_EBUDIV_SELF100 0x00000001 808c2ecf20Sopenharmony_ci/* Serial Flash */ 818c2ecf20Sopenharmony_ci#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000 828c2ecf20Sopenharmony_ci/* 8-bit multiplexed */ 838c2ecf20Sopenharmony_ci#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000 848c2ecf20Sopenharmony_ci/* Serial Flash */ 858c2ecf20Sopenharmony_ci#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000 868c2ecf20Sopenharmony_ci/* Chip Select after opcode */ 878c2ecf20Sopenharmony_ci#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define CLOCK_100M 100000000 908c2ecf20Sopenharmony_ci#define CLOCK_50M 50000000 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct falcon_sflash { 938c2ecf20Sopenharmony_ci u32 sfcmd; /* for caching of opcode, direction, ... */ 948c2ecf20Sopenharmony_ci struct spi_master *master; 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciint falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, 988c2ecf20Sopenharmony_ci unsigned long flags) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 1018c2ecf20Sopenharmony_ci struct falcon_sflash *priv = spi_master_get_devdata(spi->master); 1028c2ecf20Sopenharmony_ci const u8 *txp = t->tx_buf; 1038c2ecf20Sopenharmony_ci u8 *rxp = t->rx_buf; 1048c2ecf20Sopenharmony_ci unsigned int bytelen = ((8 * t->len + 7) / 8); 1058c2ecf20Sopenharmony_ci unsigned int len, alen, dumlen; 1068c2ecf20Sopenharmony_ci u32 val; 1078c2ecf20Sopenharmony_ci enum { 1088c2ecf20Sopenharmony_ci state_init, 1098c2ecf20Sopenharmony_ci state_command_prepare, 1108c2ecf20Sopenharmony_ci state_write, 1118c2ecf20Sopenharmony_ci state_read, 1128c2ecf20Sopenharmony_ci state_disable_cs, 1138c2ecf20Sopenharmony_ci state_end 1148c2ecf20Sopenharmony_ci } state = state_init; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci do { 1178c2ecf20Sopenharmony_ci switch (state) { 1188c2ecf20Sopenharmony_ci case state_init: /* detect phase of upper layer sequence */ 1198c2ecf20Sopenharmony_ci { 1208c2ecf20Sopenharmony_ci /* initial write ? */ 1218c2ecf20Sopenharmony_ci if (flags & FALCON_SPI_XFER_BEGIN) { 1228c2ecf20Sopenharmony_ci if (!txp) { 1238c2ecf20Sopenharmony_ci dev_err(dev, 1248c2ecf20Sopenharmony_ci "BEGIN without tx data!\n"); 1258c2ecf20Sopenharmony_ci return -ENODATA; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * Prepare the parts of the sfcmd register, 1298c2ecf20Sopenharmony_ci * which should not change during a sequence! 1308c2ecf20Sopenharmony_ci * Only exception are the length fields, 1318c2ecf20Sopenharmony_ci * especially alen and dumlen. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci priv->sfcmd = ((spi->chip_select 1358c2ecf20Sopenharmony_ci << SFCMD_CS_OFFSET) 1368c2ecf20Sopenharmony_ci & SFCMD_CS_MASK); 1378c2ecf20Sopenharmony_ci priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED; 1388c2ecf20Sopenharmony_ci priv->sfcmd |= *txp; 1398c2ecf20Sopenharmony_ci txp++; 1408c2ecf20Sopenharmony_ci bytelen--; 1418c2ecf20Sopenharmony_ci if (bytelen) { 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * more data: 1448c2ecf20Sopenharmony_ci * maybe address and/or dummy 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci state = state_command_prepare; 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci } else { 1498c2ecf20Sopenharmony_ci dev_dbg(dev, "write cmd %02X\n", 1508c2ecf20Sopenharmony_ci priv->sfcmd & SFCMD_OPC_MASK); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci /* continued write ? */ 1548c2ecf20Sopenharmony_ci if (txp && bytelen) { 1558c2ecf20Sopenharmony_ci state = state_write; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci /* read data? */ 1598c2ecf20Sopenharmony_ci if (rxp && bytelen) { 1608c2ecf20Sopenharmony_ci state = state_read; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci /* end of sequence? */ 1648c2ecf20Sopenharmony_ci if (flags & FALCON_SPI_XFER_END) 1658c2ecf20Sopenharmony_ci state = state_disable_cs; 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci state = state_end; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci /* collect tx data for address and dummy phase */ 1718c2ecf20Sopenharmony_ci case state_command_prepare: 1728c2ecf20Sopenharmony_ci { 1738c2ecf20Sopenharmony_ci /* txp is valid, already checked */ 1748c2ecf20Sopenharmony_ci val = 0; 1758c2ecf20Sopenharmony_ci alen = 0; 1768c2ecf20Sopenharmony_ci dumlen = 0; 1778c2ecf20Sopenharmony_ci while (bytelen > 0) { 1788c2ecf20Sopenharmony_ci if (alen < 3) { 1798c2ecf20Sopenharmony_ci val = (val << 8) | (*txp++); 1808c2ecf20Sopenharmony_ci alen++; 1818c2ecf20Sopenharmony_ci } else if ((dumlen < 15) && (*txp == 0)) { 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * assume dummy bytes are set to 0 1848c2ecf20Sopenharmony_ci * from upper layer 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci dumlen++; 1878c2ecf20Sopenharmony_ci txp++; 1888c2ecf20Sopenharmony_ci } else { 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci bytelen--; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK); 1948c2ecf20Sopenharmony_ci priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) | 1958c2ecf20Sopenharmony_ci (dumlen << SFCMD_DUMLEN_OFFSET); 1968c2ecf20Sopenharmony_ci if (alen > 0) 1978c2ecf20Sopenharmony_ci ltq_ebu_w32(val, SFADDR); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n", 2008c2ecf20Sopenharmony_ci priv->sfcmd & SFCMD_OPC_MASK, 2018c2ecf20Sopenharmony_ci alen, val, dumlen); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (bytelen > 0) { 2048c2ecf20Sopenharmony_ci /* continue with write */ 2058c2ecf20Sopenharmony_ci state = state_write; 2068c2ecf20Sopenharmony_ci } else if (flags & FALCON_SPI_XFER_END) { 2078c2ecf20Sopenharmony_ci /* end of sequence? */ 2088c2ecf20Sopenharmony_ci state = state_disable_cs; 2098c2ecf20Sopenharmony_ci } else { 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * go to end and expect another 2128c2ecf20Sopenharmony_ci * call (read or write) 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci state = state_end; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci case state_write: 2198c2ecf20Sopenharmony_ci { 2208c2ecf20Sopenharmony_ci /* txp still valid */ 2218c2ecf20Sopenharmony_ci priv->sfcmd |= SFCMD_DIR_WRITE; 2228c2ecf20Sopenharmony_ci len = 0; 2238c2ecf20Sopenharmony_ci val = 0; 2248c2ecf20Sopenharmony_ci do { 2258c2ecf20Sopenharmony_ci if (bytelen--) 2268c2ecf20Sopenharmony_ci val |= (*txp++) << (8 * len++); 2278c2ecf20Sopenharmony_ci if ((flags & FALCON_SPI_XFER_END) 2288c2ecf20Sopenharmony_ci && (bytelen == 0)) { 2298c2ecf20Sopenharmony_ci priv->sfcmd &= 2308c2ecf20Sopenharmony_ci ~SFCMD_KEEP_CS_KEEP_SELECTED; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci if ((len == 4) || (bytelen == 0)) { 2338c2ecf20Sopenharmony_ci ltq_ebu_w32(val, SFDATA); 2348c2ecf20Sopenharmony_ci ltq_ebu_w32(priv->sfcmd 2358c2ecf20Sopenharmony_ci | (len<<SFCMD_DLEN_OFFSET), 2368c2ecf20Sopenharmony_ci SFCMD); 2378c2ecf20Sopenharmony_ci len = 0; 2388c2ecf20Sopenharmony_ci val = 0; 2398c2ecf20Sopenharmony_ci priv->sfcmd &= ~(SFCMD_ALEN_MASK 2408c2ecf20Sopenharmony_ci | SFCMD_DUMLEN_MASK); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci } while (bytelen); 2438c2ecf20Sopenharmony_ci state = state_end; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci case state_read: 2478c2ecf20Sopenharmony_ci { 2488c2ecf20Sopenharmony_ci /* read data */ 2498c2ecf20Sopenharmony_ci priv->sfcmd &= ~SFCMD_DIR_WRITE; 2508c2ecf20Sopenharmony_ci do { 2518c2ecf20Sopenharmony_ci if ((flags & FALCON_SPI_XFER_END) 2528c2ecf20Sopenharmony_ci && (bytelen <= 4)) { 2538c2ecf20Sopenharmony_ci priv->sfcmd &= 2548c2ecf20Sopenharmony_ci ~SFCMD_KEEP_CS_KEEP_SELECTED; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci len = (bytelen > 4) ? 4 : bytelen; 2578c2ecf20Sopenharmony_ci bytelen -= len; 2588c2ecf20Sopenharmony_ci ltq_ebu_w32(priv->sfcmd 2598c2ecf20Sopenharmony_ci | (len << SFCMD_DLEN_OFFSET), SFCMD); 2608c2ecf20Sopenharmony_ci priv->sfcmd &= ~(SFCMD_ALEN_MASK 2618c2ecf20Sopenharmony_ci | SFCMD_DUMLEN_MASK); 2628c2ecf20Sopenharmony_ci do { 2638c2ecf20Sopenharmony_ci val = ltq_ebu_r32(SFSTAT); 2648c2ecf20Sopenharmony_ci if (val & SFSTAT_CMD_ERR) { 2658c2ecf20Sopenharmony_ci /* reset error status */ 2668c2ecf20Sopenharmony_ci dev_err(dev, "SFSTAT: CMD_ERR"); 2678c2ecf20Sopenharmony_ci dev_err(dev, " (%x)\n", val); 2688c2ecf20Sopenharmony_ci ltq_ebu_w32(SFSTAT_CMD_ERR, 2698c2ecf20Sopenharmony_ci SFSTAT); 2708c2ecf20Sopenharmony_ci return -EBADE; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } while (val & SFSTAT_CMD_PEND); 2738c2ecf20Sopenharmony_ci val = ltq_ebu_r32(SFDATA); 2748c2ecf20Sopenharmony_ci do { 2758c2ecf20Sopenharmony_ci *rxp = (val & 0xFF); 2768c2ecf20Sopenharmony_ci rxp++; 2778c2ecf20Sopenharmony_ci val >>= 8; 2788c2ecf20Sopenharmony_ci len--; 2798c2ecf20Sopenharmony_ci } while (len); 2808c2ecf20Sopenharmony_ci } while (bytelen); 2818c2ecf20Sopenharmony_ci state = state_end; 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci case state_disable_cs: 2858c2ecf20Sopenharmony_ci { 2868c2ecf20Sopenharmony_ci priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED; 2878c2ecf20Sopenharmony_ci ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET), 2888c2ecf20Sopenharmony_ci SFCMD); 2898c2ecf20Sopenharmony_ci val = ltq_ebu_r32(SFSTAT); 2908c2ecf20Sopenharmony_ci if (val & SFSTAT_CMD_ERR) { 2918c2ecf20Sopenharmony_ci /* reset error status */ 2928c2ecf20Sopenharmony_ci dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val); 2938c2ecf20Sopenharmony_ci ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT); 2948c2ecf20Sopenharmony_ci return -EBADE; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci state = state_end; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci case state_end: 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci } while (state != state_end); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int falcon_sflash_setup(struct spi_device *spi) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci unsigned int i; 3108c2ecf20Sopenharmony_ci unsigned long flags; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci spin_lock_irqsave(&ebu_lock, flags); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (spi->max_speed_hz >= CLOCK_100M) { 3158c2ecf20Sopenharmony_ci /* set EBU clock to 100 MHz */ 3168c2ecf20Sopenharmony_ci ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC); 3178c2ecf20Sopenharmony_ci i = 1; /* divider */ 3188c2ecf20Sopenharmony_ci } else { 3198c2ecf20Sopenharmony_ci /* set EBU clock to 50 MHz */ 3208c2ecf20Sopenharmony_ci ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* search for suitable divider */ 3238c2ecf20Sopenharmony_ci for (i = 1; i < 7; i++) { 3248c2ecf20Sopenharmony_ci if (CLOCK_50M / i <= spi->max_speed_hz) 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* setup period of serial clock */ 3308c2ecf20Sopenharmony_ci ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK 3318c2ecf20Sopenharmony_ci | SFTIME_SCKR_POS_MASK 3328c2ecf20Sopenharmony_ci | SFTIME_SCK_PER_MASK, 3338c2ecf20Sopenharmony_ci (i << SFTIME_SCKR_POS_OFFSET) 3348c2ecf20Sopenharmony_ci | (i << (SFTIME_SCK_PER_OFFSET + 1)), 3358c2ecf20Sopenharmony_ci SFTIME); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* 3388c2ecf20Sopenharmony_ci * set some bits of unused_wd, to not trigger HOLD/WP 3398c2ecf20Sopenharmony_ci * signals on non QUAD flashes 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX, 3448c2ecf20Sopenharmony_ci BUSRCON0); 3458c2ecf20Sopenharmony_ci ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0); 3468c2ecf20Sopenharmony_ci /* set address wrap around to maximum for 24-bit addresses */ 3478c2ecf20Sopenharmony_ci ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ebu_lock, flags); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int falcon_sflash_xfer_one(struct spi_master *master, 3558c2ecf20Sopenharmony_ci struct spi_message *m) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct falcon_sflash *priv = spi_master_get_devdata(master); 3588c2ecf20Sopenharmony_ci struct spi_transfer *t; 3598c2ecf20Sopenharmony_ci unsigned long spi_flags; 3608c2ecf20Sopenharmony_ci unsigned long flags; 3618c2ecf20Sopenharmony_ci int ret = 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci priv->sfcmd = 0; 3648c2ecf20Sopenharmony_ci m->actual_length = 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci spi_flags = FALCON_SPI_XFER_BEGIN; 3678c2ecf20Sopenharmony_ci list_for_each_entry(t, &m->transfers, transfer_list) { 3688c2ecf20Sopenharmony_ci if (list_is_last(&t->transfer_list, &m->transfers)) 3698c2ecf20Sopenharmony_ci spi_flags |= FALCON_SPI_XFER_END; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci spin_lock_irqsave(&ebu_lock, flags); 3728c2ecf20Sopenharmony_ci ret = falcon_sflash_xfer(m->spi, t, spi_flags); 3738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ebu_lock, flags); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (ret) 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci m->actual_length += t->len; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci WARN_ON(t->delay_usecs || t->delay.value || t->cs_change); 3818c2ecf20Sopenharmony_ci spi_flags = 0; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci m->status = ret; 3858c2ecf20Sopenharmony_ci spi_finalize_current_message(master); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int falcon_sflash_probe(struct platform_device *pdev) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct falcon_sflash *priv; 3938c2ecf20Sopenharmony_ci struct spi_master *master; 3948c2ecf20Sopenharmony_ci int ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*priv)); 3978c2ecf20Sopenharmony_ci if (!master) 3988c2ecf20Sopenharmony_ci return -ENOMEM; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci priv = spi_master_get_devdata(master); 4018c2ecf20Sopenharmony_ci priv->master = master; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci master->mode_bits = SPI_MODE_3; 4048c2ecf20Sopenharmony_ci master->flags = SPI_MASTER_HALF_DUPLEX; 4058c2ecf20Sopenharmony_ci master->setup = falcon_sflash_setup; 4068c2ecf20Sopenharmony_ci master->transfer_one_message = falcon_sflash_xfer_one; 4078c2ecf20Sopenharmony_ci master->dev.of_node = pdev->dev.of_node; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ret = devm_spi_register_master(&pdev->dev, master); 4108c2ecf20Sopenharmony_ci if (ret) 4118c2ecf20Sopenharmony_ci spi_master_put(master); 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const struct of_device_id falcon_sflash_match[] = { 4168c2ecf20Sopenharmony_ci { .compatible = "lantiq,sflash-falcon" }, 4178c2ecf20Sopenharmony_ci {}, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, falcon_sflash_match); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic struct platform_driver falcon_sflash_driver = { 4228c2ecf20Sopenharmony_ci .probe = falcon_sflash_probe, 4238c2ecf20Sopenharmony_ci .driver = { 4248c2ecf20Sopenharmony_ci .name = DRV_NAME, 4258c2ecf20Sopenharmony_ci .of_match_table = falcon_sflash_match, 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cimodule_platform_driver(falcon_sflash_driver); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver"); 433