18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Broadcom BRCMSTB, NSP, NS2, Cygnus SPI Controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2016 Broadcom 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/ioport.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 228c2ecf20Sopenharmony_ci#include <linux/mtd/spi-nor.h> 238c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci#include "spi-bcm-qspi.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DRIVER_NAME "bcm_qspi" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* BSPI register offsets */ 318c2ecf20Sopenharmony_ci#define BSPI_REVISION_ID 0x000 328c2ecf20Sopenharmony_ci#define BSPI_SCRATCH 0x004 338c2ecf20Sopenharmony_ci#define BSPI_MAST_N_BOOT_CTRL 0x008 348c2ecf20Sopenharmony_ci#define BSPI_BUSY_STATUS 0x00c 358c2ecf20Sopenharmony_ci#define BSPI_INTR_STATUS 0x010 368c2ecf20Sopenharmony_ci#define BSPI_B0_STATUS 0x014 378c2ecf20Sopenharmony_ci#define BSPI_B0_CTRL 0x018 388c2ecf20Sopenharmony_ci#define BSPI_B1_STATUS 0x01c 398c2ecf20Sopenharmony_ci#define BSPI_B1_CTRL 0x020 408c2ecf20Sopenharmony_ci#define BSPI_STRAP_OVERRIDE_CTRL 0x024 418c2ecf20Sopenharmony_ci#define BSPI_FLEX_MODE_ENABLE 0x028 428c2ecf20Sopenharmony_ci#define BSPI_BITS_PER_CYCLE 0x02c 438c2ecf20Sopenharmony_ci#define BSPI_BITS_PER_PHASE 0x030 448c2ecf20Sopenharmony_ci#define BSPI_CMD_AND_MODE_BYTE 0x034 458c2ecf20Sopenharmony_ci#define BSPI_BSPI_FLASH_UPPER_ADDR_BYTE 0x038 468c2ecf20Sopenharmony_ci#define BSPI_BSPI_XOR_VALUE 0x03c 478c2ecf20Sopenharmony_ci#define BSPI_BSPI_XOR_ENABLE 0x040 488c2ecf20Sopenharmony_ci#define BSPI_BSPI_PIO_MODE_ENABLE 0x044 498c2ecf20Sopenharmony_ci#define BSPI_BSPI_PIO_IODIR 0x048 508c2ecf20Sopenharmony_ci#define BSPI_BSPI_PIO_DATA 0x04c 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* RAF register offsets */ 538c2ecf20Sopenharmony_ci#define BSPI_RAF_START_ADDR 0x100 548c2ecf20Sopenharmony_ci#define BSPI_RAF_NUM_WORDS 0x104 558c2ecf20Sopenharmony_ci#define BSPI_RAF_CTRL 0x108 568c2ecf20Sopenharmony_ci#define BSPI_RAF_FULLNESS 0x10c 578c2ecf20Sopenharmony_ci#define BSPI_RAF_WATERMARK 0x110 588c2ecf20Sopenharmony_ci#define BSPI_RAF_STATUS 0x114 598c2ecf20Sopenharmony_ci#define BSPI_RAF_READ_DATA 0x118 608c2ecf20Sopenharmony_ci#define BSPI_RAF_WORD_CNT 0x11c 618c2ecf20Sopenharmony_ci#define BSPI_RAF_CURR_ADDR 0x120 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Override mode masks */ 648c2ecf20Sopenharmony_ci#define BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE BIT(0) 658c2ecf20Sopenharmony_ci#define BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL BIT(1) 668c2ecf20Sopenharmony_ci#define BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE BIT(2) 678c2ecf20Sopenharmony_ci#define BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD BIT(3) 688c2ecf20Sopenharmony_ci#define BSPI_STRAP_OVERRIDE_CTRL_ENDAIN_MODE BIT(4) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define BSPI_ADDRLEN_3BYTES 3 718c2ecf20Sopenharmony_ci#define BSPI_ADDRLEN_4BYTES 4 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define BSPI_RAF_STATUS_FIFO_EMPTY_MASK BIT(1) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define BSPI_RAF_CTRL_START_MASK BIT(0) 768c2ecf20Sopenharmony_ci#define BSPI_RAF_CTRL_CLEAR_MASK BIT(1) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define BSPI_BPP_MODE_SELECT_MASK BIT(8) 798c2ecf20Sopenharmony_ci#define BSPI_BPP_ADDR_SELECT_MASK BIT(16) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define BSPI_READ_LENGTH 256 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* MSPI register offsets */ 848c2ecf20Sopenharmony_ci#define MSPI_SPCR0_LSB 0x000 858c2ecf20Sopenharmony_ci#define MSPI_SPCR0_MSB 0x004 868c2ecf20Sopenharmony_ci#define MSPI_SPCR1_LSB 0x008 878c2ecf20Sopenharmony_ci#define MSPI_SPCR1_MSB 0x00c 888c2ecf20Sopenharmony_ci#define MSPI_NEWQP 0x010 898c2ecf20Sopenharmony_ci#define MSPI_ENDQP 0x014 908c2ecf20Sopenharmony_ci#define MSPI_SPCR2 0x018 918c2ecf20Sopenharmony_ci#define MSPI_MSPI_STATUS 0x020 928c2ecf20Sopenharmony_ci#define MSPI_CPTQP 0x024 938c2ecf20Sopenharmony_ci#define MSPI_SPCR3 0x028 948c2ecf20Sopenharmony_ci#define MSPI_REV 0x02c 958c2ecf20Sopenharmony_ci#define MSPI_TXRAM 0x040 968c2ecf20Sopenharmony_ci#define MSPI_RXRAM 0x0c0 978c2ecf20Sopenharmony_ci#define MSPI_CDRAM 0x140 988c2ecf20Sopenharmony_ci#define MSPI_WRITE_LOCK 0x180 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define MSPI_MASTER_BIT BIT(7) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define MSPI_NUM_CDRAM 16 1038c2ecf20Sopenharmony_ci#define MSPI_CDRAM_CONT_BIT BIT(7) 1048c2ecf20Sopenharmony_ci#define MSPI_CDRAM_BITSE_BIT BIT(6) 1058c2ecf20Sopenharmony_ci#define MSPI_CDRAM_PCS 0xf 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define MSPI_SPCR2_SPE BIT(6) 1088c2ecf20Sopenharmony_ci#define MSPI_SPCR2_CONT_AFTER_CMD BIT(7) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define MSPI_SPCR3_FASTBR BIT(0) 1118c2ecf20Sopenharmony_ci#define MSPI_SPCR3_FASTDT BIT(1) 1128c2ecf20Sopenharmony_ci#define MSPI_SPCR3_SYSCLKSEL_MASK GENMASK(11, 10) 1138c2ecf20Sopenharmony_ci#define MSPI_SPCR3_SYSCLKSEL_27 (MSPI_SPCR3_SYSCLKSEL_MASK & \ 1148c2ecf20Sopenharmony_ci ~(BIT(10) | BIT(11))) 1158c2ecf20Sopenharmony_ci#define MSPI_SPCR3_SYSCLKSEL_108 (MSPI_SPCR3_SYSCLKSEL_MASK & \ 1168c2ecf20Sopenharmony_ci BIT(11)) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define MSPI_MSPI_STATUS_SPIF BIT(0) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define INTR_BASE_BIT_SHIFT 0x02 1218c2ecf20Sopenharmony_ci#define INTR_COUNT 0x07 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define NUM_CHIPSELECT 4 1248c2ecf20Sopenharmony_ci#define QSPI_SPBR_MAX 255U 1258c2ecf20Sopenharmony_ci#define MSPI_BASE_FREQ 27000000UL 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define OPCODE_DIOR 0xBB 1288c2ecf20Sopenharmony_ci#define OPCODE_QIOR 0xEB 1298c2ecf20Sopenharmony_ci#define OPCODE_DIOR_4B 0xBC 1308c2ecf20Sopenharmony_ci#define OPCODE_QIOR_4B 0xEC 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define MAX_CMD_SIZE 6 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define ADDR_4MB_MASK GENMASK(22, 0) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* stop at end of transfer, no other reason */ 1378c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_NONE 0 1388c2ecf20Sopenharmony_ci/* stop at end of spi_message */ 1398c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_EOM 1 1408c2ecf20Sopenharmony_ci/* stop at end of spi_transfer if delay */ 1418c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_DELAY 2 1428c2ecf20Sopenharmony_ci/* stop at end of spi_transfer if cs_change */ 1438c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_CS_CHANGE 4 1448c2ecf20Sopenharmony_ci/* stop if we run out of bytes */ 1458c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_NO_BYTES 8 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* events that make us stop filling TX slots */ 1488c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_TX (TRANS_STATUS_BREAK_EOM | \ 1498c2ecf20Sopenharmony_ci TRANS_STATUS_BREAK_DELAY | \ 1508c2ecf20Sopenharmony_ci TRANS_STATUS_BREAK_CS_CHANGE) 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* events that make us deassert CS */ 1538c2ecf20Sopenharmony_ci#define TRANS_STATUS_BREAK_DESELECT (TRANS_STATUS_BREAK_EOM | \ 1548c2ecf20Sopenharmony_ci TRANS_STATUS_BREAK_CS_CHANGE) 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistruct bcm_qspi_parms { 1578c2ecf20Sopenharmony_ci u32 speed_hz; 1588c2ecf20Sopenharmony_ci u8 mode; 1598c2ecf20Sopenharmony_ci u8 bits_per_word; 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistruct bcm_xfer_mode { 1638c2ecf20Sopenharmony_ci bool flex_mode; 1648c2ecf20Sopenharmony_ci unsigned int width; 1658c2ecf20Sopenharmony_ci unsigned int addrlen; 1668c2ecf20Sopenharmony_ci unsigned int hp; 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cienum base_type { 1708c2ecf20Sopenharmony_ci MSPI, 1718c2ecf20Sopenharmony_ci BSPI, 1728c2ecf20Sopenharmony_ci CHIP_SELECT, 1738c2ecf20Sopenharmony_ci BASEMAX, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cienum irq_source { 1778c2ecf20Sopenharmony_ci SINGLE_L2, 1788c2ecf20Sopenharmony_ci MUXED_L1, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistruct bcm_qspi_irq { 1828c2ecf20Sopenharmony_ci const char *irq_name; 1838c2ecf20Sopenharmony_ci const irq_handler_t irq_handler; 1848c2ecf20Sopenharmony_ci int irq_source; 1858c2ecf20Sopenharmony_ci u32 mask; 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistruct bcm_qspi_dev_id { 1898c2ecf20Sopenharmony_ci const struct bcm_qspi_irq *irqp; 1908c2ecf20Sopenharmony_ci void *dev; 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistruct qspi_trans { 1958c2ecf20Sopenharmony_ci struct spi_transfer *trans; 1968c2ecf20Sopenharmony_ci int byte; 1978c2ecf20Sopenharmony_ci bool mspi_last_trans; 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistruct bcm_qspi { 2018c2ecf20Sopenharmony_ci struct platform_device *pdev; 2028c2ecf20Sopenharmony_ci struct spi_master *master; 2038c2ecf20Sopenharmony_ci struct clk *clk; 2048c2ecf20Sopenharmony_ci u32 base_clk; 2058c2ecf20Sopenharmony_ci u32 max_speed_hz; 2068c2ecf20Sopenharmony_ci void __iomem *base[BASEMAX]; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Some SoCs provide custom interrupt status register(s) */ 2098c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci struct bcm_qspi_parms last_parms; 2128c2ecf20Sopenharmony_ci struct qspi_trans trans_pos; 2138c2ecf20Sopenharmony_ci int curr_cs; 2148c2ecf20Sopenharmony_ci int bspi_maj_rev; 2158c2ecf20Sopenharmony_ci int bspi_min_rev; 2168c2ecf20Sopenharmony_ci int bspi_enabled; 2178c2ecf20Sopenharmony_ci const struct spi_mem_op *bspi_rf_op; 2188c2ecf20Sopenharmony_ci u32 bspi_rf_op_idx; 2198c2ecf20Sopenharmony_ci u32 bspi_rf_op_len; 2208c2ecf20Sopenharmony_ci u32 bspi_rf_op_status; 2218c2ecf20Sopenharmony_ci struct bcm_xfer_mode xfer_mode; 2228c2ecf20Sopenharmony_ci u32 s3_strap_override_ctrl; 2238c2ecf20Sopenharmony_ci bool bspi_mode; 2248c2ecf20Sopenharmony_ci bool big_endian; 2258c2ecf20Sopenharmony_ci int num_irqs; 2268c2ecf20Sopenharmony_ci struct bcm_qspi_dev_id *dev_ids; 2278c2ecf20Sopenharmony_ci struct completion mspi_done; 2288c2ecf20Sopenharmony_ci struct completion bspi_done; 2298c2ecf20Sopenharmony_ci u8 mspi_maj_rev; 2308c2ecf20Sopenharmony_ci u8 mspi_min_rev; 2318c2ecf20Sopenharmony_ci bool mspi_spcr3_sysclk; 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic inline bool has_bspi(struct bcm_qspi *qspi) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return qspi->bspi_mode; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* hardware supports spcr3 and fast baud-rate */ 2408c2ecf20Sopenharmony_cistatic inline bool bcm_qspi_has_fastbr(struct bcm_qspi *qspi) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci if (!has_bspi(qspi) && 2438c2ecf20Sopenharmony_ci ((qspi->mspi_maj_rev >= 1) && 2448c2ecf20Sopenharmony_ci (qspi->mspi_min_rev >= 5))) 2458c2ecf20Sopenharmony_ci return true; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return false; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* hardware supports sys clk 108Mhz */ 2518c2ecf20Sopenharmony_cistatic inline bool bcm_qspi_has_sysclk_108(struct bcm_qspi *qspi) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci if (!has_bspi(qspi) && (qspi->mspi_spcr3_sysclk || 2548c2ecf20Sopenharmony_ci ((qspi->mspi_maj_rev >= 1) && 2558c2ecf20Sopenharmony_ci (qspi->mspi_min_rev >= 6)))) 2568c2ecf20Sopenharmony_ci return true; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return false; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci if (bcm_qspi_has_fastbr(qspi)) 2648c2ecf20Sopenharmony_ci return 1; 2658c2ecf20Sopenharmony_ci else 2668c2ecf20Sopenharmony_ci return 8; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* Read qspi controller register*/ 2708c2ecf20Sopenharmony_cistatic inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type, 2718c2ecf20Sopenharmony_ci unsigned int offset) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci return bcm_qspi_readl(qspi->big_endian, qspi->base[type] + offset); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* Write qspi controller register*/ 2778c2ecf20Sopenharmony_cistatic inline void bcm_qspi_write(struct bcm_qspi *qspi, enum base_type type, 2788c2ecf20Sopenharmony_ci unsigned int offset, unsigned int data) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci bcm_qspi_writel(qspi->big_endian, data, qspi->base[type] + offset); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* BSPI helpers */ 2848c2ecf20Sopenharmony_cistatic int bcm_qspi_bspi_busy_poll(struct bcm_qspi *qspi) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int i; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* this should normally finish within 10us */ 2898c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 2908c2ecf20Sopenharmony_ci if (!(bcm_qspi_read(qspi, BSPI, BSPI_BUSY_STATUS) & 1)) 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci udelay(1); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci dev_warn(&qspi->pdev->dev, "timeout waiting for !busy_status\n"); 2958c2ecf20Sopenharmony_ci return -EIO; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic inline bool bcm_qspi_bspi_ver_three(struct bcm_qspi *qspi) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci if (qspi->bspi_maj_rev < 4) 3018c2ecf20Sopenharmony_ci return true; 3028c2ecf20Sopenharmony_ci return false; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void bcm_qspi_bspi_flush_prefetch_buffers(struct bcm_qspi *qspi) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci bcm_qspi_bspi_busy_poll(qspi); 3088c2ecf20Sopenharmony_ci /* Force rising edge for the b0/b1 'flush' field */ 3098c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 1); 3108c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 1); 3118c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 0); 3128c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 0); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int bcm_qspi_bspi_lr_is_fifo_empty(struct bcm_qspi *qspi) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci return (bcm_qspi_read(qspi, BSPI, BSPI_RAF_STATUS) & 3188c2ecf20Sopenharmony_ci BSPI_RAF_STATUS_FIFO_EMPTY_MASK); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic inline u32 bcm_qspi_bspi_lr_read_fifo(struct bcm_qspi *qspi) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci u32 data = bcm_qspi_read(qspi, BSPI, BSPI_RAF_READ_DATA); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* BSPI v3 LR is LE only, convert data to host endianness */ 3268c2ecf20Sopenharmony_ci if (bcm_qspi_bspi_ver_three(qspi)) 3278c2ecf20Sopenharmony_ci data = le32_to_cpu(data); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return data; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic inline void bcm_qspi_bspi_lr_start(struct bcm_qspi *qspi) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci bcm_qspi_bspi_busy_poll(qspi); 3358c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_RAF_CTRL, 3368c2ecf20Sopenharmony_ci BSPI_RAF_CTRL_START_MASK); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic inline void bcm_qspi_bspi_lr_clear(struct bcm_qspi *qspi) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_RAF_CTRL, 3428c2ecf20Sopenharmony_ci BSPI_RAF_CTRL_CLEAR_MASK); 3438c2ecf20Sopenharmony_ci bcm_qspi_bspi_flush_prefetch_buffers(qspi); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void bcm_qspi_bspi_lr_data_read(struct bcm_qspi *qspi) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci u32 *buf = (u32 *)qspi->bspi_rf_op->data.buf.in; 3498c2ecf20Sopenharmony_ci u32 data = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_op, 3528c2ecf20Sopenharmony_ci qspi->bspi_rf_op->data.buf.in, qspi->bspi_rf_op_len); 3538c2ecf20Sopenharmony_ci while (!bcm_qspi_bspi_lr_is_fifo_empty(qspi)) { 3548c2ecf20Sopenharmony_ci data = bcm_qspi_bspi_lr_read_fifo(qspi); 3558c2ecf20Sopenharmony_ci if (likely(qspi->bspi_rf_op_len >= 4) && 3568c2ecf20Sopenharmony_ci IS_ALIGNED((uintptr_t)buf, 4)) { 3578c2ecf20Sopenharmony_ci buf[qspi->bspi_rf_op_idx++] = data; 3588c2ecf20Sopenharmony_ci qspi->bspi_rf_op_len -= 4; 3598c2ecf20Sopenharmony_ci } else { 3608c2ecf20Sopenharmony_ci /* Read out remaining bytes, make sure*/ 3618c2ecf20Sopenharmony_ci u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_op_idx]; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci data = cpu_to_le32(data); 3648c2ecf20Sopenharmony_ci while (qspi->bspi_rf_op_len) { 3658c2ecf20Sopenharmony_ci *cbuf++ = (u8)data; 3668c2ecf20Sopenharmony_ci data >>= 8; 3678c2ecf20Sopenharmony_ci qspi->bspi_rf_op_len--; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte, 3748c2ecf20Sopenharmony_ci int bpp, int bpc, int flex_mode) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0); 3778c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_BITS_PER_CYCLE, bpc); 3788c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_BITS_PER_PHASE, bpp); 3798c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_CMD_AND_MODE_BYTE, cmd_byte); 3808c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, flex_mode); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, 3848c2ecf20Sopenharmony_ci const struct spi_mem_op *op, int hp) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int bpc = 0, bpp = 0; 3878c2ecf20Sopenharmony_ci u8 command = op->cmd.opcode; 3888c2ecf20Sopenharmony_ci int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE; 3898c2ecf20Sopenharmony_ci int addrlen = op->addr.nbytes; 3908c2ecf20Sopenharmony_ci int flex_mode = 1; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n", 3938c2ecf20Sopenharmony_ci width, addrlen, hp); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (addrlen == BSPI_ADDRLEN_4BYTES) 3968c2ecf20Sopenharmony_ci bpp = BSPI_BPP_ADDR_SELECT_MASK; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci switch (width) { 4018c2ecf20Sopenharmony_ci case SPI_NBITS_SINGLE: 4028c2ecf20Sopenharmony_ci if (addrlen == BSPI_ADDRLEN_3BYTES) 4038c2ecf20Sopenharmony_ci /* default mode, does not need flex_cmd */ 4048c2ecf20Sopenharmony_ci flex_mode = 0; 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci case SPI_NBITS_DUAL: 4078c2ecf20Sopenharmony_ci bpc = 0x00000001; 4088c2ecf20Sopenharmony_ci if (hp) { 4098c2ecf20Sopenharmony_ci bpc |= 0x00010100; /* address and mode are 2-bit */ 4108c2ecf20Sopenharmony_ci bpp = BSPI_BPP_MODE_SELECT_MASK; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case SPI_NBITS_QUAD: 4148c2ecf20Sopenharmony_ci bpc = 0x00000002; 4158c2ecf20Sopenharmony_ci if (hp) { 4168c2ecf20Sopenharmony_ci bpc |= 0x00020200; /* address and mode are 4-bit */ 4178c2ecf20Sopenharmony_ci bpp |= BSPI_BPP_MODE_SELECT_MASK; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci default: 4218c2ecf20Sopenharmony_ci return -EINVAL; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc, flex_mode); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, 4308c2ecf20Sopenharmony_ci const struct spi_mem_op *op, int hp) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE; 4338c2ecf20Sopenharmony_ci int addrlen = op->addr.nbytes; 4348c2ecf20Sopenharmony_ci u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n", 4378c2ecf20Sopenharmony_ci width, addrlen, hp); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci switch (width) { 4408c2ecf20Sopenharmony_ci case SPI_NBITS_SINGLE: 4418c2ecf20Sopenharmony_ci /* clear quad/dual mode */ 4428c2ecf20Sopenharmony_ci data &= ~(BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD | 4438c2ecf20Sopenharmony_ci BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL); 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci case SPI_NBITS_QUAD: 4468c2ecf20Sopenharmony_ci /* clear dual mode and set quad mode */ 4478c2ecf20Sopenharmony_ci data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL; 4488c2ecf20Sopenharmony_ci data |= BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case SPI_NBITS_DUAL: 4518c2ecf20Sopenharmony_ci /* clear quad mode set dual mode */ 4528c2ecf20Sopenharmony_ci data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD; 4538c2ecf20Sopenharmony_ci data |= BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci default: 4568c2ecf20Sopenharmony_ci return -EINVAL; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (addrlen == BSPI_ADDRLEN_4BYTES) 4608c2ecf20Sopenharmony_ci /* set 4byte mode*/ 4618c2ecf20Sopenharmony_ci data |= BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE; 4628c2ecf20Sopenharmony_ci else 4638c2ecf20Sopenharmony_ci /* clear 4 byte mode */ 4648c2ecf20Sopenharmony_ci data &= ~BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* set the override mode */ 4678c2ecf20Sopenharmony_ci data |= BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE; 4688c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data); 4698c2ecf20Sopenharmony_ci bcm_qspi_bspi_set_xfer_params(qspi, op->cmd.opcode, 0, 0, 0); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi, 4758c2ecf20Sopenharmony_ci const struct spi_mem_op *op, int hp) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci int error = 0; 4788c2ecf20Sopenharmony_ci int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE; 4798c2ecf20Sopenharmony_ci int addrlen = op->addr.nbytes; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* default mode */ 4828c2ecf20Sopenharmony_ci qspi->xfer_mode.flex_mode = true; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!bcm_qspi_bspi_ver_three(qspi)) { 4858c2ecf20Sopenharmony_ci u32 val, mask; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci val = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); 4888c2ecf20Sopenharmony_ci mask = BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE; 4898c2ecf20Sopenharmony_ci if (val & mask || qspi->s3_strap_override_ctrl & mask) { 4908c2ecf20Sopenharmony_ci qspi->xfer_mode.flex_mode = false; 4918c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0); 4928c2ecf20Sopenharmony_ci error = bcm_qspi_bspi_set_override(qspi, op, hp); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (qspi->xfer_mode.flex_mode) 4978c2ecf20Sopenharmony_ci error = bcm_qspi_bspi_set_flex_mode(qspi, op, hp); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (error) { 5008c2ecf20Sopenharmony_ci dev_warn(&qspi->pdev->dev, 5018c2ecf20Sopenharmony_ci "INVALID COMBINATION: width=%d addrlen=%d hp=%d\n", 5028c2ecf20Sopenharmony_ci width, addrlen, hp); 5038c2ecf20Sopenharmony_ci } else if (qspi->xfer_mode.width != width || 5048c2ecf20Sopenharmony_ci qspi->xfer_mode.addrlen != addrlen || 5058c2ecf20Sopenharmony_ci qspi->xfer_mode.hp != hp) { 5068c2ecf20Sopenharmony_ci qspi->xfer_mode.width = width; 5078c2ecf20Sopenharmony_ci qspi->xfer_mode.addrlen = addrlen; 5088c2ecf20Sopenharmony_ci qspi->xfer_mode.hp = hp; 5098c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, 5108c2ecf20Sopenharmony_ci "cs:%d %d-lane output, %d-byte address%s\n", 5118c2ecf20Sopenharmony_ci qspi->curr_cs, 5128c2ecf20Sopenharmony_ci qspi->xfer_mode.width, 5138c2ecf20Sopenharmony_ci qspi->xfer_mode.addrlen, 5148c2ecf20Sopenharmony_ci qspi->xfer_mode.hp != -1 ? ", hp mode" : ""); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return error; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void bcm_qspi_enable_bspi(struct bcm_qspi *qspi) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci if (!has_bspi(qspi)) 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci qspi->bspi_enabled = 1; 5268c2ecf20Sopenharmony_ci if ((bcm_qspi_read(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL) & 1) == 0) 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci bcm_qspi_bspi_flush_prefetch_buffers(qspi); 5308c2ecf20Sopenharmony_ci udelay(1); 5318c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL, 0); 5328c2ecf20Sopenharmony_ci udelay(1); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void bcm_qspi_disable_bspi(struct bcm_qspi *qspi) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci if (!has_bspi(qspi)) 5388c2ecf20Sopenharmony_ci return; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci qspi->bspi_enabled = 0; 5418c2ecf20Sopenharmony_ci if ((bcm_qspi_read(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL) & 1)) 5428c2ecf20Sopenharmony_ci return; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci bcm_qspi_bspi_busy_poll(qspi); 5458c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL, 1); 5468c2ecf20Sopenharmony_ci udelay(1); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void bcm_qspi_chip_select(struct bcm_qspi *qspi, int cs) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci u32 rd = 0; 5528c2ecf20Sopenharmony_ci u32 wr = 0; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (cs >= 0 && qspi->base[CHIP_SELECT]) { 5558c2ecf20Sopenharmony_ci rd = bcm_qspi_read(qspi, CHIP_SELECT, 0); 5568c2ecf20Sopenharmony_ci wr = (rd & ~0xff) | (1 << cs); 5578c2ecf20Sopenharmony_ci if (rd == wr) 5588c2ecf20Sopenharmony_ci return; 5598c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, CHIP_SELECT, 0, wr); 5608c2ecf20Sopenharmony_ci usleep_range(10, 20); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "using cs:%d\n", cs); 5648c2ecf20Sopenharmony_ci qspi->curr_cs = cs; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci/* MSPI helpers */ 5688c2ecf20Sopenharmony_cistatic void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, 5698c2ecf20Sopenharmony_ci const struct bcm_qspi_parms *xp) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci u32 spcr, spbr = 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (xp->speed_hz) 5748c2ecf20Sopenharmony_ci spbr = qspi->base_clk / (2 * xp->speed_hz); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX); 5778c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!qspi->mspi_maj_rev) 5808c2ecf20Sopenharmony_ci /* legacy controller */ 5818c2ecf20Sopenharmony_ci spcr = MSPI_MASTER_BIT; 5828c2ecf20Sopenharmony_ci else 5838c2ecf20Sopenharmony_ci spcr = 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* for 16 bit the data should be zero */ 5868c2ecf20Sopenharmony_ci if (xp->bits_per_word != 16) 5878c2ecf20Sopenharmony_ci spcr |= xp->bits_per_word << 2; 5888c2ecf20Sopenharmony_ci spcr |= xp->mode & 3; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (bcm_qspi_has_fastbr(qspi)) { 5938c2ecf20Sopenharmony_ci spcr = 0; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* enable fastbr */ 5968c2ecf20Sopenharmony_ci spcr |= MSPI_SPCR3_FASTBR; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (bcm_qspi_has_sysclk_108(qspi)) { 5998c2ecf20Sopenharmony_ci /* SYSCLK_108 */ 6008c2ecf20Sopenharmony_ci spcr |= MSPI_SPCR3_SYSCLKSEL_108; 6018c2ecf20Sopenharmony_ci qspi->base_clk = MSPI_BASE_FREQ * 4; 6028c2ecf20Sopenharmony_ci /* Change spbr as we changed sysclk */ 6038c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, 4); 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci qspi->last_parms = *xp; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic void bcm_qspi_update_parms(struct bcm_qspi *qspi, 6138c2ecf20Sopenharmony_ci struct spi_device *spi, 6148c2ecf20Sopenharmony_ci struct spi_transfer *trans) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct bcm_qspi_parms xp; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci xp.speed_hz = trans->speed_hz; 6198c2ecf20Sopenharmony_ci xp.bits_per_word = trans->bits_per_word; 6208c2ecf20Sopenharmony_ci xp.mode = spi->mode; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci bcm_qspi_hw_set_parms(qspi, &xp); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int bcm_qspi_setup(struct spi_device *spi) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct bcm_qspi_parms *xp; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (spi->bits_per_word > 16) 6308c2ecf20Sopenharmony_ci return -EINVAL; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci xp = spi_get_ctldata(spi); 6338c2ecf20Sopenharmony_ci if (!xp) { 6348c2ecf20Sopenharmony_ci xp = kzalloc(sizeof(*xp), GFP_KERNEL); 6358c2ecf20Sopenharmony_ci if (!xp) 6368c2ecf20Sopenharmony_ci return -ENOMEM; 6378c2ecf20Sopenharmony_ci spi_set_ctldata(spi, xp); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci xp->speed_hz = spi->max_speed_hz; 6408c2ecf20Sopenharmony_ci xp->mode = spi->mode; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (spi->bits_per_word) 6438c2ecf20Sopenharmony_ci xp->bits_per_word = spi->bits_per_word; 6448c2ecf20Sopenharmony_ci else 6458c2ecf20Sopenharmony_ci xp->bits_per_word = 8; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic bool bcm_qspi_mspi_transfer_is_last(struct bcm_qspi *qspi, 6518c2ecf20Sopenharmony_ci struct qspi_trans *qt) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci if (qt->mspi_last_trans && 6548c2ecf20Sopenharmony_ci spi_transfer_is_last(qspi->master, qt->trans)) 6558c2ecf20Sopenharmony_ci return true; 6568c2ecf20Sopenharmony_ci else 6578c2ecf20Sopenharmony_ci return false; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int update_qspi_trans_byte_count(struct bcm_qspi *qspi, 6618c2ecf20Sopenharmony_ci struct qspi_trans *qt, int flags) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci int ret = TRANS_STATUS_BREAK_NONE; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* count the last transferred bytes */ 6668c2ecf20Sopenharmony_ci if (qt->trans->bits_per_word <= 8) 6678c2ecf20Sopenharmony_ci qt->byte++; 6688c2ecf20Sopenharmony_ci else 6698c2ecf20Sopenharmony_ci qt->byte += 2; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (qt->byte >= qt->trans->len) { 6728c2ecf20Sopenharmony_ci /* we're at the end of the spi_transfer */ 6738c2ecf20Sopenharmony_ci /* in TX mode, need to pause for a delay or CS change */ 6748c2ecf20Sopenharmony_ci if (qt->trans->delay_usecs && 6758c2ecf20Sopenharmony_ci (flags & TRANS_STATUS_BREAK_DELAY)) 6768c2ecf20Sopenharmony_ci ret |= TRANS_STATUS_BREAK_DELAY; 6778c2ecf20Sopenharmony_ci if (qt->trans->cs_change && 6788c2ecf20Sopenharmony_ci (flags & TRANS_STATUS_BREAK_CS_CHANGE)) 6798c2ecf20Sopenharmony_ci ret |= TRANS_STATUS_BREAK_CS_CHANGE; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (bcm_qspi_mspi_transfer_is_last(qspi, qt)) 6828c2ecf20Sopenharmony_ci ret |= TRANS_STATUS_BREAK_EOM; 6838c2ecf20Sopenharmony_ci else 6848c2ecf20Sopenharmony_ci ret |= TRANS_STATUS_BREAK_NO_BYTES; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci qt->trans = NULL; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "trans %p len %d byte %d ret %x\n", 6908c2ecf20Sopenharmony_ci qt->trans, qt->trans ? qt->trans->len : 0, qt->byte, ret); 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic inline u8 read_rxram_slot_u8(struct bcm_qspi *qspi, int slot) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci u32 slot_offset = MSPI_RXRAM + (slot << 3) + 0x4; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* mask out reserved bits */ 6998c2ecf20Sopenharmony_ci return bcm_qspi_read(qspi, MSPI, slot_offset) & 0xff; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic inline u16 read_rxram_slot_u16(struct bcm_qspi *qspi, int slot) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci u32 reg_offset = MSPI_RXRAM; 7058c2ecf20Sopenharmony_ci u32 lsb_offset = reg_offset + (slot << 3) + 0x4; 7068c2ecf20Sopenharmony_ci u32 msb_offset = reg_offset + (slot << 3); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return (bcm_qspi_read(qspi, MSPI, lsb_offset) & 0xff) | 7098c2ecf20Sopenharmony_ci ((bcm_qspi_read(qspi, MSPI, msb_offset) & 0xff) << 8); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic void read_from_hw(struct bcm_qspi *qspi, int slots) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct qspi_trans tp; 7158c2ecf20Sopenharmony_ci int slot; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci bcm_qspi_disable_bspi(qspi); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (slots > MSPI_NUM_CDRAM) { 7208c2ecf20Sopenharmony_ci /* should never happen */ 7218c2ecf20Sopenharmony_ci dev_err(&qspi->pdev->dev, "%s: too many slots!\n", __func__); 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci tp = qspi->trans_pos; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci for (slot = 0; slot < slots; slot++) { 7288c2ecf20Sopenharmony_ci if (tp.trans->bits_per_word <= 8) { 7298c2ecf20Sopenharmony_ci u8 *buf = tp.trans->rx_buf; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (buf) 7328c2ecf20Sopenharmony_ci buf[tp.byte] = read_rxram_slot_u8(qspi, slot); 7338c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "RD %02x\n", 7348c2ecf20Sopenharmony_ci buf ? buf[tp.byte] : 0x0); 7358c2ecf20Sopenharmony_ci } else { 7368c2ecf20Sopenharmony_ci u16 *buf = tp.trans->rx_buf; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (buf) 7398c2ecf20Sopenharmony_ci buf[tp.byte / 2] = read_rxram_slot_u16(qspi, 7408c2ecf20Sopenharmony_ci slot); 7418c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "RD %04x\n", 7428c2ecf20Sopenharmony_ci buf ? buf[tp.byte / 2] : 0x0); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci update_qspi_trans_byte_count(qspi, &tp, 7468c2ecf20Sopenharmony_ci TRANS_STATUS_BREAK_NONE); 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci qspi->trans_pos = tp; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic inline void write_txram_slot_u8(struct bcm_qspi *qspi, int slot, 7538c2ecf20Sopenharmony_ci u8 val) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci u32 reg_offset = MSPI_TXRAM + (slot << 3); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* mask out reserved bits */ 7588c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, reg_offset, val); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic inline void write_txram_slot_u16(struct bcm_qspi *qspi, int slot, 7628c2ecf20Sopenharmony_ci u16 val) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci u32 reg_offset = MSPI_TXRAM; 7658c2ecf20Sopenharmony_ci u32 msb_offset = reg_offset + (slot << 3); 7668c2ecf20Sopenharmony_ci u32 lsb_offset = reg_offset + (slot << 3) + 0x4; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, msb_offset, (val >> 8)); 7698c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, lsb_offset, (val & 0xff)); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic inline u32 read_cdram_slot(struct bcm_qspi *qspi, int slot) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci return bcm_qspi_read(qspi, MSPI, MSPI_CDRAM + (slot << 2)); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic inline void write_cdram_slot(struct bcm_qspi *qspi, int slot, u32 val) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, (MSPI_CDRAM + (slot << 2)), val); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/* Return number of slots written */ 7838c2ecf20Sopenharmony_cistatic int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct qspi_trans tp; 7868c2ecf20Sopenharmony_ci int slot = 0, tstatus = 0; 7878c2ecf20Sopenharmony_ci u32 mspi_cdram = 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci bcm_qspi_disable_bspi(qspi); 7908c2ecf20Sopenharmony_ci tp = qspi->trans_pos; 7918c2ecf20Sopenharmony_ci bcm_qspi_update_parms(qspi, spi, tp.trans); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Run until end of transfer or reached the max data */ 7948c2ecf20Sopenharmony_ci while (!tstatus && slot < MSPI_NUM_CDRAM) { 7958c2ecf20Sopenharmony_ci if (tp.trans->bits_per_word <= 8) { 7968c2ecf20Sopenharmony_ci const u8 *buf = tp.trans->tx_buf; 7978c2ecf20Sopenharmony_ci u8 val = buf ? buf[tp.byte] : 0x00; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci write_txram_slot_u8(qspi, slot, val); 8008c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "WR %02x\n", val); 8018c2ecf20Sopenharmony_ci } else { 8028c2ecf20Sopenharmony_ci const u16 *buf = tp.trans->tx_buf; 8038c2ecf20Sopenharmony_ci u16 val = buf ? buf[tp.byte / 2] : 0x0000; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci write_txram_slot_u16(qspi, slot, val); 8068c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "WR %04x\n", val); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci mspi_cdram = MSPI_CDRAM_CONT_BIT; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (has_bspi(qspi)) 8118c2ecf20Sopenharmony_ci mspi_cdram &= ~1; 8128c2ecf20Sopenharmony_ci else 8138c2ecf20Sopenharmony_ci mspi_cdram |= (~(1 << spi->chip_select) & 8148c2ecf20Sopenharmony_ci MSPI_CDRAM_PCS); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 : 8178c2ecf20Sopenharmony_ci MSPI_CDRAM_BITSE_BIT); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci write_cdram_slot(qspi, slot, mspi_cdram); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci tstatus = update_qspi_trans_byte_count(qspi, &tp, 8228c2ecf20Sopenharmony_ci TRANS_STATUS_BREAK_TX); 8238c2ecf20Sopenharmony_ci slot++; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (!slot) { 8278c2ecf20Sopenharmony_ci dev_err(&qspi->pdev->dev, "%s: no data to send?", __func__); 8288c2ecf20Sopenharmony_ci goto done; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, "submitting %d slots\n", slot); 8328c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0); 8338c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, slot - 1); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* 8368c2ecf20Sopenharmony_ci * case 1) EOM =1, cs_change =0: SSb inactive 8378c2ecf20Sopenharmony_ci * case 2) EOM =1, cs_change =1: SSb stay active 8388c2ecf20Sopenharmony_ci * case 3) EOM =0, cs_change =0: SSb stay active 8398c2ecf20Sopenharmony_ci * case 4) EOM =0, cs_change =1: SSb inactive 8408c2ecf20Sopenharmony_ci */ 8418c2ecf20Sopenharmony_ci if (((tstatus & TRANS_STATUS_BREAK_DESELECT) 8428c2ecf20Sopenharmony_ci == TRANS_STATUS_BREAK_CS_CHANGE) || 8438c2ecf20Sopenharmony_ci ((tstatus & TRANS_STATUS_BREAK_DESELECT) 8448c2ecf20Sopenharmony_ci == TRANS_STATUS_BREAK_EOM)) { 8458c2ecf20Sopenharmony_ci mspi_cdram = read_cdram_slot(qspi, slot - 1) & 8468c2ecf20Sopenharmony_ci ~MSPI_CDRAM_CONT_BIT; 8478c2ecf20Sopenharmony_ci write_cdram_slot(qspi, slot - 1, mspi_cdram); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (has_bspi(qspi)) 8518c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 1); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* Must flush previous writes before starting MSPI operation */ 8548c2ecf20Sopenharmony_ci mb(); 8558c2ecf20Sopenharmony_ci /* Set cont | spe | spifie */ 8568c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0xe0); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cidone: 8598c2ecf20Sopenharmony_ci return slot; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int bcm_qspi_bspi_exec_mem_op(struct spi_device *spi, 8638c2ecf20Sopenharmony_ci const struct spi_mem_op *op) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = spi_master_get_devdata(spi->master); 8668c2ecf20Sopenharmony_ci u32 addr = 0, len, rdlen, len_words, from = 0; 8678c2ecf20Sopenharmony_ci int ret = 0; 8688c2ecf20Sopenharmony_ci unsigned long timeo = msecs_to_jiffies(100); 8698c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (bcm_qspi_bspi_ver_three(qspi)) 8728c2ecf20Sopenharmony_ci if (op->addr.nbytes == BSPI_ADDRLEN_4BYTES) 8738c2ecf20Sopenharmony_ci return -EIO; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci from = op->addr.val; 8768c2ecf20Sopenharmony_ci if (!spi->cs_gpiod) 8778c2ecf20Sopenharmony_ci bcm_qspi_chip_select(qspi, spi->chip_select); 8788c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* 8818c2ecf20Sopenharmony_ci * when using flex mode we need to send 8828c2ecf20Sopenharmony_ci * the upper address byte to bspi 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci if (bcm_qspi_bspi_ver_three(qspi) == false) { 8858c2ecf20Sopenharmony_ci addr = from & 0xff000000; 8868c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, 8878c2ecf20Sopenharmony_ci BSPI_BSPI_FLASH_UPPER_ADDR_BYTE, addr); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (!qspi->xfer_mode.flex_mode) 8918c2ecf20Sopenharmony_ci addr = from; 8928c2ecf20Sopenharmony_ci else 8938c2ecf20Sopenharmony_ci addr = from & 0x00ffffff; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (bcm_qspi_bspi_ver_three(qspi) == true) 8968c2ecf20Sopenharmony_ci addr = (addr + 0xc00000) & 0xffffff; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* 8998c2ecf20Sopenharmony_ci * read into the entire buffer by breaking the reads 9008c2ecf20Sopenharmony_ci * into RAF buffer read lengths 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_ci len = op->data.nbytes; 9038c2ecf20Sopenharmony_ci qspi->bspi_rf_op_idx = 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci do { 9068c2ecf20Sopenharmony_ci if (len > BSPI_READ_LENGTH) 9078c2ecf20Sopenharmony_ci rdlen = BSPI_READ_LENGTH; 9088c2ecf20Sopenharmony_ci else 9098c2ecf20Sopenharmony_ci rdlen = len; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci reinit_completion(&qspi->bspi_done); 9128c2ecf20Sopenharmony_ci bcm_qspi_enable_bspi(qspi); 9138c2ecf20Sopenharmony_ci len_words = (rdlen + 3) >> 2; 9148c2ecf20Sopenharmony_ci qspi->bspi_rf_op = op; 9158c2ecf20Sopenharmony_ci qspi->bspi_rf_op_status = 0; 9168c2ecf20Sopenharmony_ci qspi->bspi_rf_op_len = rdlen; 9178c2ecf20Sopenharmony_ci dev_dbg(&qspi->pdev->dev, 9188c2ecf20Sopenharmony_ci "bspi xfr addr 0x%x len 0x%x", addr, rdlen); 9198c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr); 9208c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_RAF_NUM_WORDS, len_words); 9218c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_RAF_WATERMARK, 0); 9228c2ecf20Sopenharmony_ci if (qspi->soc_intc) { 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * clear soc MSPI and BSPI interrupts and enable 9258c2ecf20Sopenharmony_ci * BSPI interrupts. 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_BSPI_DONE); 9288c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, true); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Must flush previous writes before starting BSPI operation */ 9328c2ecf20Sopenharmony_ci mb(); 9338c2ecf20Sopenharmony_ci bcm_qspi_bspi_lr_start(qspi); 9348c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&qspi->bspi_done, timeo)) { 9358c2ecf20Sopenharmony_ci dev_err(&qspi->pdev->dev, "timeout waiting for BSPI\n"); 9368c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 9378c2ecf20Sopenharmony_ci break; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* set msg return length */ 9418c2ecf20Sopenharmony_ci addr += rdlen; 9428c2ecf20Sopenharmony_ci len -= rdlen; 9438c2ecf20Sopenharmony_ci } while (len); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci return ret; 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic int bcm_qspi_transfer_one(struct spi_master *master, 9498c2ecf20Sopenharmony_ci struct spi_device *spi, 9508c2ecf20Sopenharmony_ci struct spi_transfer *trans) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = spi_master_get_devdata(master); 9538c2ecf20Sopenharmony_ci int slots; 9548c2ecf20Sopenharmony_ci unsigned long timeo = msecs_to_jiffies(100); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (!spi->cs_gpiod) 9578c2ecf20Sopenharmony_ci bcm_qspi_chip_select(qspi, spi->chip_select); 9588c2ecf20Sopenharmony_ci qspi->trans_pos.trans = trans; 9598c2ecf20Sopenharmony_ci qspi->trans_pos.byte = 0; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci while (qspi->trans_pos.byte < trans->len) { 9628c2ecf20Sopenharmony_ci reinit_completion(&qspi->mspi_done); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci slots = write_to_hw(qspi, spi); 9658c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&qspi->mspi_done, timeo)) { 9668c2ecf20Sopenharmony_ci dev_err(&qspi->pdev->dev, "timeout waiting for MSPI\n"); 9678c2ecf20Sopenharmony_ci return -ETIMEDOUT; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci read_from_hw(qspi, slots); 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci bcm_qspi_enable_bspi(qspi); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci return 0; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic int bcm_qspi_mspi_exec_mem_op(struct spi_device *spi, 9788c2ecf20Sopenharmony_ci const struct spi_mem_op *op) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct spi_master *master = spi->master; 9818c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = spi_master_get_devdata(master); 9828c2ecf20Sopenharmony_ci struct spi_transfer t[2]; 9838c2ecf20Sopenharmony_ci u8 cmd[6] = { }; 9848c2ecf20Sopenharmony_ci int ret, i; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci memset(cmd, 0, sizeof(cmd)); 9878c2ecf20Sopenharmony_ci memset(t, 0, sizeof(t)); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* tx */ 9908c2ecf20Sopenharmony_ci /* opcode is in cmd[0] */ 9918c2ecf20Sopenharmony_ci cmd[0] = op->cmd.opcode; 9928c2ecf20Sopenharmony_ci for (i = 0; i < op->addr.nbytes; i++) 9938c2ecf20Sopenharmony_ci cmd[1 + i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1)); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci t[0].tx_buf = cmd; 9968c2ecf20Sopenharmony_ci t[0].len = op->addr.nbytes + op->dummy.nbytes + 1; 9978c2ecf20Sopenharmony_ci t[0].bits_per_word = spi->bits_per_word; 9988c2ecf20Sopenharmony_ci t[0].tx_nbits = op->cmd.buswidth; 9998c2ecf20Sopenharmony_ci /* lets mspi know that this is not last transfer */ 10008c2ecf20Sopenharmony_ci qspi->trans_pos.mspi_last_trans = false; 10018c2ecf20Sopenharmony_ci ret = bcm_qspi_transfer_one(master, spi, &t[0]); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* rx */ 10048c2ecf20Sopenharmony_ci qspi->trans_pos.mspi_last_trans = true; 10058c2ecf20Sopenharmony_ci if (!ret) { 10068c2ecf20Sopenharmony_ci /* rx */ 10078c2ecf20Sopenharmony_ci t[1].rx_buf = op->data.buf.in; 10088c2ecf20Sopenharmony_ci t[1].len = op->data.nbytes; 10098c2ecf20Sopenharmony_ci t[1].rx_nbits = op->data.buswidth; 10108c2ecf20Sopenharmony_ci t[1].bits_per_word = spi->bits_per_word; 10118c2ecf20Sopenharmony_ci ret = bcm_qspi_transfer_one(master, spi, &t[1]); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci return ret; 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic int bcm_qspi_exec_mem_op(struct spi_mem *mem, 10188c2ecf20Sopenharmony_ci const struct spi_mem_op *op) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct spi_device *spi = mem->spi; 10218c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = spi_master_get_devdata(spi->master); 10228c2ecf20Sopenharmony_ci int ret = 0; 10238c2ecf20Sopenharmony_ci bool mspi_read = false; 10248c2ecf20Sopenharmony_ci u32 addr = 0, len; 10258c2ecf20Sopenharmony_ci u_char *buf; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 || 10288c2ecf20Sopenharmony_ci op->data.dir != SPI_MEM_DATA_IN) 10298c2ecf20Sopenharmony_ci return -ENOTSUPP; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci buf = op->data.buf.in; 10328c2ecf20Sopenharmony_ci addr = op->addr.val; 10338c2ecf20Sopenharmony_ci len = op->data.nbytes; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (has_bspi(qspi) && bcm_qspi_bspi_ver_three(qspi) == true) { 10368c2ecf20Sopenharmony_ci /* 10378c2ecf20Sopenharmony_ci * The address coming into this function is a raw flash offset. 10388c2ecf20Sopenharmony_ci * But for BSPI <= V3, we need to convert it to a remapped BSPI 10398c2ecf20Sopenharmony_ci * address. If it crosses a 4MB boundary, just revert back to 10408c2ecf20Sopenharmony_ci * using MSPI. 10418c2ecf20Sopenharmony_ci */ 10428c2ecf20Sopenharmony_ci addr = (addr + 0xc00000) & 0xffffff; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if ((~ADDR_4MB_MASK & addr) ^ 10458c2ecf20Sopenharmony_ci (~ADDR_4MB_MASK & (addr + len - 1))) 10468c2ecf20Sopenharmony_ci mspi_read = true; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* non-aligned and very short transfers are handled by MSPI */ 10508c2ecf20Sopenharmony_ci if (!IS_ALIGNED((uintptr_t)addr, 4) || !IS_ALIGNED((uintptr_t)buf, 4) || 10518c2ecf20Sopenharmony_ci len < 4 || op->cmd.opcode == SPINOR_OP_RDSFDP) 10528c2ecf20Sopenharmony_ci mspi_read = true; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (!has_bspi(qspi) || mspi_read) 10558c2ecf20Sopenharmony_ci return bcm_qspi_mspi_exec_mem_op(spi, op); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ret = bcm_qspi_bspi_set_mode(qspi, op, 0); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (!ret) 10608c2ecf20Sopenharmony_ci ret = bcm_qspi_bspi_exec_mem_op(spi, op); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return ret; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic void bcm_qspi_cleanup(struct spi_device *spi) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct bcm_qspi_parms *xp = spi_get_ctldata(spi); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci kfree(xp); 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic irqreturn_t bcm_qspi_mspi_l2_isr(int irq, void *dev_id) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct bcm_qspi_dev_id *qspi_dev_id = dev_id; 10758c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = qspi_dev_id->dev; 10768c2ecf20Sopenharmony_ci u32 status = bcm_qspi_read(qspi, MSPI, MSPI_MSPI_STATUS); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (status & MSPI_MSPI_STATUS_SPIF) { 10798c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; 10808c2ecf20Sopenharmony_ci /* clear interrupt */ 10818c2ecf20Sopenharmony_ci status &= ~MSPI_MSPI_STATUS_SPIF; 10828c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_MSPI_STATUS, status); 10838c2ecf20Sopenharmony_ci if (qspi->soc_intc) 10848c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_DONE); 10858c2ecf20Sopenharmony_ci complete(&qspi->mspi_done); 10868c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return IRQ_NONE; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci struct bcm_qspi_dev_id *qspi_dev_id = dev_id; 10958c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = qspi_dev_id->dev; 10968c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; 10978c2ecf20Sopenharmony_ci u32 status = qspi_dev_id->irqp->mask; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (qspi->bspi_enabled && qspi->bspi_rf_op) { 11008c2ecf20Sopenharmony_ci bcm_qspi_bspi_lr_data_read(qspi); 11018c2ecf20Sopenharmony_ci if (qspi->bspi_rf_op_len == 0) { 11028c2ecf20Sopenharmony_ci qspi->bspi_rf_op = NULL; 11038c2ecf20Sopenharmony_ci if (qspi->soc_intc) { 11048c2ecf20Sopenharmony_ci /* disable soc BSPI interrupt */ 11058c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, 11068c2ecf20Sopenharmony_ci false); 11078c2ecf20Sopenharmony_ci /* indicate done */ 11088c2ecf20Sopenharmony_ci status = INTR_BSPI_LR_SESSION_DONE_MASK; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (qspi->bspi_rf_op_status) 11128c2ecf20Sopenharmony_ci bcm_qspi_bspi_lr_clear(qspi); 11138c2ecf20Sopenharmony_ci else 11148c2ecf20Sopenharmony_ci bcm_qspi_bspi_flush_prefetch_buffers(qspi); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (qspi->soc_intc) 11188c2ecf20Sopenharmony_ci /* clear soc BSPI interrupt */ 11198c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_DONE); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci status &= INTR_BSPI_LR_SESSION_DONE_MASK; 11238c2ecf20Sopenharmony_ci if (qspi->bspi_enabled && status && qspi->bspi_rf_op_len == 0) 11248c2ecf20Sopenharmony_ci complete(&qspi->bspi_done); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct bcm_qspi_dev_id *qspi_dev_id = dev_id; 11328c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = qspi_dev_id->dev; 11338c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci dev_err(&qspi->pdev->dev, "BSPI INT error\n"); 11368c2ecf20Sopenharmony_ci qspi->bspi_rf_op_status = -EIO; 11378c2ecf20Sopenharmony_ci if (qspi->soc_intc) 11388c2ecf20Sopenharmony_ci /* clear soc interrupt */ 11398c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_ERR); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci complete(&qspi->bspi_done); 11428c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic irqreturn_t bcm_qspi_l1_isr(int irq, void *dev_id) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci struct bcm_qspi_dev_id *qspi_dev_id = dev_id; 11488c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = qspi_dev_id->dev; 11498c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; 11508c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (soc_intc) { 11538c2ecf20Sopenharmony_ci u32 status = soc_intc->bcm_qspi_get_int_status(soc_intc); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (status & MSPI_DONE) 11568c2ecf20Sopenharmony_ci ret = bcm_qspi_mspi_l2_isr(irq, dev_id); 11578c2ecf20Sopenharmony_ci else if (status & BSPI_DONE) 11588c2ecf20Sopenharmony_ci ret = bcm_qspi_bspi_lr_l2_isr(irq, dev_id); 11598c2ecf20Sopenharmony_ci else if (status & BSPI_ERR) 11608c2ecf20Sopenharmony_ci ret = bcm_qspi_bspi_lr_err_l2_isr(irq, dev_id); 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return ret; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic const struct bcm_qspi_irq qspi_irq_tab[] = { 11678c2ecf20Sopenharmony_ci { 11688c2ecf20Sopenharmony_ci .irq_name = "spi_lr_fullness_reached", 11698c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_bspi_lr_l2_isr, 11708c2ecf20Sopenharmony_ci .mask = INTR_BSPI_LR_FULLNESS_REACHED_MASK, 11718c2ecf20Sopenharmony_ci }, 11728c2ecf20Sopenharmony_ci { 11738c2ecf20Sopenharmony_ci .irq_name = "spi_lr_session_aborted", 11748c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_bspi_lr_err_l2_isr, 11758c2ecf20Sopenharmony_ci .mask = INTR_BSPI_LR_SESSION_ABORTED_MASK, 11768c2ecf20Sopenharmony_ci }, 11778c2ecf20Sopenharmony_ci { 11788c2ecf20Sopenharmony_ci .irq_name = "spi_lr_impatient", 11798c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_bspi_lr_err_l2_isr, 11808c2ecf20Sopenharmony_ci .mask = INTR_BSPI_LR_IMPATIENT_MASK, 11818c2ecf20Sopenharmony_ci }, 11828c2ecf20Sopenharmony_ci { 11838c2ecf20Sopenharmony_ci .irq_name = "spi_lr_session_done", 11848c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_bspi_lr_l2_isr, 11858c2ecf20Sopenharmony_ci .mask = INTR_BSPI_LR_SESSION_DONE_MASK, 11868c2ecf20Sopenharmony_ci }, 11878c2ecf20Sopenharmony_ci#ifdef QSPI_INT_DEBUG 11888c2ecf20Sopenharmony_ci /* this interrupt is for debug purposes only, dont request irq */ 11898c2ecf20Sopenharmony_ci { 11908c2ecf20Sopenharmony_ci .irq_name = "spi_lr_overread", 11918c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_bspi_lr_err_l2_isr, 11928c2ecf20Sopenharmony_ci .mask = INTR_BSPI_LR_OVERREAD_MASK, 11938c2ecf20Sopenharmony_ci }, 11948c2ecf20Sopenharmony_ci#endif 11958c2ecf20Sopenharmony_ci { 11968c2ecf20Sopenharmony_ci .irq_name = "mspi_done", 11978c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_mspi_l2_isr, 11988c2ecf20Sopenharmony_ci .mask = INTR_MSPI_DONE_MASK, 11998c2ecf20Sopenharmony_ci }, 12008c2ecf20Sopenharmony_ci { 12018c2ecf20Sopenharmony_ci .irq_name = "mspi_halted", 12028c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_mspi_l2_isr, 12038c2ecf20Sopenharmony_ci .mask = INTR_MSPI_HALTED_MASK, 12048c2ecf20Sopenharmony_ci }, 12058c2ecf20Sopenharmony_ci { 12068c2ecf20Sopenharmony_ci /* single muxed L1 interrupt source */ 12078c2ecf20Sopenharmony_ci .irq_name = "spi_l1_intr", 12088c2ecf20Sopenharmony_ci .irq_handler = bcm_qspi_l1_isr, 12098c2ecf20Sopenharmony_ci .irq_source = MUXED_L1, 12108c2ecf20Sopenharmony_ci .mask = QSPI_INTERRUPTS_ALL, 12118c2ecf20Sopenharmony_ci }, 12128c2ecf20Sopenharmony_ci}; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic void bcm_qspi_bspi_init(struct bcm_qspi *qspi) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci u32 val = 0; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci val = bcm_qspi_read(qspi, BSPI, BSPI_REVISION_ID); 12198c2ecf20Sopenharmony_ci qspi->bspi_maj_rev = (val >> 8) & 0xff; 12208c2ecf20Sopenharmony_ci qspi->bspi_min_rev = val & 0xff; 12218c2ecf20Sopenharmony_ci if (!(bcm_qspi_bspi_ver_three(qspi))) { 12228c2ecf20Sopenharmony_ci /* Force mapping of BSPI address -> flash offset */ 12238c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_BSPI_XOR_VALUE, 0); 12248c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_BSPI_XOR_ENABLE, 1); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci qspi->bspi_enabled = 1; 12278c2ecf20Sopenharmony_ci bcm_qspi_disable_bspi(qspi); 12288c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 0); 12298c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 0); 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic void bcm_qspi_hw_init(struct bcm_qspi *qspi) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci struct bcm_qspi_parms parms; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_LSB, 0); 12378c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_MSB, 0); 12388c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0); 12398c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, 0); 12408c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0x20); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci parms.mode = SPI_MODE_3; 12438c2ecf20Sopenharmony_ci parms.bits_per_word = 8; 12448c2ecf20Sopenharmony_ci parms.speed_hz = qspi->max_speed_hz; 12458c2ecf20Sopenharmony_ci bcm_qspi_hw_set_parms(qspi, &parms); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (has_bspi(qspi)) 12488c2ecf20Sopenharmony_ci bcm_qspi_bspi_init(qspi); 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic void bcm_qspi_hw_uninit(struct bcm_qspi *qspi) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci u32 status = bcm_qspi_read(qspi, MSPI, MSPI_MSPI_STATUS); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0); 12568c2ecf20Sopenharmony_ci if (has_bspi(qspi)) 12578c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* clear interrupt */ 12608c2ecf20Sopenharmony_ci bcm_qspi_write(qspi, MSPI, MSPI_MSPI_STATUS, status & ~1); 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic const struct spi_controller_mem_ops bcm_qspi_mem_ops = { 12648c2ecf20Sopenharmony_ci .exec_op = bcm_qspi_exec_mem_op, 12658c2ecf20Sopenharmony_ci}; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistruct bcm_qspi_data { 12688c2ecf20Sopenharmony_ci bool has_mspi_rev; 12698c2ecf20Sopenharmony_ci bool has_spcr3_sysclk; 12708c2ecf20Sopenharmony_ci}; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic const struct bcm_qspi_data bcm_qspi_no_rev_data = { 12738c2ecf20Sopenharmony_ci .has_mspi_rev = false, 12748c2ecf20Sopenharmony_ci .has_spcr3_sysclk = false, 12758c2ecf20Sopenharmony_ci}; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic const struct bcm_qspi_data bcm_qspi_rev_data = { 12788c2ecf20Sopenharmony_ci .has_mspi_rev = true, 12798c2ecf20Sopenharmony_ci .has_spcr3_sysclk = false, 12808c2ecf20Sopenharmony_ci}; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic const struct bcm_qspi_data bcm_qspi_spcr3_data = { 12838c2ecf20Sopenharmony_ci .has_mspi_rev = true, 12848c2ecf20Sopenharmony_ci .has_spcr3_sysclk = true, 12858c2ecf20Sopenharmony_ci}; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic const struct of_device_id bcm_qspi_of_match[] = { 12888c2ecf20Sopenharmony_ci { 12898c2ecf20Sopenharmony_ci .compatible = "brcm,spi-bcm7445-qspi", 12908c2ecf20Sopenharmony_ci .data = &bcm_qspi_rev_data, 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci }, 12938c2ecf20Sopenharmony_ci { 12948c2ecf20Sopenharmony_ci .compatible = "brcm,spi-bcm-qspi", 12958c2ecf20Sopenharmony_ci .data = &bcm_qspi_no_rev_data, 12968c2ecf20Sopenharmony_ci }, 12978c2ecf20Sopenharmony_ci { 12988c2ecf20Sopenharmony_ci .compatible = "brcm,spi-bcm7216-qspi", 12998c2ecf20Sopenharmony_ci .data = &bcm_qspi_spcr3_data, 13008c2ecf20Sopenharmony_ci }, 13018c2ecf20Sopenharmony_ci { 13028c2ecf20Sopenharmony_ci .compatible = "brcm,spi-bcm7278-qspi", 13038c2ecf20Sopenharmony_ci .data = &bcm_qspi_spcr3_data, 13048c2ecf20Sopenharmony_ci }, 13058c2ecf20Sopenharmony_ci {}, 13068c2ecf20Sopenharmony_ci}; 13078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_qspi_of_match); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ciint bcm_qspi_probe(struct platform_device *pdev, 13108c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci const struct of_device_id *of_id = NULL; 13138c2ecf20Sopenharmony_ci const struct bcm_qspi_data *data; 13148c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13158c2ecf20Sopenharmony_ci struct bcm_qspi *qspi; 13168c2ecf20Sopenharmony_ci struct spi_master *master; 13178c2ecf20Sopenharmony_ci struct resource *res; 13188c2ecf20Sopenharmony_ci int irq, ret = 0, num_ints = 0; 13198c2ecf20Sopenharmony_ci u32 val; 13208c2ecf20Sopenharmony_ci u32 rev = 0; 13218c2ecf20Sopenharmony_ci const char *name = NULL; 13228c2ecf20Sopenharmony_ci int num_irqs = ARRAY_SIZE(qspi_irq_tab); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* We only support device-tree instantiation */ 13258c2ecf20Sopenharmony_ci if (!dev->of_node) 13268c2ecf20Sopenharmony_ci return -ENODEV; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci of_id = of_match_node(bcm_qspi_of_match, dev->of_node); 13298c2ecf20Sopenharmony_ci if (!of_id) 13308c2ecf20Sopenharmony_ci return -ENODEV; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci data = of_id->data; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci master = devm_spi_alloc_master(dev, sizeof(struct bcm_qspi)); 13358c2ecf20Sopenharmony_ci if (!master) { 13368c2ecf20Sopenharmony_ci dev_err(dev, "error allocating spi_master\n"); 13378c2ecf20Sopenharmony_ci return -ENOMEM; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci qspi = spi_master_get_devdata(master); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci qspi->clk = devm_clk_get_optional(&pdev->dev, NULL); 13438c2ecf20Sopenharmony_ci if (IS_ERR(qspi->clk)) 13448c2ecf20Sopenharmony_ci return PTR_ERR(qspi->clk); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci qspi->pdev = pdev; 13478c2ecf20Sopenharmony_ci qspi->trans_pos.trans = NULL; 13488c2ecf20Sopenharmony_ci qspi->trans_pos.byte = 0; 13498c2ecf20Sopenharmony_ci qspi->trans_pos.mspi_last_trans = true; 13508c2ecf20Sopenharmony_ci qspi->master = master; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci master->bus_num = -1; 13538c2ecf20Sopenharmony_ci master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD; 13548c2ecf20Sopenharmony_ci master->setup = bcm_qspi_setup; 13558c2ecf20Sopenharmony_ci master->transfer_one = bcm_qspi_transfer_one; 13568c2ecf20Sopenharmony_ci master->mem_ops = &bcm_qspi_mem_ops; 13578c2ecf20Sopenharmony_ci master->cleanup = bcm_qspi_cleanup; 13588c2ecf20Sopenharmony_ci master->dev.of_node = dev->of_node; 13598c2ecf20Sopenharmony_ci master->num_chipselect = NUM_CHIPSELECT; 13608c2ecf20Sopenharmony_ci master->use_gpio_descriptors = true; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci qspi->big_endian = of_device_is_big_endian(dev->of_node); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "num-cs", &val)) 13658c2ecf20Sopenharmony_ci master->num_chipselect = val; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hif_mspi"); 13688c2ecf20Sopenharmony_ci if (!res) 13698c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 13708c2ecf20Sopenharmony_ci "mspi"); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci qspi->base[MSPI] = devm_ioremap_resource(dev, res); 13738c2ecf20Sopenharmony_ci if (IS_ERR(qspi->base[MSPI])) 13748c2ecf20Sopenharmony_ci return PTR_ERR(qspi->base[MSPI]); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi"); 13778c2ecf20Sopenharmony_ci if (res) { 13788c2ecf20Sopenharmony_ci qspi->base[BSPI] = devm_ioremap_resource(dev, res); 13798c2ecf20Sopenharmony_ci if (IS_ERR(qspi->base[BSPI])) 13808c2ecf20Sopenharmony_ci return PTR_ERR(qspi->base[BSPI]); 13818c2ecf20Sopenharmony_ci qspi->bspi_mode = true; 13828c2ecf20Sopenharmony_ci } else { 13838c2ecf20Sopenharmony_ci qspi->bspi_mode = false; 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci dev_info(dev, "using %smspi mode\n", qspi->bspi_mode ? "bspi-" : ""); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg"); 13898c2ecf20Sopenharmony_ci if (res) { 13908c2ecf20Sopenharmony_ci qspi->base[CHIP_SELECT] = devm_ioremap_resource(dev, res); 13918c2ecf20Sopenharmony_ci if (IS_ERR(qspi->base[CHIP_SELECT])) 13928c2ecf20Sopenharmony_ci return PTR_ERR(qspi->base[CHIP_SELECT]); 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id), 13968c2ecf20Sopenharmony_ci GFP_KERNEL); 13978c2ecf20Sopenharmony_ci if (!qspi->dev_ids) 13988c2ecf20Sopenharmony_ci return -ENOMEM; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci /* 14018c2ecf20Sopenharmony_ci * Some SoCs integrate spi controller (e.g., its interrupt bits) 14028c2ecf20Sopenharmony_ci * in specific ways 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_ci if (soc_intc) { 14058c2ecf20Sopenharmony_ci qspi->soc_intc = soc_intc; 14068c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_set(soc_intc, MSPI_DONE, true); 14078c2ecf20Sopenharmony_ci } else { 14088c2ecf20Sopenharmony_ci qspi->soc_intc = NULL; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (qspi->clk) { 14128c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qspi->clk); 14138c2ecf20Sopenharmony_ci if (ret) { 14148c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare clock\n"); 14158c2ecf20Sopenharmony_ci goto qspi_probe_err; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci qspi->base_clk = clk_get_rate(qspi->clk); 14188c2ecf20Sopenharmony_ci } else { 14198c2ecf20Sopenharmony_ci qspi->base_clk = MSPI_BASE_FREQ; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (data->has_mspi_rev) { 14238c2ecf20Sopenharmony_ci rev = bcm_qspi_read(qspi, MSPI, MSPI_REV); 14248c2ecf20Sopenharmony_ci /* some older revs do not have a MSPI_REV register */ 14258c2ecf20Sopenharmony_ci if ((rev & 0xff) == 0xff) 14268c2ecf20Sopenharmony_ci rev = 0; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci qspi->mspi_maj_rev = (rev >> 4) & 0xf; 14308c2ecf20Sopenharmony_ci qspi->mspi_min_rev = rev & 0xf; 14318c2ecf20Sopenharmony_ci qspi->mspi_spcr3_sysclk = data->has_spcr3_sysclk; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci qspi->max_speed_hz = qspi->base_clk / (bcm_qspi_spbr_min(qspi) * 2); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci /* 14368c2ecf20Sopenharmony_ci * On SW resets it is possible to have the mask still enabled 14378c2ecf20Sopenharmony_ci * Need to disable the mask and clear the status while we init 14388c2ecf20Sopenharmony_ci */ 14398c2ecf20Sopenharmony_ci bcm_qspi_hw_uninit(qspi); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci for (val = 0; val < num_irqs; val++) { 14428c2ecf20Sopenharmony_ci irq = -1; 14438c2ecf20Sopenharmony_ci name = qspi_irq_tab[val].irq_name; 14448c2ecf20Sopenharmony_ci if (qspi_irq_tab[val].irq_source == SINGLE_L2) { 14458c2ecf20Sopenharmony_ci /* get the l2 interrupts */ 14468c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, name); 14478c2ecf20Sopenharmony_ci } else if (!num_ints && soc_intc) { 14488c2ecf20Sopenharmony_ci /* all mspi, bspi intrs muxed to one L1 intr */ 14498c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (irq >= 0) { 14538c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, 14548c2ecf20Sopenharmony_ci qspi_irq_tab[val].irq_handler, 0, 14558c2ecf20Sopenharmony_ci name, 14568c2ecf20Sopenharmony_ci &qspi->dev_ids[val]); 14578c2ecf20Sopenharmony_ci if (ret < 0) { 14588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ %s not found\n", name); 14598c2ecf20Sopenharmony_ci goto qspi_unprepare_err; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci qspi->dev_ids[val].dev = qspi; 14638c2ecf20Sopenharmony_ci qspi->dev_ids[val].irqp = &qspi_irq_tab[val]; 14648c2ecf20Sopenharmony_ci num_ints++; 14658c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "registered IRQ %s %d\n", 14668c2ecf20Sopenharmony_ci qspi_irq_tab[val].irq_name, 14678c2ecf20Sopenharmony_ci irq); 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (!num_ints) { 14728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no IRQs registered, cannot init driver\n"); 14738c2ecf20Sopenharmony_ci ret = -EINVAL; 14748c2ecf20Sopenharmony_ci goto qspi_unprepare_err; 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci bcm_qspi_hw_init(qspi); 14788c2ecf20Sopenharmony_ci init_completion(&qspi->mspi_done); 14798c2ecf20Sopenharmony_ci init_completion(&qspi->bspi_done); 14808c2ecf20Sopenharmony_ci qspi->curr_cs = -1; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, qspi); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci qspi->xfer_mode.width = -1; 14858c2ecf20Sopenharmony_ci qspi->xfer_mode.addrlen = -1; 14868c2ecf20Sopenharmony_ci qspi->xfer_mode.hp = -1; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci ret = spi_register_master(master); 14898c2ecf20Sopenharmony_ci if (ret < 0) { 14908c2ecf20Sopenharmony_ci dev_err(dev, "can't register master\n"); 14918c2ecf20Sopenharmony_ci goto qspi_reg_err; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci return 0; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ciqspi_reg_err: 14978c2ecf20Sopenharmony_ci bcm_qspi_hw_uninit(qspi); 14988c2ecf20Sopenharmony_ciqspi_unprepare_err: 14998c2ecf20Sopenharmony_ci clk_disable_unprepare(qspi->clk); 15008c2ecf20Sopenharmony_ciqspi_probe_err: 15018c2ecf20Sopenharmony_ci kfree(qspi->dev_ids); 15028c2ecf20Sopenharmony_ci return ret; 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci/* probe function to be called by SoC specific platform driver probe */ 15058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_qspi_probe); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ciint bcm_qspi_remove(struct platform_device *pdev) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = platform_get_drvdata(pdev); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci spi_unregister_master(qspi->master); 15128c2ecf20Sopenharmony_ci bcm_qspi_hw_uninit(qspi); 15138c2ecf20Sopenharmony_ci clk_disable_unprepare(qspi->clk); 15148c2ecf20Sopenharmony_ci kfree(qspi->dev_ids); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci return 0; 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci/* function to be called by SoC specific platform driver remove() */ 15198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_qspi_remove); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic int __maybe_unused bcm_qspi_suspend(struct device *dev) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = dev_get_drvdata(dev); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* store the override strap value */ 15268c2ecf20Sopenharmony_ci if (!bcm_qspi_bspi_ver_three(qspi)) 15278c2ecf20Sopenharmony_ci qspi->s3_strap_override_ctrl = 15288c2ecf20Sopenharmony_ci bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci spi_master_suspend(qspi->master); 15318c2ecf20Sopenharmony_ci clk_disable_unprepare(qspi->clk); 15328c2ecf20Sopenharmony_ci bcm_qspi_hw_uninit(qspi); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci return 0; 15358c2ecf20Sopenharmony_ci}; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_cistatic int __maybe_unused bcm_qspi_resume(struct device *dev) 15388c2ecf20Sopenharmony_ci{ 15398c2ecf20Sopenharmony_ci struct bcm_qspi *qspi = dev_get_drvdata(dev); 15408c2ecf20Sopenharmony_ci int ret = 0; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci bcm_qspi_hw_init(qspi); 15438c2ecf20Sopenharmony_ci bcm_qspi_chip_select(qspi, qspi->curr_cs); 15448c2ecf20Sopenharmony_ci if (qspi->soc_intc) 15458c2ecf20Sopenharmony_ci /* enable MSPI interrupt */ 15468c2ecf20Sopenharmony_ci qspi->soc_intc->bcm_qspi_int_set(qspi->soc_intc, MSPI_DONE, 15478c2ecf20Sopenharmony_ci true); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qspi->clk); 15508c2ecf20Sopenharmony_ci if (!ret) 15518c2ecf20Sopenharmony_ci spi_master_resume(qspi->master); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci return ret; 15548c2ecf20Sopenharmony_ci} 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci/* pm_ops to be called by SoC specific platform driver */ 15598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_qspi_pm_ops); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kamal Dasu"); 15628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom QSPI driver"); 15638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 15648c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 1565