| // SPDX-License-Identifier: GPL-2.0 |
| |
| /* |
| * ASR QSPI driver |
| * |
| * Copyright (C) 2019 ASR Micro Limited |
| * |
| */ |
| |
| #include <linux/bitops.h> |
| #include <linux/clk.h> |
| #include <linux/completion.h> |
| #include <linux/delay.h> |
| #include <linux/dmaengine.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/err.h> |
| #include <linux/errno.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/iopoll.h> |
| #include <linux/jiffies.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/mutex.h> |
| #include <linux/of.h> |
| #include <linux/of_device.h> |
| #include <linux/platform_device.h> |
| #include <linux/pm_qos.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/sizes.h> |
| #include <linux/genalloc.h> |
| #include <linux/cputype.h> |
| |
| #include <soc/asr/regs-addr.h> |
| #include <linux/spi/spi.h> |
| #include <linux/spi/spi-mem.h> |
| |
| |
| /* #define ASR_DUMP_QSPI_REG 0 */ |
| |
| #define QSPI_WAIT_TIMEOUT (300) /* ms */ |
| #define QSPI_AUTOSUSPEND_TIMEOUT 2000 |
| #define ASR_MPMU_ACGR 0x1024 |
| |
| /* QSPI PMUap register */ |
| #define PMUA_QSPI_CLK_RES_CTRL 0x01282860 |
| #define QSPI_CLK_SEL(x) ((x) << 6) |
| #define QSPI_CLK_SEL_MASK GENMASK(8, 6) |
| #define QSPI_CLK_EN BIT(4) |
| #define QSPI_BUS_CLK_EN BIT(3) |
| #define QSPI_CLK_RST BIT(1) |
| #define QSPI_BUS_RST BIT(0) |
| |
| /* QSPI memory base */ |
| #if 0 |
| #define QSPI_AMBA_BASE 0x300000 |
| #define QSPI_FLASH_A1_BASE QSPI_AMBA_BASE |
| #define QSPI_FLASH_A1_TOP (QSPI_FLASH_A1_BASE + 0xa00000) |
| #define QSPI_FLASH_A2_BASE QSPI_FLASH_A1_TOP |
| #define QSPI_FLASH_A2_TOP (QSPI_FLASH_A2_BASE + 0x100000) |
| #define QSPI_FLASH_B1_BASE QSPI_FLASH_A2_TOP |
| #define QSPI_FLASH_B1_TOP (QSPI_FLASH_B1_BASE + 0x100000) |
| #define QSPI_FLASH_B2_BASE QSPI_FLASH_B1_TOP |
| #define QSPI_FLASH_B2_TOP (QSPI_FLASH_B2_BASE + 0x100000) |
| #else |
| /* AHB base addr */ |
| #define QSPI_AMBA_BASE 0x80000000 |
| #define QSPI_FLASH_A1_BASE 0x80000000 |
| #define QSPI_FLASH_A1_TOP 0x88000000 |
| #define QSPI_FLASH_A2_BASE 0x88000000 |
| #define QSPI_FLASH_A2_TOP 0x90000000 |
| #define QSPI_FLASH_B1_BASE 0x90000000 |
| #define QSPI_FLASH_B1_TOP 0x98000000 |
| #define QSPI_FLASH_B2_BASE 0x98000000 |
| #define QSPI_FLASH_B2_TOP 0xa0000000 |
| |
| #endif |
| |
| /* TX/RX/ABH buffer max */ |
| #define QSPI_RX_BUFF_MAX SZ_128 |
| #define QSPI_TX_BUFF_MAX SZ_256 |
| #define QSPI_TX_BUFF_POP_MIN 16 |
| #define QSPI_AHB_BUFF_MAX_SIZE SZ_512 |
| #define QSPI_TX_DMA_BURST SZ_32 |
| |
| #define QSPI_WAIT_BIT_CLEAR 0 |
| #define QSPI_WAIT_BIT_SET 1 |
| |
| /* clk source from PLL1 */ |
| #define QSPI_CLK_PLL1_51P2 51200000 |
| #define QSPI_CLK_PLL1_76P8 76800000 |
| #define QSPI_CLK_PLL1_102P4 102400000 |
| #define QSPI_CLK_PLL1_153P6 153600000 |
| #define QSPI_CLK_PLL1_204P8 204800000 |
| #define QSPI_CLK_PLL1_307P2 307200000 |
| #define QSPI_CLK_PLL1_409P6 409600000 |
| #define ASR_QSPI_DEFAULT_CLK_FREQ (QSPI_CLK_PLL1_102P4 >> 2) |
| |
| /* QSPI Host Registers used by the driver */ |
| #define QSPI_MCR 0x00 |
| #define QSPI_MCR_DQS_INV_EN BIT(26) |
| #define QSPI_MCR_DQS_LP_EN BIT(25) |
| #define QSPI_MCR_ISD_MASK GENMASK(19, 16) |
| #define QSPI_MCR_MDIS_MASK BIT(14) |
| #define QSPI_MCR_CLR_TXF_MASK BIT(11) |
| #define QSPI_MCR_CLR_RXF_MASK BIT(10) |
| #define QSPI_MCR_DDR_EN_MASK BIT(7) |
| #define QSPI_MCR_DQS_EN BIT(6) |
| #define QSPI_MCR_END_CFG_MASK GENMASK(3, 2) |
| #define QSPI_MCR_SWRSTHD_MASK BIT(1) |
| #define QSPI_MCR_SWRSTSD_MASK BIT(0) |
| |
| #define QSPI_TCR 0x04 |
| #define QSPI_IPCR 0x08 |
| #define QSPI_IPCR_SEQID(x) ((x) << 24) |
| |
| #define QSPI_FLSHCR 0x0c |
| |
| #define QSPI_BUF0CR 0x10 |
| #define QSPI_BUF1CR 0x14 |
| #define QSPI_BUF2CR 0x18 |
| #define QSPI_BUF3CR 0x1c |
| #define QSPI_BUF3CR_ALLMST_MASK BIT(31) |
| #define QSPI_BUF3CR_ADATSZ(x) ((x) << 8) |
| #define QSPI_BUF3CR_ADATSZ_MASK GENMASK(15, 8) |
| |
| #define QSPI_BFGENCR 0x20 |
| #define QSPI_BFGENCR_SEQID(x) ((x) << 12) |
| |
| #define QSPI_SOCCR 0x24 |
| #define QSPI_SOCCR_DLINE_EN BIT(8) |
| |
| #define QSPI_DLACR_DLINE_CODE_SHIFT 0 |
| #define QSPI_DLACR_DLINE_CODE_MASK GENMASK(7, 0) |
| #define QSPI_DLACR_DLINE_STEP_SHIFT 8 |
| #define QSPI_DLACR_DLINE_STEP_MASK GENMASK(15, 8) |
| |
| #define QSPI_BUF0IND 0x30 |
| #define QSPI_BUF1IND 0x34 |
| #define QSPI_BUF2IND 0x38 |
| #define QSPI_DLACR 0x3C |
| |
| #define QSPI_SFAR 0x100 |
| #define QSPI_SFACR 0x104 |
| |
| #define QSPI_SMPR 0x108 |
| #define QSPI_SMPR_DDRSMP_SHIFT 16 |
| #define QSPI_SMPR_DDRSMP_MASK GENMASK(18, 16) |
| #define QSPI_SMPR_FSDLY_MASK BIT(6) |
| #define QSPI_SMPR_FSPHS_MASK BIT(5) |
| #define QSPI_SMPR_HSENA_MASK BIT(0) |
| |
| #define QSPI_RBSR 0x10c |
| |
| #define QSPI_RBCT 0x110 |
| #define QSPI_RBCT_WMRK_MASK GENMASK(4, 0) |
| #define QSPI_RBCT_RXBRD_MASK BIT(8) |
| |
| #define QSPI_TBSR 0x150 |
| #define QSPI_TBDR 0x154 |
| #define QSPI_TBCT 0x158 |
| #define QSPI_TX_WMRK (QSPI_TX_DMA_BURST / 4 - 1) |
| |
| #define QSPI_SR 0x15c |
| #define QSPI_SR_BUSY BIT(0) |
| #define QSPI_SR_IP_ACC_MASK BIT(1) |
| #define QSPI_SR_AHB_ACC_MASK BIT(2) |
| #define QSPI_SR_TXFULL BIT(27) |
| |
| #define QSPI_FR 0x160 |
| #define QSPI_FR_TFF_MASK BIT(0) |
| #define QSPI_FR_IPGEF BIT(4) |
| #define QSPI_FR_IPIEF BIT(6) |
| #define QSPI_FR_IPAEF BIT(7) |
| #define QSPI_FR_IUEF BIT(11) |
| #define QSPI_FR_ABOF BIT(12) |
| #define QSPI_FR_AIBSEF BIT(13) |
| #define QSPI_FR_AITEF BIT(14) |
| #define QSPI_FR_ABSEF BIT(15) |
| #define QSPI_FR_RBDF BIT(16) |
| #define QSPI_FR_RBOF BIT(17) |
| #define QSPI_FR_ILLINE BIT(23) |
| #define QSPI_FR_TBUF BIT(26) |
| #define QSPI_FR_TBFF BIT(27) |
| #define BUFFER_FR_FLAG (QSPI_FR_ABOF| QSPI_FR_RBOF| \ |
| QSPI_FR_TBUF) |
| |
| #define COMMAND_FR_FLAG (QSPI_FR_ABSEF | QSPI_FR_AITEF | \ |
| QSPI_FR_AIBSEF | QSPI_FR_IUEF | \ |
| QSPI_FR_IPAEF |QSPI_FR_IPIEF | \ |
| QSPI_FR_IPGEF) |
| |
| #define QSPI_RSER 0x164 |
| #define QSPI_RSER_TFIE BIT(0) |
| #define QSPI_RSER_IPGEIE BIT(4) |
| #define QSPI_RSER_IPIEIE BIT(6) |
| #define QSPI_RSER_IPAEIE BIT(7) |
| #define QSPI_RSER_IUEIE BIT(11) |
| #define QSPI_RSER_ABOIE BIT(12) |
| #define QSPI_RSER_AIBSIE BIT(13) |
| #define QSPI_RSER_AITIE BIT(14) |
| #define QSPI_RSER_ABSEIE BIT(15) |
| #define QSPI_RSER_RBDIE BIT(16) |
| #define QSPI_RSER_RBOIE BIT(17) |
| #define QSPI_RSER_RBDDE BIT(21) |
| #define QSPI_RSER_ILLINIE BIT(23) |
| #define QSPI_RSER_TBFDE BIT(25) |
| #define QSPI_RSER_TBUIE BIT(26) |
| #define QSPI_RSER_TBFIE BIT(27) |
| #define BUFFER_ERROR_INT (QSPI_RSER_ABOIE| QSPI_RSER_RBOIE| \ |
| QSPI_RSER_TBUIE) |
| |
| #define COMMAND_ERROR_INT (QSPI_RSER_ABSEIE | QSPI_RSER_AITIE | \ |
| QSPI_RSER_AIBSIE | QSPI_RSER_IUEIE | \ |
| QSPI_RSER_IPAEIE |QSPI_RSER_IPIEIE | \ |
| QSPI_RSER_IPGEIE) |
| |
| #define QSPI_SPNDST 0x168 |
| #define QSPI_SPTRCLR 0x16c |
| #define QSPI_SPTRCLR_IPPTRC BIT(8) |
| #define QSPI_SPTRCLR_BFPTRC BIT(0) |
| |
| #define QSPI_SFA1AD 0x180 |
| #define QSPI_SFA2AD 0x184 |
| #define QSPI_SFB1AD 0x188 |
| #define QSPI_SFB2AD 0x18c |
| #define QSPI_DLPR 0x190 |
| #define QSPI_RBDR(x) (0x200 + ((x) * 4)) |
| |
| #define QSPI_LUTKEY 0x300 |
| #define QSPI_LUTKEY_VALUE 0x5af05af0 |
| |
| #define QSPI_LCKCR 0x304 |
| #define QSPI_LCKER_LOCK BIT(0) |
| #define QSPI_LCKER_UNLOCK BIT(1) |
| |
| #define QSPI_LUT_BASE 0x310 |
| /* 16Bytes per sequence */ |
| #define QSPI_LUT_REG(seqid, i) (QSPI_LUT_BASE + (seqid) * 16 + (i) * 4) |
| |
| /* |
| * QSPI Sequence index. |
| * index 0 is preset at boot for AHB read, |
| * index 1 is used for other command. |
| */ |
| #define SEQID_LUT_AHBREAD_ID 0 |
| #define SEQID_LUT_SHARED_ID 1 |
| |
| /* QSPI Instruction set for the LUT register */ |
| #define LUT_INSTR_STOP 0 |
| #define LUT_INSTR_CMD 1 |
| #define LUT_INSTR_ADDR 2 |
| #define LUT_INSTR_DUMMY 3 |
| #define LUT_INSTR_MODE 4 |
| #define LUT_INSTR_MODE2 5 |
| #define LUT_INSTR_MODE4 6 |
| #define LUT_INSTR_READ 7 |
| #define LUT_INSTR_WRITE 8 |
| #define LUT_INSTR_JMP_ON_CS 9 |
| #define LUT_INSTR_ADDR_DDR 10 |
| #define LUT_INSTR_MODE_DDR 11 |
| #define LUT_INSTR_MODE2_DDR 12 |
| #define LUT_INSTR_MODE4_DDR 13 |
| #define LUT_INSTR_READ_DDR 14 |
| #define LUT_INSTR_WRITE_DDR 15 |
| #define LUT_INSTR_DATA_LEARN 16 |
| #define LUT_INSTR_CMD_DDR 17 |
| |
| /* |
| * The PAD definitions for LUT register. |
| * |
| * The pad stands for the number of IO lines [0:3]. |
| * For example, the quad read needs four IO lines, |
| * so you should use LUT_PAD(4). |
| */ |
| #define LUT_PAD(x) (fls(x) - 1) |
| |
| /* |
| * One sequence must be consisted of 4 LUT enteries(16Bytes). |
| * LUT entries with the following register layout: |
| * b'31 b'0 |
| * --------------------------------------------------------------------------- |
| * |INSTR1[15~10]|PAD1[9~8]|OPRND1[7~0] | INSTR0[15~10]|PAD0[9~8]|OPRND0[7~0]| |
| * --------------------------------------------------------------------------- |
| */ |
| #define LUT_DEF(idx, ins, pad, opr) \ |
| ((((ins) << 10) | ((pad) << 8) | (opr)) << (((idx) & 0x1) * 16)) |
| |
| #define READ_FROM_CACHE_OP 0x03 |
| #define READ_FROM_CACHE_OP_Fast 0x0b |
| #define READ_FROM_CACHE_OP_X2 0x3b |
| #define READ_FROM_CACHE_OP_X4 0x6b |
| #define READ_FROM_CACHE_OP_DUALIO 0xbb |
| #define READ_FROM_CACHE_OP_QUADIO 0xeb |
| |
| u32 reg_offset_table[] = { |
| QSPI_MCR, QSPI_TCR, QSPI_IPCR, QSPI_FLSHCR, |
| QSPI_BUF0CR, QSPI_BUF1CR, QSPI_BUF2CR, QSPI_BUF3CR, |
| QSPI_BFGENCR, QSPI_SOCCR, QSPI_BUF0IND, QSPI_BUF1IND, |
| QSPI_BUF2IND, QSPI_SFAR, QSPI_SFACR, QSPI_SMPR, |
| QSPI_RBSR, QSPI_RBCT, QSPI_TBSR, QSPI_TBDR, |
| QSPI_TBCT, QSPI_SR, QSPI_FR, QSPI_RSER, |
| QSPI_SPNDST, QSPI_SPTRCLR, QSPI_SFA1AD, QSPI_SFA2AD, |
| QSPI_SFB1AD, QSPI_SFB2AD, QSPI_DLPR, QSPI_LUTKEY, |
| QSPI_LCKCR |
| }; |
| |
| #define QSPI_MAX_SEQ_NUM 16 |
| |
| /* asr qspi host priv */ |
| struct asr_qspi { |
| struct device *dev; |
| struct spi_controller *ctrl; |
| void __iomem *io_map; |
| phys_addr_t io_phys; |
| |
| void __iomem *ahb_map; |
| phys_addr_t memmap_base; |
| u32 memmap_size; |
| struct spi_mem_op *ahb_op; |
| |
| struct { |
| struct gen_pool *pool; |
| void __iomem *virt; |
| dma_addr_t dma; |
| } sram; |
| |
| u32 sfa1ad; |
| u32 sfa2ad; |
| u32 sfb1ad; |
| u32 sfb2ad; |
| |
| u32 pmuap_reg; |
| void __iomem *pmuap_addr; |
| |
| u32 rx_buf_size; |
| u32 tx_buf_size; |
| u32 ahb_buf_size; |
| u32 ahb_read_enable; |
| u32 tx_unit_size; |
| u32 rx_unit_size; |
| |
| u32 has_dtr; |
| u32 support_dqs; |
| u32 dtr_tx_delay; |
| u32 dtr_rx_delay; |
| u32 cmd_interrupt; |
| u32 fr_error_flag; |
| |
| u32 tx_dma_enable; |
| u32 tx_wmrk; |
| struct dma_chan *tx_dma; |
| struct dma_slave_config tx_dma_cfg; |
| |
| u32 rx_dma_enable; |
| struct dma_chan *rx_dma; |
| |
| struct sg_table sgt; |
| struct completion dma_completion; |
| |
| u32 cs_selected; |
| u32 max_hz; |
| u32 endian_xchg; |
| u32 dma_enable; |
| |
| struct clk *clk, *bus_clk; |
| struct completion cmd_completion; |
| struct mutex lock; |
| struct pm_qos_request pm_qos_req; |
| struct pm_qos_request pm_ddr_qos; |
| u32 lpm_qos; |
| bool rst_protect; |
| |
| /* seq id 0 and 1 is reserved */ |
| u8 seq_opcode[QSPI_MAX_SEQ_NUM]; |
| }; |
| |
| enum qpsi_cs { |
| QSPI_CS_A1 = 0, |
| QSPI_CS_A2, |
| QSPI_CS_B1, |
| QSPI_CS_B2, |
| QSPI_CS_MAX, |
| }; |
| #define QSPI_DEFAULT_CS (QSPI_CS_A1) |
| |
| |
| enum qpsi_mode { |
| QSPI_NORMAL_MODE = 0, |
| QSPI_DISABLE_MODE, |
| QSPI_STOP_MODE, |
| }; |
| |
| |
| static void qspi_writel(struct asr_qspi *qspi, u32 val, void __iomem *addr) |
| { |
| if (qspi->endian_xchg) |
| iowrite32be(val, addr); |
| else |
| iowrite32(val, addr); |
| } |
| |
| static u32 qspi_readl(struct asr_qspi *qspi, void __iomem *addr) |
| { |
| if (qspi->endian_xchg) |
| return ioread32be(addr); |
| else |
| return ioread32(addr); |
| } |
| |
| static void qspi_enter_mode(struct asr_qspi *qspi, uint32_t mode) |
| { |
| uint32_t mcr; |
| |
| mcr = qspi_readl(qspi, qspi->io_map + QSPI_MCR); |
| if (mode == QSPI_NORMAL_MODE) |
| mcr &= ~QSPI_MCR_MDIS_MASK; |
| else if (mode == QSPI_DISABLE_MODE) |
| mcr |= QSPI_MCR_MDIS_MASK; |
| qspi_writel(qspi, mcr, qspi->io_map + QSPI_MCR); |
| } |
| |
| static int asr_qspi_set_default_timing(struct asr_qspi *qspi, int clk_hz) |
| { |
| void __iomem *base = qspi->io_map; |
| u32 reg; |
| |
| /* clock settings */ |
| qspi_enter_mode(qspi, QSPI_DISABLE_MODE); |
| |
| /* disable DQS */ |
| reg = qspi_readl(qspi, base + QSPI_MCR); |
| reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN | QSPI_MCR_DQS_INV_EN); |
| qspi_writel(qspi, reg, base + QSPI_MCR); |
| |
| reg = 0; |
| qspi_writel(qspi, reg, base + QSPI_SMPR); |
| |
| /* set tx hold time */ |
| reg = 0x202; |
| if (qspi->has_dtr) |
| reg |= qspi->dtr_tx_delay << 16; |
| qspi_writel(qspi, reg, base + QSPI_FLSHCR); |
| |
| /* Module enabled */ |
| qspi_enter_mode(qspi, QSPI_NORMAL_MODE); |
| |
| return 0; |
| } |
| |
| static int qspi_set_func_clk(struct asr_qspi *qspi, int max_hz) |
| { |
| int ret = 0; |
| |
| if (qspi->has_dtr) { |
| qspi->clk = devm_clk_get(qspi->dev, "qspi_clk_dtr"); |
| if (IS_ERR_OR_NULL(qspi->clk)) { |
| dev_err(qspi->dev, "can not find the clock\n"); |
| return -EINVAL; |
| } |
| } else { |
| qspi->clk = devm_clk_get(qspi->dev, "qspi_clk"); |
| if (IS_ERR_OR_NULL(qspi->clk)) { |
| dev_err(qspi->dev, "can not find the clock\n"); |
| return -EINVAL; |
| } |
| } |
| |
| qspi->bus_clk = devm_clk_get(qspi->dev, "qspi_bus_clk"); |
| if (IS_ERR_OR_NULL(qspi->bus_clk)) { |
| dev_err(qspi->dev, "can not find the bus clock\n"); |
| return -EINVAL; |
| } |
| clk_prepare_enable(qspi->bus_clk); |
| |
| ret = clk_set_rate(qspi->clk, max_hz); |
| if (ret) { |
| dev_err(qspi->dev, "fail to set clk, ret:%d\n", ret); |
| return ret; |
| } |
| |
| ret = clk_prepare_enable(qspi->clk); |
| if (ret) { |
| dev_err(qspi->dev, "fail to enable clk, ret:%d\n", ret); |
| return ret; |
| } |
| |
| asr_qspi_set_default_timing(qspi, max_hz); |
| |
| dev_notice(qspi->dev, "bus clock %dHz, PMUap reg[0x%08x]:0x%08x\n", |
| max_hz, qspi->pmuap_reg, qspi_readl(qspi, qspi->pmuap_addr)); |
| |
| return 0; |
| } |
| |
| static void qspi_config_mfp(struct asr_qspi *qspi) |
| { |
| int cs = qspi->cs_selected; |
| |
| /* TODO: only for FPGA */ |
| #if 0 |
| if (cs == QSPI_CS_A1 || cs == QSPI_CS_A2) { |
| writel(0x1002, 0x0101e2c4); // QSPI_DAT3 |
| writel(0x1002, 0x0101e2c8); // QSPI_DAT2 |
| writel(0x1002, 0x0101e2cc); // QSPI_DAT1 |
| writel(0x1002, 0x0101e2d0); // QSPI_DAT0 |
| writel(0x1002, 0x0101e2d4); // QSPI_CLK |
| writel(0xd002, 0x0101e2d8); // QSPI_CS1 |
| writel(0xd002, 0x0101e2dc); // QSPI_CS2 |
| } |
| #endif |
| dev_info(qspi->dev, "config mfp for cs:[%d]\n", cs); |
| } |
| |
| static int asr_qspi_readl_poll_tout(struct asr_qspi *qspi, void __iomem *base, |
| u32 mask, u32 timeout_us, u8 wait_set) |
| { |
| u32 reg; |
| |
| if (qspi->endian_xchg) |
| mask = swab32(mask); |
| |
| if (wait_set) |
| return readl_poll_timeout_atomic(base, reg, (reg & mask), |
| 10, timeout_us); |
| else |
| return readl_poll_timeout_atomic(base, reg, !(reg & mask), |
| 10, timeout_us); |
| } |
| |
| static void qspi_reset(struct asr_qspi *qspi) |
| { |
| uint32_t reg; |
| int err; |
| |
| /* QSPI_SR[QSPI_SR_BUSY] must be 0 */ |
| err = asr_qspi_readl_poll_tout(qspi, qspi->io_map + QSPI_SR, |
| QSPI_SR_BUSY, QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR); |
| if (err) { |
| dev_err(qspi->dev, "failed to reset qspi host.\n"); |
| } else { |
| /* qspi softreset first */ |
| reg = qspi_readl(qspi, qspi->io_map + QSPI_MCR); |
| reg |= QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK; |
| qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); |
| reg = qspi_readl(qspi, qspi->io_map + QSPI_MCR); |
| if ((reg & 0x3) != 0x3) |
| dev_info(qspi->dev, "reset ignored 0x%x.\n", reg); |
| |
| udelay(1); |
| reg &= ~(QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK); |
| qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); |
| } |
| } |
| |
| static void qspi_write_sfar(struct asr_qspi *qspi, uint32_t val) |
| { |
| int err; |
| |
| /* QSPI_SR[IP_ACC] must be 0 */ |
| err = asr_qspi_readl_poll_tout(qspi, qspi->io_map + QSPI_SR, |
| QSPI_SR_IP_ACC_MASK, QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR); |
| if (err) |
| dev_err(qspi->dev, "failed to set QSPI_SFAR.\n"); |
| else |
| qspi_writel(qspi, val, qspi->io_map + QSPI_SFAR); |
| } |
| |
| /* |
| * IP Command Trigger could not be executed Error Flag may happen for write |
| * access to RBCT/SFAR register, need retry for these two register |
| */ |
| static void qspi_write_rbct(struct asr_qspi *qspi, uint32_t val) |
| { |
| int err; |
| |
| /* QSPI_SR[IP_ACC] must be 0 */ |
| err = asr_qspi_readl_poll_tout(qspi, qspi->io_map + QSPI_SR, |
| QSPI_SR_IP_ACC_MASK, QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR); |
| if (err) |
| dev_err(qspi->dev, "failed to set QSPI_RBCT.\n"); |
| else |
| qspi_writel(qspi, val, qspi->io_map + QSPI_RBCT); |
| } |
| |
| void qspi_init_ahbread(struct asr_qspi *qspi, int seq_id) |
| { |
| u32 buf_cfg = 0; |
| |
| buf_cfg = QSPI_BUF3CR_ALLMST_MASK | |
| QSPI_BUF3CR_ADATSZ((qspi->ahb_buf_size / 8)); |
| |
| #ifdef CONFIG_CPU_ASR1903 |
| /* Disable BUF1~BUF2, use BUF0 for all masters */ |
| qspi_writel(qspi, (512/8 - 1) * 8, qspi->io_map + QSPI_BUF0IND); |
| qspi_writel(qspi, 512, qspi->io_map + QSPI_BUF1IND); |
| qspi_writel(qspi, 512, qspi->io_map + QSPI_BUF2IND); |
| |
| /* AHB Master port */ |
| qspi_writel(qspi, buf_cfg, qspi->io_map + QSPI_BUF0CR); // other masters |
| qspi_writel(qspi, 0xe, qspi->io_map + QSPI_BUF1CR); |
| qspi_writel(qspi, 0xe, qspi->io_map + QSPI_BUF2CR); |
| qspi_writel(qspi, 0xe, qspi->io_map + QSPI_BUF3CR); |
| #else |
| /* Disable BUF0~BUF1, use BUF3 for all masters */ |
| qspi_writel(qspi, 0, qspi->io_map + QSPI_BUF0IND); |
| qspi_writel(qspi, 0, qspi->io_map + QSPI_BUF1IND); |
| qspi_writel(qspi, 0, qspi->io_map + QSPI_BUF2IND); |
| |
| /* AHB Master port */ |
| qspi_writel(qspi, 0xe, qspi->io_map + QSPI_BUF0CR); |
| qspi_writel(qspi, 0xe, qspi->io_map + QSPI_BUF1CR); |
| qspi_writel(qspi, 0xe, qspi->io_map + QSPI_BUF2CR); |
| qspi_writel(qspi, buf_cfg, qspi->io_map + QSPI_BUF3CR); // other masters |
| #endif |
| /* set AHB read sequence id */ |
| qspi_writel(qspi, QSPI_BFGENCR_SEQID(seq_id), qspi->io_map + QSPI_BFGENCR); |
| } |
| |
| void qspi_dump_reg(struct asr_qspi *qspi) |
| { |
| u32 reg = 0; |
| void __iomem *base = qspi->io_map; |
| int i; |
| |
| dev_notice(qspi->dev, "dump qspi host register:\n"); |
| for (i = 0; i < ARRAY_SIZE(reg_offset_table); i++) { |
| if (i > 0 && (i % 4 == 0)) |
| dev_notice(qspi->dev, "\n"); |
| reg = qspi_readl(qspi, base + reg_offset_table[i]); |
| dev_notice(qspi->dev, "offset[0x%03x]:0x%08x\t\t", |
| reg_offset_table[i], reg); |
| } |
| |
| dev_notice(qspi->dev, "\ndump AHB read LUT:\n"); |
| for (i = 0; i < 4; i++) { |
| reg = qspi_readl(qspi, base + QSPI_LUT_REG(SEQID_LUT_AHBREAD_ID, i)); |
| dev_notice(qspi->dev, "lut_reg[0x%03x]:0x%08x\t\t", |
| QSPI_LUT_REG(SEQID_LUT_AHBREAD_ID, i), reg); |
| } |
| |
| dev_notice(qspi->dev, "\ndump shared LUT:\n"); |
| for (i = 0; i < 4; i++) { |
| reg = qspi_readl(qspi, base + QSPI_LUT_REG(SEQID_LUT_SHARED_ID, i)); |
| dev_notice(qspi->dev, "lut_reg[0x%03x]:0x%08x\t\t", |
| QSPI_LUT_REG(SEQID_LUT_SHARED_ID, i), reg); |
| } |
| dev_notice(qspi->dev, "\n"); |
| } |
| |
| /* |
| * If the slave device content being changed by Write/Erase, need to |
| * invalidate the AHB buffer. This can be achieved by doing the reset |
| * of controller after setting MCR0[SWRESET] bit. |
| */ |
| static inline void asr_qspi_invalid(struct asr_qspi *qspi) |
| { |
| u32 reg; |
| |
| reg = qspi_readl(qspi, qspi->io_map + QSPI_MCR); |
| reg |= QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK; |
| qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); |
| |
| /* |
| * The minimum delay : 1 AHB + 2 SFCK clocks. |
| * Delay 1 us is enough. |
| */ |
| udelay(1); |
| |
| reg &= ~(QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK); |
| qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); |
| } |
| |
| static u8 asr_qspi_prepare_lut(struct asr_qspi *qspi, |
| const struct spi_mem_op *op, u32 seq_id) |
| { |
| u32 lutval[4] = {0,}; |
| int lutidx = 0; |
| int i; |
| u8 opcode; |
| |
| if (seq_id != SEQID_LUT_AHBREAD_ID) { |
| for (i = 2; i < QSPI_MAX_SEQ_NUM; i++) { |
| opcode = qspi->seq_opcode[i]; |
| if (!opcode) { |
| seq_id = i; |
| break; |
| } else if (opcode == op->cmd.opcode) { |
| return i; |
| } |
| } |
| } |
| |
| /* qspi cmd */ |
| lutval[0] |= LUT_DEF(lutidx, |
| (op->cmd.dtr ? LUT_INSTR_CMD_DDR : LUT_INSTR_CMD), |
| LUT_PAD(op->cmd.buswidth), |
| op->cmd.opcode); |
| lutidx++; |
| |
| /* addr bytes */ |
| if (op->addr.nbytes) { |
| lutval[lutidx / 2] |= |
| LUT_DEF(lutidx, |
| (op->addr.dtr ? LUT_INSTR_ADDR_DDR : LUT_INSTR_ADDR), |
| LUT_PAD(op->addr.buswidth), |
| op->addr.nbytes * 8); |
| lutidx++; |
| } |
| |
| /* dummy bytes, if needed */ |
| if (op->dummy.nbytes) { |
| lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_INSTR_DUMMY, |
| LUT_PAD(op->dummy.buswidth), |
| op->dummy.nbytes * 8 / |
| op->dummy.buswidth / |
| (op->dummy.dtr ? 2 : 1)); |
| lutidx++; |
| } |
| |
| /* read/write data bytes */ |
| if (op->data.buswidth) { |
| u8 inst; |
| |
| if ( op->data.dir == SPI_MEM_DATA_IN) { |
| if (op->data.dtr) |
| inst = LUT_INSTR_READ_DDR; |
| else |
| inst = LUT_INSTR_READ; |
| } else { |
| if (op->data.dtr) |
| inst = LUT_INSTR_WRITE_DDR; |
| else |
| inst = LUT_INSTR_WRITE; |
| } |
| lutval[lutidx / 2] |= LUT_DEF(lutidx, inst, |
| LUT_PAD(op->data.buswidth), |
| 0); |
| lutidx++; |
| } |
| |
| /* stop condition. */ |
| lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_INSTR_STOP, 0, 0); |
| |
| /* unlock LUT */ |
| qspi_writel(qspi, QSPI_LUTKEY_VALUE, qspi->io_map + QSPI_LUTKEY); |
| qspi_writel(qspi, QSPI_LCKER_UNLOCK, qspi->io_map + QSPI_LCKCR); |
| |
| /* fill LUT register */ |
| for (i = 0; i < ARRAY_SIZE(lutval); i++) |
| qspi_writel(qspi, lutval[i], qspi->io_map + QSPI_LUT_REG(seq_id, i)); |
| |
| /* lock LUT */ |
| qspi_writel(qspi, QSPI_LUTKEY_VALUE, qspi->io_map + QSPI_LUTKEY); |
| qspi_writel(qspi, QSPI_LCKER_LOCK, qspi->io_map + QSPI_LCKCR); |
| |
| dev_dbg(qspi->dev, "opcode:0x%x, lut_reg[0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x]\n", |
| op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3]); |
| |
| qspi->seq_opcode[seq_id] = op->cmd.opcode; |
| return seq_id; |
| } |
| |
| static void asr_qspi_enable_interrupt(struct asr_qspi *qspi, u32 val) |
| { |
| u32 resr = 0; |
| |
| resr = qspi_readl(qspi, qspi->io_map + QSPI_RSER); |
| resr |= val; |
| qspi_writel(qspi, resr, qspi->io_map + QSPI_RSER); |
| } |
| |
| static void asr_qspi_disable_interrupt(struct asr_qspi *qspi, u32 val) |
| { |
| u32 resr = 0; |
| |
| resr = qspi_readl(qspi, qspi->io_map + QSPI_RSER); |
| resr &= ~val; |
| qspi_writel(qspi, resr, qspi->io_map + QSPI_RSER); |
| } |
| |
| static void asr_qspi_prepare_dma(struct asr_qspi *qspi) |
| { |
| struct dma_slave_config dma_cfg; |
| struct device *dev = qspi->dev; |
| dma_cap_mask_t mask; |
| |
| /* RX DMA: DMA_MEMCPY type */ |
| dma_cap_zero(mask); |
| dma_cap_set(DMA_MEMCPY, mask); |
| |
| if (qspi->rx_dma_enable) { |
| qspi->rx_dma = dma_request_chan_by_mask(&mask); |
| if (IS_ERR_OR_NULL(qspi->rx_dma)) { |
| dev_err(dev, "rx dma request channel failed\n"); |
| qspi->rx_dma = NULL; |
| qspi->rx_dma_enable = 0; |
| } else { |
| dev_notice(dev, "rx dma enable, channel:%d\n", |
| qspi->rx_dma->chan_id); |
| } |
| } else { |
| dev_notice(dev, "rx dma not enable\n"); |
| } |
| |
| /* TX DMA: DMA_SLAVE type */ |
| if (qspi->tx_dma_enable) { |
| qspi->tx_dma = dma_request_slave_channel(dev, "tx-dma"); |
| if (qspi->tx_dma) { |
| memset(&dma_cfg, 0, sizeof(struct dma_slave_config)); |
| dma_cfg.direction = DMA_MEM_TO_DEV; |
| dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
| dma_cfg.dst_addr = qspi->io_phys + QSPI_TBDR - 4; |
| dma_cfg.dst_maxburst = QSPI_TX_DMA_BURST; |
| if (dmaengine_slave_config(qspi->tx_dma, &dma_cfg)) { |
| dev_err(dev, "tx dma slave config failed\n"); |
| dma_release_channel(qspi->tx_dma); |
| qspi->tx_dma = NULL; |
| qspi->tx_dma_enable = 0; |
| } else { |
| dev_notice(dev, "tx dma enable, channel:%d\n", |
| qspi->tx_dma->chan_id); |
| } |
| } |
| } else { |
| dev_notice(dev, "tx dma not enable\n"); |
| } |
| |
| if (qspi->tx_dma || qspi->rx_dma) |
| init_completion(&qspi->dma_completion); |
| } |
| |
| static void asr_qspi_dma_callback(void *arg) |
| { |
| struct completion *dma_completion = arg; |
| |
| complete(dma_completion); |
| } |
| |
| int asr_qspi_tx_dma_exec(struct asr_qspi *qspi, |
| const struct spi_mem_op *op) |
| { |
| struct dma_async_tx_descriptor *desc; |
| enum dma_transfer_direction dma_dir = DMA_MEM_TO_DEV; |
| dma_cookie_t cookie; |
| int err = 0; |
| |
| if (qspi->sram.virt) { |
| /* use buffer from sram to avoid tx underrun error */ |
| memcpy(qspi->sram.virt, op->data.buf.in, op->data.nbytes); |
| |
| desc = dmaengine_prep_slave_single(qspi->tx_dma, |
| qspi->sram.dma, op->data.nbytes, dma_dir, |
| DMA_PREP_INTERRUPT); |
| if (!desc) { |
| dev_err(qspi->dev, "tx dma prep error\n"); |
| return -ENOMEM; |
| } |
| } else { |
| if (!virt_addr_valid(op->data.buf.in) || |
| spi_controller_dma_map_mem_op_data(qspi->ctrl, |
| op, &qspi->sgt)) { |
| dev_err(qspi->dev, "tx dma map error\n"); |
| return -EIO; |
| } |
| |
| desc = dmaengine_prep_slave_sg( |
| qspi->tx_dma, qspi->sgt.sgl, qspi->sgt.nents, |
| dma_dir, DMA_PREP_INTERRUPT); |
| if (!desc) { |
| dev_err(qspi->dev, "tx dma prep error\n"); |
| err = -ENOMEM; |
| goto out; |
| } |
| } |
| |
| reinit_completion(&qspi->dma_completion); |
| desc->callback = asr_qspi_dma_callback; |
| desc->callback_param = &qspi->dma_completion; |
| |
| cookie = dmaengine_submit(desc); |
| err = dma_submit_error(cookie); |
| if (err) { |
| dev_err(qspi->dev, "tx dma dmaengine_submit error\n"); |
| goto out; |
| } |
| |
| dma_async_issue_pending(qspi->tx_dma); |
| |
| return 0; |
| |
| out: |
| if (!qspi->sram.virt) |
| spi_controller_dma_unmap_mem_op_data(qspi->ctrl, op, &qspi->sgt); |
| return err; |
| } |
| |
| int asr_qspi_rx_dma_exec(struct asr_qspi *qspi, dma_addr_t dma_dst, |
| dma_addr_t dma_src, size_t len) |
| { |
| dma_cookie_t cookie; |
| enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; |
| struct dma_async_tx_descriptor *desc; |
| int ret; |
| |
| desc = dmaengine_prep_dma_memcpy(qspi->rx_dma, dma_dst, dma_src, len, flags); |
| if (!desc) { |
| dev_err(qspi->dev, "dmaengine_prep_dma_memcpy error\n"); |
| return -EIO; |
| } |
| |
| reinit_completion(&qspi->dma_completion); |
| desc->callback = asr_qspi_dma_callback; |
| desc->callback_param = &qspi->dma_completion; |
| cookie = dmaengine_submit(desc); |
| ret = dma_submit_error(cookie); |
| if (ret) { |
| dev_err(qspi->dev, "dma_submit_error %d\n", cookie); |
| return -EIO; |
| } |
| |
| dma_async_issue_pending(qspi->rx_dma); |
| ret = wait_for_completion_timeout(&qspi->dma_completion, |
| msecs_to_jiffies(len)); |
| if (ret <= 0) { |
| dmaengine_terminate_sync(qspi->rx_dma); |
| dev_err(qspi->dev, "DMA wait_for_completion_timeout\n"); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int asr_qspi_rx_dma_sg(struct asr_qspi *qspi, struct sg_table rx_sg, |
| loff_t from) |
| { |
| struct scatterlist *sg; |
| dma_addr_t dma_src = qspi->memmap_base + from; |
| dma_addr_t dma_dst; |
| int i, len, ret; |
| |
| for_each_sg(rx_sg.sgl, sg, rx_sg.nents, i) { |
| dma_dst = sg_dma_address(sg); |
| len = sg_dma_len(sg); |
| dev_dbg(qspi->dev, "rx dma, dst:0x%08x, src:0x%08x, len:%d\n", |
| dma_dst, dma_src, len); |
| ret = asr_qspi_rx_dma_exec(qspi, dma_dst, dma_src, len); |
| if (ret) |
| return ret; |
| dma_src += len; |
| } |
| |
| return 0; |
| } |
| |
| static int asr_qspi_ahb_read(struct asr_qspi *qspi, |
| const struct spi_mem_op *op) |
| { |
| int ret = 0; |
| u32 len = op->data.nbytes; |
| u32 from = op->addr.val; |
| struct sg_table sgt; |
| |
| /* Read out the data directly from the AHB buffer. */ |
| dev_dbg(qspi->dev, "ahb read %d bytes from address:0x%llx\n", |
| len, (qspi->memmap_base + op->addr.val)); |
| if (from + len > qspi->memmap_size) |
| return -ENOTSUPP; |
| |
| /* firstly try the DMA */ |
| if (qspi->rx_dma_enable) { |
| if (virt_addr_valid(op->data.buf.in) && |
| !spi_controller_dma_map_mem_op_data(qspi->ctrl, op, &sgt)) { |
| ret = asr_qspi_rx_dma_sg(qspi, sgt, from); |
| spi_controller_dma_unmap_mem_op_data(qspi->ctrl, op, &sgt); |
| } else { |
| ret = -EIO; |
| dev_err(qspi->dev, "spi_controller_dma_map_mem_op_data error\n"); |
| } |
| |
| /* DMA completed */ |
| if (!ret) |
| return 0; |
| } |
| |
| if (qspi->rx_dma_enable && ret) { |
| dev_notice(qspi->dev, "rx dma read fallback to memcpy read.\n"); |
| } |
| |
| restart: |
| qspi->rst_protect = false; |
| if (!qspi->rx_dma_enable || (qspi->rx_dma_enable && ret)) { |
| memcpy_fromio(op->data.buf.in, (qspi->ahb_map + op->addr.val), len); |
| } |
| |
| if (qspi->rst_protect) { |
| dev_info_ratelimited(qspi->dev, "retry read for reset protect\n"); |
| goto restart; |
| } |
| |
| return 0; |
| } |
| |
| static int asr_qspi_fill_txfifo(struct asr_qspi *qspi, |
| const struct spi_mem_op *op) |
| { |
| void __iomem *base = qspi->io_map; |
| int i; |
| u32 val; |
| u32 tbsr; |
| u32 wait_cnt; |
| |
| if (!qspi->tx_dma_enable || op->data.nbytes < QSPI_TX_BUFF_POP_MIN*2 || |
| (op->data.nbytes % QSPI_TX_DMA_BURST)) { |
| int tbdr_cnt = 0; |
| |
| qspi->tx_wmrk = 0; |
| for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 4); i += 4) { |
| memcpy(&val, op->data.buf.out + i, 4); |
| qspi_writel(qspi, val, base + QSPI_TBDR); |
| tbdr_cnt += 4; |
| } |
| |
| if (i < op->data.nbytes) { |
| memcpy(&val, op->data.buf.out + i, op->data.nbytes - i); |
| qspi_writel(qspi, val, base + QSPI_TBDR); |
| tbdr_cnt += 4; |
| } |
| |
| /* |
| * There must be at least 128bit data available in TX FIFO |
| * for any pop operation otherwise QSPI_FR[TBUF] will be set |
| */ |
| tbdr_cnt = tbdr_cnt % QSPI_TX_BUFF_POP_MIN; |
| for (i = tbdr_cnt; i < QSPI_TX_BUFF_POP_MIN; i += 4) |
| qspi_writel(qspi, 0, base + QSPI_TBDR); |
| } else { |
| /* |
| * Note that the number of bytes per DMA loop is determined |
| * by thee size of the QSPI_TBCT[WMRK]. |
| * bytes per DMA loop = (QSPI_TBCT[WMRK] + 1) * 4. |
| * set QSPI_TX_WMRK as the TX watermark. |
| */ |
| qspi->tx_wmrk = QSPI_TX_WMRK; |
| qspi_writel(qspi, qspi->tx_wmrk, base + QSPI_TBCT); |
| |
| /* increase ddr freq for tx dma, avoid fifo underrun */ |
| if (!qspi->sram.virt && qspi->tx_unit_size > qspi->tx_buf_size) |
| pm_qos_update_request_timeout( |
| &qspi->pm_ddr_qos, INT_MAX, 100*1000); |
| |
| /* config DMA channel and start */ |
| if (asr_qspi_tx_dma_exec(qspi, op)) { |
| qspi->tx_wmrk = 0; |
| dev_err(qspi->dev, "failed to start tx dma\n"); |
| return -EIO; |
| } |
| /* enable DMA request */ |
| asr_qspi_enable_interrupt(qspi, QSPI_RSER_TBFDE); |
| |
| /* |
| * before trigger qspi to send data to external bus, TX bufer |
| * need to have some data, or underrun error may happen. |
| * DMA need some time to write data to TX buffer, so add |
| * a delay here for this requirement. |
| */ |
| wait_cnt = 0; |
| do { |
| tbsr = qspi_readl(qspi, base + QSPI_TBSR); |
| tbsr = 4 * (tbsr >> 16); |
| if (tbsr >= min_t(unsigned int, qspi->tx_buf_size, |
| op->data.nbytes)) |
| break; |
| |
| if (wait_cnt > 10050) { |
| dev_err(qspi->dev, |
| "TX DMA failed, TBSR=0x%x\n", tbsr); |
| qspi_dump_reg(qspi); |
| |
| /* disable all interrupts */ |
| qspi_writel(qspi, 0, qspi->io_map + QSPI_RSER); |
| dmaengine_dump_status(qspi->tx_dma); |
| dmaengine_terminate_all(qspi->tx_dma); |
| if (!qspi->sram.virt) |
| spi_controller_dma_unmap_mem_op_data( |
| qspi->ctrl, op, &qspi->sgt); |
| qspi->tx_wmrk = 0; |
| |
| return -EAGAIN; |
| } |
| |
| if (wait_cnt++ >= 10000) |
| msleep(10); |
| else |
| udelay(1); |
| } while (1); |
| } |
| |
| return 0; |
| } |
| |
| static void asr_qspi_read_rxfifo(struct asr_qspi *qspi, |
| const struct spi_mem_op *op) |
| { |
| void __iomem *base = qspi->io_map; |
| int i; |
| u8 *buf = op->data.buf.in; |
| u32 val; |
| |
| dev_dbg(qspi->dev, "ip read %d bytes\n", op->data.nbytes); |
| for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 4); i += 4) { |
| val = qspi_readl(qspi, base + QSPI_RBDR(i / 4)); |
| memcpy(buf + i, &val, 4); |
| } |
| |
| if (i < op->data.nbytes) { |
| val = qspi_readl(qspi, base + QSPI_RBDR(i / 4)); |
| memcpy(buf + i, &val, op->data.nbytes - i); |
| } |
| } |
| |
| static irqreturn_t asr_qspi_reset_handler(int irq, void *dev_id) |
| { |
| struct asr_qspi *qspi = dev_id; |
| |
| qspi->rst_protect = true; |
| dev_info_ratelimited(qspi->dev, "qspi catch reset signal\n"); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t asr_qspi_irq_handler(int irq, void *dev_id) |
| { |
| struct asr_qspi *qspi = dev_id; |
| u32 fr; |
| |
| fr = qspi_readl(qspi, qspi->io_map + QSPI_FR); |
| qspi_writel(qspi, fr & ~QSPI_FR_RBDF, qspi->io_map + QSPI_FR); |
| dev_dbg(qspi->dev, "QSPI_FR:0x%08x\n", fr); |
| |
| /* check QSPI_FR error flag */ |
| if (fr & (COMMAND_FR_FLAG | BUFFER_FR_FLAG)) { |
| qspi->fr_error_flag = fr & (COMMAND_FR_FLAG | BUFFER_FR_FLAG); |
| |
| if (fr & QSPI_FR_IPGEF) |
| dev_err(qspi->dev, "IP command trigger during AHB grant\n"); |
| if (fr & QSPI_FR_IPIEF) |
| dev_err(qspi->dev, "IP command trigger could not be executed\n"); |
| if (fr & QSPI_FR_IPAEF) |
| dev_err(qspi->dev, "IP command trigger during AHB access\n"); |
| if (fr & QSPI_FR_IUEF) |
| dev_err(qspi->dev, "IP command usage error\n"); |
| if (fr & QSPI_FR_AIBSEF) |
| dev_err(qspi->dev, "AHB illegal burst size error\n"); |
| if (fr & QSPI_FR_AITEF) |
| dev_err(qspi->dev, "AHB illegal trancaction error\n"); |
| if (fr & QSPI_FR_ABSEF) |
| dev_err(qspi->dev, "AHB sequence error\n"); |
| |
| if (fr & QSPI_FR_TBUF) |
| dev_err_ratelimited(qspi->dev, "TX buffer underrun\n"); |
| if (fr & QSPI_FR_RBOF) |
| dev_err(qspi->dev, "RX buffer overflow\n"); |
| if (fr & QSPI_FR_ABOF) |
| dev_err(qspi->dev, "AHB buffer overflow\n"); |
| } |
| |
| if (qspi->cmd_interrupt && (fr & (QSPI_FR_TFF_MASK | COMMAND_FR_FLAG | BUFFER_FR_FLAG))) |
| complete(&qspi->cmd_completion); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int asr_qspi_do_op(struct asr_qspi *qspi, const struct spi_mem_op *op, |
| u8 seq_id) |
| { |
| void __iomem *base = qspi->io_map; |
| int err = 0; |
| u32 mcr; |
| //void __iomem *mpmu_acgr = regs_addr_get_va(REGS_ADDR_MPMU) + ASR_MPMU_ACGR; |
| |
| #ifdef ASR_DUMP_QSPI_REG |
| /* dump reg if need */ |
| qspi_dump_reg(qspi); |
| #endif |
| |
| if (qspi->cmd_interrupt) { |
| asr_qspi_enable_interrupt(qspi, QSPI_RSER_TFIE | BUFFER_ERROR_INT | COMMAND_ERROR_INT); |
| init_completion(&qspi->cmd_completion); |
| } |
| |
| /* trigger LUT */ |
| qspi_writel(qspi, op->data.nbytes | QSPI_IPCR_SEQID(seq_id), |
| base + QSPI_IPCR); |
| |
| /* wait for the transaction complete */ |
| if (qspi->cmd_interrupt) { |
| if (!wait_for_completion_timeout(&qspi->cmd_completion, |
| msecs_to_jiffies(1000))) |
| err = -ETIMEDOUT; |
| } else { |
| err = asr_qspi_readl_poll_tout(qspi, base + QSPI_FR, QSPI_FR_TFF_MASK, |
| QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_SET); |
| } |
| if (err) { |
| dev_err(qspi->dev, "opcode:0x%x transaction abort, ret:%d, error flag:0x%08x\n", |
| op->cmd.opcode, err, qspi->fr_error_flag); |
| dev_err(qspi->dev, "pmuap[0x%08x]:0x%08x\n", qspi->pmuap_reg, qspi_readl(qspi, qspi->pmuap_addr)); |
| //dev_err(qspi->dev, "mpmu[0x%08x]:0x%08x\n", ASR_MPMU_ACGR, qspi_readl(qspi, mpmu_acgr)); |
| qspi_dump_reg(qspi); |
| goto tx_dma_unmap; |
| } |
| |
| /* read RX buffer for IP command read */ |
| if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN) { |
| #ifdef ASR_DUMP_QSPI_REG |
| qspi_dump_reg(qspi); |
| #endif |
| asr_qspi_read_rxfifo(qspi, op); |
| } |
| |
| if (qspi->fr_error_flag & QSPI_FR_TBUF) { |
| /* abort current dma transfer */ |
| if (qspi->tx_dma_enable) |
| dmaengine_terminate_all(qspi->tx_dma); |
| |
| /* clear TX buf */ |
| mcr = qspi_readl(qspi, qspi->io_map + QSPI_MCR); |
| mcr |= QSPI_MCR_CLR_TXF_MASK ; |
| qspi_writel(qspi, mcr, qspi->io_map + QSPI_MCR); |
| |
| err = -EAGAIN; |
| } |
| |
| tx_dma_unmap: |
| if (qspi->tx_wmrk) { |
| if (!qspi->sram.virt) |
| spi_controller_dma_unmap_mem_op_data(qspi->ctrl, op, &qspi->sgt); |
| qspi->tx_wmrk = 0; |
| } |
| |
| /* disable all interrupts */ |
| qspi_writel(qspi, 0, qspi->io_map + QSPI_RSER); |
| return err; |
| } |
| |
| static void dump_spi_mem_op_info(struct asr_qspi *qspi, |
| const struct spi_mem_op *op) |
| { |
| dev_dbg(qspi->dev, "cmd.opcode:0x%x\n", op->cmd.opcode); |
| dev_dbg(qspi->dev, "cmd.buswidth:%d\n", op->cmd.buswidth); |
| dev_dbg(qspi->dev, "addr.nbytes:%d,\n", op->addr.nbytes); |
| dev_dbg(qspi->dev, "addr.buswidth:%d\n", op->addr.buswidth); |
| dev_dbg(qspi->dev, "addr.val:0x%llx\n", op->addr.val); |
| dev_dbg(qspi->dev, "dummy.nbytes:%d\n", op->dummy.nbytes); |
| dev_dbg(qspi->dev, "dummy.buswidth:%d\n", op->dummy.buswidth); |
| dev_dbg(qspi->dev, "%s data.nbytes:%d\n", |
| (op->data.dir == SPI_MEM_DATA_IN) ? "read" :"write", |
| op->data.nbytes); |
| dev_dbg(qspi->dev, "data.buswidth:%d\n", op->data.buswidth); |
| dev_dbg(qspi->dev, "data.buf:0x%p\n", op->data.buf.in); |
| } |
| |
| static int asr_qspi_check_buswidth(struct asr_qspi *qspi, u8 width) |
| { |
| switch (width) { |
| case 1: |
| case 2: |
| case 4: |
| return 0; |
| } |
| |
| return -ENOTSUPP; |
| } |
| |
| static bool asr_qspi_supports_op(struct spi_mem *mem, |
| const struct spi_mem_op *op) |
| { |
| struct asr_qspi *qspi = spi_controller_get_devdata(mem->spi->master); |
| int ret; |
| int op_dtr; |
| |
| ret = asr_qspi_check_buswidth(qspi, op->cmd.buswidth); |
| |
| if (op->addr.nbytes) |
| ret |= asr_qspi_check_buswidth(qspi, op->addr.buswidth); |
| |
| if (op->dummy.nbytes) |
| ret |= asr_qspi_check_buswidth(qspi, op->dummy.buswidth); |
| |
| if (op->data.nbytes) |
| ret |= asr_qspi_check_buswidth(qspi, op->data.buswidth); |
| |
| if (ret) |
| return false; |
| |
| /* address bytes should be equal to or less than 4 bytes */ |
| if (op->addr.nbytes > 4) |
| return false; |
| |
| /* check controller TX/RX buffer limits and alignment */ |
| if (op->data.dir == SPI_MEM_DATA_IN && |
| (op->data.nbytes > qspi->rx_unit_size || |
| (op->data.nbytes > qspi->rx_buf_size - 4 && !IS_ALIGNED(op->data.nbytes, 4)))) { |
| return false; |
| } |
| |
| if (op->data.dir == SPI_MEM_DATA_OUT && op->data.nbytes > qspi->tx_unit_size) |
| return false; |
| |
| /* |
| * If requested address value is greater than controller assigned |
| * memory mapped space, return error as it didn't fit in the range. |
| */ |
| if (op->addr.val >= qspi->memmap_size) { |
| pr_err("asr_qspi_supports_op: addr.val:%lld greater than the map size\n", op->addr.val); |
| return false; |
| } |
| |
| /* number of dummy clock cycles should be <= 64 cycles */ |
| if (op->dummy.buswidth && |
| (op->dummy.nbytes * 8 / op->dummy.buswidth > 64)) |
| return false; |
| |
| if (op->cmd.dtr || op->addr.dtr || op->data.dtr) |
| op_dtr = 1; |
| else |
| op_dtr = 0; |
| |
| if (!qspi->has_dtr && op_dtr) |
| return false; |
| |
| return true; |
| } |
| |
| void asr_qspi_adjust_tx_size(struct spi_mem *mem, u32 reduce_sz) |
| { |
| struct asr_qspi *qspi = spi_controller_get_devdata(mem->spi->master); |
| |
| if (qspi->tx_dma_enable) { |
| if (reduce_sz) |
| qspi->tx_unit_size = qspi->tx_buf_size; |
| else |
| qspi->tx_unit_size = SZ_4K; |
| } |
| } |
| |
| static int __asr_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) |
| { |
| struct asr_qspi *qspi = spi_controller_get_devdata(mem->spi->master); |
| void __iomem *mpmu_acgr = regs_addr_get_va(REGS_ADDR_MPMU) + ASR_MPMU_ACGR; |
| void __iomem *base = qspi->io_map; |
| int err = 0; |
| u32 mask; |
| u32 reg; |
| u8 seq_id; |
| |
| mutex_lock(&qspi->lock); |
| |
| dump_spi_mem_op_info(qspi, op); |
| |
| /* wait for controller being ready */ |
| mask = QSPI_SR_BUSY | QSPI_SR_IP_ACC_MASK | QSPI_SR_AHB_ACC_MASK; |
| err = asr_qspi_readl_poll_tout(qspi, base + QSPI_SR, mask, |
| QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR); |
| if (err) { |
| dev_err(qspi->dev, "controller not ready!\n"); |
| dev_err(qspi->dev, "pmuap[0x%08x]:0x%08x\n", qspi->pmuap_reg, |
| qspi_readl(qspi, qspi->pmuap_addr)); |
| dev_err(qspi->dev, "mpmu[0x%08x]:0x%08x\n", ASR_MPMU_ACGR, |
| qspi_readl(qspi, mpmu_acgr)); |
| qspi_dump_reg(qspi); |
| mutex_unlock(&qspi->lock); |
| return err; |
| } |
| |
| /* clear TX/RX buffer before transaction */ |
| reg = qspi_readl(qspi, base + QSPI_MCR); |
| reg |= QSPI_MCR_CLR_TXF_MASK | QSPI_MCR_CLR_RXF_MASK; |
| qspi_writel(qspi, reg, base + QSPI_MCR); |
| |
| /* |
| * reset the sequence pointers whenever the sequence ID is changed by |
| * updating the SEDID filed in QSPI_IPCR OR QSPI_BFGENCR. |
| */ |
| reg = qspi_readl(qspi, base + QSPI_SPTRCLR); |
| reg |= (QSPI_SPTRCLR_IPPTRC | QSPI_SPTRCLR_BFPTRC); |
| qspi_writel(qspi, reg, base + QSPI_SPTRCLR); |
| |
| /* set the flash address into the QSPI_SFAR */ |
| qspi_write_sfar(qspi, qspi->memmap_base + op->addr.val); |
| |
| /* clear QSPI_FR before trigger LUT command */ |
| reg = qspi_readl(qspi, base + QSPI_FR); |
| if (reg) |
| qspi_writel(qspi, reg, base + QSPI_FR); |
| qspi->fr_error_flag = 0; |
| |
| /* IP command */ |
| seq_id = asr_qspi_prepare_lut(qspi, op, SEQID_LUT_SHARED_ID); |
| if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) |
| err = asr_qspi_fill_txfifo(qspi, op); |
| if (!err) |
| err = asr_qspi_do_op(qspi, op, seq_id); |
| |
| /* invalidate the data in the AHB buffer. */ |
| if (op->cmd.opcode != 0x1F && op->cmd.opcode != 0x0F) |
| asr_qspi_invalid(qspi); |
| |
| mutex_unlock(&qspi->lock); |
| |
| return err; |
| } |
| |
| static int asr_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) |
| { |
| struct asr_qspi *qspi = spi_controller_get_devdata(mem->spi->master); |
| |
| if (op->data.dir == SPI_MEM_DATA_OUT) { |
| if (op->data.nbytes > qspi->tx_unit_size) |
| op->data.nbytes = qspi->tx_unit_size; |
| } else { |
| if (op->data.nbytes > qspi->rx_unit_size) |
| op->data.nbytes = qspi->rx_unit_size; |
| } |
| |
| return 0; |
| } |
| |
| static int asr_qspi_config_dqs_clk(struct asr_qspi *qspi, int dcode) |
| { |
| void __iomem *base = qspi->io_map; |
| u32 reg; |
| |
| reg = qspi_readl(qspi, base + QSPI_MCR); |
| |
| if (dcode <= 0 || dcode > 255) { |
| reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN | |
| QSPI_MCR_DQS_INV_EN); |
| qspi_writel(qspi, reg, base + QSPI_MCR); |
| return -1; |
| } |
| |
| reg |= QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN | QSPI_MCR_DQS_INV_EN; |
| qspi_writel(qspi, reg, base + QSPI_MCR); |
| |
| /* DQS enabled, use sample point N/1 */ |
| qspi_writel(qspi, 0x0, base + QSPI_SMPR); |
| |
| reg = qspi_readl(qspi, base + QSPI_SOCCR); |
| reg |= QSPI_SOCCR_DLINE_EN; |
| qspi_writel(qspi, reg, base + QSPI_SOCCR); |
| |
| reg = qspi_readl(qspi, base + QSPI_DLACR); |
| reg &= ~QSPI_DLACR_DLINE_STEP_MASK; |
| reg = 0x7 << QSPI_DLACR_DLINE_STEP_SHIFT; |
| reg |= dcode & QSPI_DLACR_DLINE_CODE_MASK; |
| qspi_writel(qspi, reg, base + QSPI_DLACR); |
| |
| asr_qspi_invalid(qspi); |
| dev_info(qspi->dev, "enable DQS clock, QSPI_DLACR=0x%x\n", reg); |
| return 0; |
| } |
| |
| static int __asr_qspi_adjust_timing(struct asr_qspi *qspi, |
| struct spi_mem_timing *timing, int clk_hz) |
| { |
| void __iomem *base = qspi->io_map; |
| u32 t, delay = 0; |
| u32 reg; |
| int dcode; |
| |
| if (clk_hz <= 13000000) |
| return 0; |
| |
| t = 1000000000 / (clk_hz/1000); /* in picosecond */ |
| |
| /* clock settings */ |
| qspi_enter_mode(qspi, QSPI_DISABLE_MODE); |
| |
| if (timing->tclqv == 0) { |
| timing->tclqv = 8; |
| timing->tset = timing->thold = 2; |
| } |
| |
| delay = (timing->tclqv + timing->tset + 1) * 1000; |
| if (delay <= t) |
| reg = 0; /* sample point N1 */ |
| else |
| reg = QSPI_SMPR_FSPHS_MASK; /* sample point I1 */ |
| |
| if (timing->use_dtr && qspi->has_dtr) { |
| int ddr_point; |
| |
| delay -= t/2; |
| if (delay > 0) |
| ddr_point = (delay + t/8 - 1) / (t/8); |
| else |
| ddr_point = qspi->dtr_rx_delay; |
| |
| reg |= ddr_point << QSPI_SMPR_DDRSMP_SHIFT; |
| } else if (qspi->support_dqs && clk_hz > 52000000) { |
| /* |
| * Do not use QDS for DDR, since SDR/DDR can not share |
| * same delay code. |
| * If DQS enabled, must use sample point N/1, clear SMPR. |
| * |
| * delay step: 52ps |
| */ |
| delay = timing->tclqv * 1000 - t/2; |
| dcode = delay / 52; |
| if (!asr_qspi_config_dqs_clk(qspi, dcode)) |
| reg = 0; |
| } |
| |
| qspi_writel(qspi, reg, base + QSPI_SMPR); |
| reg = qspi_readl(qspi, base + QSPI_SMPR); |
| dev_info(qspi->dev, "QSPI_SMPR=0x%x t=%d\n", reg, t); |
| |
| /* set tx hold time */ |
| reg = 0x202; |
| if (timing->use_dtr && qspi->has_dtr) |
| reg |= qspi->dtr_tx_delay << 16; |
| qspi_writel(qspi, reg, base + QSPI_FLSHCR); |
| |
| reg = qspi_readl(qspi, base + QSPI_FLSHCR); |
| dev_info(qspi->dev, "QSPI_FLSHCR=0x%x, delay=%d\n", reg, delay); |
| |
| /* Module enabled */ |
| qspi_enter_mode(qspi, QSPI_NORMAL_MODE); |
| |
| return 0; |
| } |
| |
| static int asr_qspi_adjust_timing(struct spi_mem *mem, struct spi_mem_timing *timing) |
| { |
| struct asr_qspi *qspi = spi_controller_get_devdata(mem->spi->master); |
| int ret = 0; |
| |
| dev_notice(qspi->dev, "tclqv=%dns tset=%dns thold=%dns\n", |
| timing->tclqv, timing->tset, timing->thold); |
| |
| if (timing->max_hz > 0 && timing->max_hz < qspi->max_hz) |
| qspi->max_hz = timing->max_hz; |
| |
| __asr_qspi_adjust_timing(qspi, timing, qspi->max_hz); |
| |
| ret = clk_set_rate(qspi->clk, qspi->max_hz); |
| if (ret) { |
| dev_err(qspi->dev, "fail to set clk, ret:%d\n", ret); |
| return ret; |
| } |
| |
| dev_notice(qspi->dev, "bus clock %dHz, PMUap reg[0x%08x]:0x%08x\n", |
| qspi->max_hz, qspi->pmuap_reg, |
| qspi_readl(qspi, qspi->pmuap_addr)); |
| |
| return 0; |
| } |
| |
| #define ASR_QSPI_MAX_RETRY 3 |
| static int asr_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) |
| { |
| struct asr_qspi *qspi = spi_controller_get_devdata(mem->spi->master); |
| int ret, i; |
| |
| restart: |
| qspi->rst_protect = false; |
| |
| ret = __asr_qspi_exec_op(mem, op); |
| if (ret == -EAGAIN) { |
| /* |
| * For tx underrun error, reduce data length to be less |
| * than tx fifo size and try again. |
| */ |
| asr_qspi_adjust_tx_size(mem, 1); |
| asr_qspi_adjust_op_size(mem, (struct spi_mem_op *)op); |
| |
| i = 0; |
| do { |
| ret = __asr_qspi_exec_op(mem, op); |
| } while (ret == -EAGAIN && ++i < ASR_QSPI_MAX_RETRY); |
| |
| BUG_ON(ret); |
| |
| dev_dbg(qspi->dev, "pass after %dth retry.\n", i+1); |
| asr_qspi_adjust_tx_size(mem, 0); |
| } |
| |
| if (qspi->rst_protect) { |
| dev_info_ratelimited(qspi->dev, "retry for reset protect\n"); |
| goto restart; |
| } |
| |
| return ret; |
| } |
| |
| static int asr_qspi_host_init(struct asr_qspi *qspi) |
| { |
| void __iomem *base = qspi->io_map; |
| u32 reg; |
| |
| /* rest qspi */ |
| qspi_reset(qspi); |
| |
| /* clock settings */ |
| qspi_enter_mode(qspi, QSPI_DISABLE_MODE); |
| |
| /* Fix wirte failure issue*/ |
| reg = qspi_readl(qspi, base + QSPI_SOCCR); |
| reg &= ~0xFF; |
| reg |= 0x8; |
| qspi_writel(qspi, reg, base + QSPI_SOCCR); |
| |
| /* set the default source address QSPI_AMBA_BASE*/ |
| qspi_write_sfar(qspi, qspi->memmap_base); |
| qspi_writel(qspi, 0x0, base + QSPI_SFACR); |
| |
| /* config ahb read */ |
| qspi_init_ahbread(qspi, SEQID_LUT_AHBREAD_ID); |
| |
| /* set flash memory map */ |
| qspi_writel(qspi, qspi->sfa1ad & 0xfffffc00, base + QSPI_SFA1AD); |
| qspi_writel(qspi, qspi->sfa2ad & 0xfffffc00, base + QSPI_SFA2AD); |
| qspi_writel(qspi, qspi->sfb1ad & 0xfffffc00, base + QSPI_SFB1AD); |
| qspi_writel(qspi, qspi->sfb2ad & 0xfffffc00, base + QSPI_SFB2AD); |
| |
| /* ISD3FB, ISD2FB, ISD3FA, ISD2FA = 1; END_CFG=0x3 */ |
| reg = qspi_readl(qspi, base + QSPI_MCR); |
| reg |= QSPI_MCR_END_CFG_MASK | QSPI_MCR_ISD_MASK; |
| if (qspi->has_dtr) |
| reg |= QSPI_MCR_DDR_EN_MASK; |
| else |
| reg &= ~QSPI_MCR_DDR_EN_MASK; |
| qspi_writel(qspi, reg, base + QSPI_MCR); |
| |
| /* Module enabled */ |
| qspi_enter_mode(qspi, QSPI_NORMAL_MODE); |
| |
| /* Read using the IP Bus registers QSPI_RBDR0 to QSPI_RBDR31*/ |
| qspi_write_rbct(qspi, QSPI_RBCT_RXBRD_MASK); |
| |
| if (!qspi->cmd_interrupt) |
| asr_qspi_disable_interrupt(qspi, 0xffffffff); |
| |
| /* clear all interrupt status */ |
| qspi_writel(qspi, 0xffffffff, base + QSPI_FR); |
| |
| #ifdef ASR_DUMP_QSPI_REG |
| qspi_dump_reg(qspi); |
| #endif |
| return 0; |
| } |
| |
| static int asr_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) |
| { |
| struct spi_controller *ctrl = desc->mem->spi->master; |
| struct asr_qspi *qspi = spi_controller_get_devdata(ctrl); |
| struct spi_mem_op *op = &desc->info.op_tmpl; |
| |
| if (op->data.dir != SPI_MEM_DATA_IN || !qspi->ahb_read_enable) |
| return -ENOTSUPP; |
| |
| asr_qspi_prepare_lut(qspi, op, SEQID_LUT_AHBREAD_ID); |
| qspi->ahb_op = op; |
| |
| if (op->cmd.dtr || op->addr.dtr || op->data.dtr) |
| printk("enable dtr command 0x%x\n", op->cmd.opcode); |
| |
| return 0; |
| } |
| |
| static ssize_t asr_qspi_direct_read(struct spi_mem_dirmap_desc *desc, |
| u64 offs, size_t len, void *buf) |
| { |
| struct spi_controller *ctrl = desc->mem->spi->master; |
| struct asr_qspi *qspi = spi_controller_get_devdata(ctrl); |
| struct spi_mem_op op = desc->info.op_tmpl; |
| int err; |
| |
| /* Below check not need for ahb read, comment out */ |
| #if 0 |
| void __iomem *base = qspi->io_map; |
| u32 mask; |
| |
| mutex_lock(&qspi->lock); |
| |
| /* wait for controller being ready */ |
| mask = QSPI_SR_BUSY | QSPI_SR_IP_ACC_MASK | QSPI_SR_AHB_ACC_MASK; |
| err = asr_qspi_readl_poll_tout(base, base + QSPI_SR, mask, |
| QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR); |
| if (err) { |
| dev_err(qspi->dev, "controller not ready!\n"); |
| mutex_unlock(&qspi->lock); |
| return err; |
| } |
| |
| mutex_unlock(&qspi->lock); |
| #endif |
| |
| op.addr.val = desc->info.offset + offs; |
| op.data.buf.in = buf; |
| op.data.nbytes = len; |
| asr_qspi_adjust_op_size(desc->mem, &op); |
| |
| err = asr_qspi_ahb_read(qspi, &op); |
| if (err) |
| return err; |
| |
| return op.data.nbytes; |
| } |
| |
| static const struct spi_controller_mem_ops asr_qspi_mem_ops = { |
| .adjust_op_size = asr_qspi_adjust_op_size, |
| .adjust_timing = asr_qspi_adjust_timing, |
| .supports_op = asr_qspi_supports_op, |
| .exec_op = asr_qspi_exec_op, |
| .dirmap_create = asr_qspi_dirmap_create, |
| .dirmap_read = asr_qspi_direct_read, |
| }; |
| |
| static int asr_qspi_probe(struct platform_device *pdev) |
| { |
| struct spi_controller *ctlr; |
| struct device *dev = &pdev->dev; |
| struct device_node *np = dev->of_node; |
| struct asr_qspi *qspi; |
| struct resource *res; |
| |
| int ret = 0; |
| u32 qspi_bus_num = 0; |
| int host_irq = 0; |
| |
| ctlr = spi_alloc_master(&pdev->dev, sizeof(struct asr_qspi)); |
| if (!ctlr) |
| return -ENOMEM; |
| |
| ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD ; |
| qspi = spi_controller_get_devdata(ctlr); |
| qspi->dev = dev; |
| qspi->ctrl = ctlr; |
| |
| platform_set_drvdata(pdev, qspi); |
| |
| /* get qspi register base address */ |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-base"); |
| qspi->io_map = devm_ioremap_resource(dev, res); |
| if (IS_ERR(qspi->io_map)) { |
| ret = PTR_ERR(qspi->io_map); |
| goto err_put_ctrl; |
| } |
| qspi->io_phys = res->start; |
| |
| /* get qspi memory-map address */ |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-mmap"); |
| qspi->ahb_map = devm_ioremap_resource(dev, res); |
| if (IS_ERR(qspi->ahb_map)) { |
| ret = PTR_ERR(qspi->ahb_map); |
| goto err_put_ctrl; |
| } |
| |
| qspi->memmap_base = res->start; |
| qspi->memmap_size = resource_size(res); |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-sfa1ad", &qspi->sfa1ad)) |
| qspi->sfa1ad = QSPI_FLASH_A1_TOP; |
| else |
| qspi->sfa1ad += qspi->memmap_base; |
| if (of_property_read_u32(dev->of_node, "asr,qspi-sfa2ad", &qspi->sfa2ad)) |
| qspi->sfa2ad = QSPI_FLASH_A2_TOP; |
| else |
| qspi->sfa2ad += qspi->sfa1ad; |
| if (of_property_read_u32(dev->of_node, "asr,qspi-sfb1ad", &qspi->sfb1ad)) |
| qspi->sfb1ad = QSPI_FLASH_B1_TOP; |
| else |
| qspi->sfb1ad = qspi->sfa2ad; |
| if (of_property_read_u32(dev->of_node, "asr,qspi-sfb2ad", &qspi->sfb2ad)) |
| qspi->sfb2ad = QSPI_FLASH_B2_TOP; |
| else |
| qspi->sfb2ad += qspi->sfb1ad; |
| |
| dev_notice(dev, "asr_qspi_probe:memmap base:0x%08x, memmap size:0x%x\n", |
| qspi->memmap_base, qspi->memmap_size); |
| |
| qspi->sram.pool = of_gen_pool_get(dev->of_node, "asr,qspi-sram", 0); |
| if (qspi->sram.pool) { |
| qspi->sram.virt = |
| (void __iomem *)gen_pool_dma_alloc( |
| qspi->sram.pool, SZ_4K, &qspi->sram.dma); |
| dev_notice(dev, "use sram as tx buf, virt=0x%x phy=0x%x\n", |
| (unsigned)qspi->sram.virt, (unsigned)qspi->sram.dma); |
| } |
| |
| host_irq = platform_get_irq(pdev, 0); |
| if (host_irq < 0) { |
| dev_err(dev, "invalid host irq:%d\n", host_irq); |
| goto err_put_ctrl; |
| } |
| ret = devm_request_irq(dev, host_irq, asr_qspi_irq_handler, |
| 0, pdev->name, qspi); |
| if (ret) { |
| dev_err(dev, "failed to request irq:%d\n", ret); |
| goto err_put_ctrl; |
| } |
| init_completion(&qspi->cmd_completion); |
| dev_notice(qspi->dev, "host_irq:%d\n", host_irq); |
| |
| host_irq = platform_get_irq(pdev, 1); |
| if (host_irq >= 0) { |
| ret = devm_request_irq(dev, host_irq, asr_qspi_reset_handler, |
| 0, pdev->name, qspi); |
| if (ret) { |
| dev_err(dev, "failed to request irq:%d\n", ret); |
| goto err_put_ctrl; |
| } |
| |
| dev_notice(qspi->dev, "reset irq:%d\n", host_irq); |
| } |
| |
| /* map QSPI PMUap register address */ |
| if (of_property_read_u32(dev->of_node, "asr,qspi-pmuap-reg", &qspi->pmuap_reg)) { |
| qspi->pmuap_reg = PMUA_QSPI_CLK_RES_CTRL; |
| } |
| qspi->pmuap_addr = ioremap(qspi->pmuap_reg, 4); |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-freq", &qspi->max_hz)) { |
| qspi->max_hz = ASR_QSPI_DEFAULT_CLK_FREQ; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-rx-buf", &qspi->rx_buf_size)) { |
| qspi->rx_buf_size = QSPI_RX_BUFF_MAX; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-tx-buf", &qspi->tx_buf_size)) { |
| qspi->tx_buf_size = QSPI_TX_BUFF_MAX; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-ahb-buf", &qspi->ahb_buf_size)) { |
| qspi->ahb_buf_size = QSPI_AHB_BUFF_MAX_SIZE; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-ahb-enable", &qspi->ahb_read_enable)) { |
| qspi->ahb_read_enable = 1; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-interrupt", &qspi->cmd_interrupt)) { |
| qspi->cmd_interrupt = 1; |
| } |
| |
| /* RX not use dma default, read from ahb directly show better performance */ |
| if (of_property_read_u32(dev->of_node, "asr,en-rx-dma", &qspi->rx_dma_enable)) { |
| qspi->rx_dma_enable = 0; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,en-tx-dma", &qspi->tx_dma_enable)) { |
| qspi->tx_dma_enable = 1; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-support-dtr", &qspi->has_dtr)) { |
| qspi->has_dtr = 0; |
| } else { |
| if (of_property_read_u32(dev->of_node, "asr,qspi-dtr-tx-delay", &qspi->dtr_tx_delay)) |
| qspi->dtr_tx_delay = 1; |
| if (of_property_read_u32(dev->of_node, "asr,qspi-dtr-rx-delay", &qspi->dtr_rx_delay)) |
| qspi->dtr_rx_delay = 0; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-support-dqs", &qspi->support_dqs)) |
| qspi->support_dqs = 0; |
| if (cpu_is_asr1903_a0() || cpu_is_asr1903_z1()) |
| qspi->support_dqs = 0; |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-endian-xchg", &qspi->endian_xchg)) { |
| qspi->endian_xchg = 0; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-cs", &qspi->cs_selected)) { |
| qspi->cs_selected = QSPI_DEFAULT_CS; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "asr,qspi-lpm-qos", &qspi->lpm_qos)) { |
| qspi->lpm_qos = PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE; |
| } |
| |
| asr_qspi_prepare_dma(qspi); |
| mutex_init(&qspi->lock); |
| |
| /* set the qspi device default index */ |
| if (of_property_read_u32(dev->of_node, "asr,qspi-id", &qspi_bus_num)) |
| ctlr->bus_num = -1; |
| else |
| ctlr->bus_num = qspi_bus_num; |
| ctlr->num_chipselect = 1; |
| ctlr->mem_ops = &asr_qspi_mem_ops; |
| |
| dev_notice(dev, "asr_qspi_probe: rx_buf_size:%d, tx_buf_size:%d\n", |
| qspi->rx_buf_size, qspi->tx_buf_size); |
| dev_notice(dev, "asr_qspi_probe: ahb_buf_size:%d, ahb_read:%d\n", |
| qspi->ahb_buf_size, qspi->ahb_read_enable); |
| |
| if (qspi->tx_dma_enable) |
| qspi->tx_unit_size = SZ_4K; |
| else |
| qspi->tx_unit_size = qspi->tx_buf_size; |
| |
| if (qspi->ahb_read_enable) |
| qspi->rx_unit_size = SZ_4K; |
| else |
| qspi->rx_unit_size = qspi->rx_buf_size; |
| |
| /* config mfp */ |
| qspi_config_mfp(qspi); |
| /* set PMUap */ |
| qspi_set_func_clk(qspi, 13000000); |
| asr_qspi_host_init(qspi); |
| dev_info(qspi->dev, "AHB buf size: %d\n", qspi->ahb_buf_size); |
| dev_notice(qspi->dev, "qspi host init done.\n"); |
| |
| qspi->pm_qos_req.name = pdev->name; |
| ctlr->auto_runtime_pm = true; |
| pm_qos_add_request(&qspi->pm_qos_req, PM_QOS_CPUIDLE_BLOCK, |
| PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); |
| |
| qspi->pm_ddr_qos.name = pdev->name; |
| pm_qos_add_request(&qspi->pm_ddr_qos, PM_QOS_DDR_DEVFREQ_MIN, |
| PM_QOS_DEFAULT_VALUE); |
| |
| pm_runtime_get_noresume(&pdev->dev); |
| pm_runtime_use_autosuspend(&pdev->dev); |
| pm_runtime_set_autosuspend_delay(&pdev->dev, QSPI_AUTOSUSPEND_TIMEOUT); |
| pm_runtime_set_active(&pdev->dev); |
| pm_suspend_ignore_children(&pdev->dev, 0); |
| pm_runtime_enable(&pdev->dev); |
| |
| /* get qos */ |
| pm_qos_update_request(&qspi->pm_qos_req, qspi->lpm_qos); |
| ctlr->dev.of_node = np; |
| ret = spi_register_controller(ctlr); |
| if (ret) |
| goto err_destroy_mutex; |
| |
| pm_runtime_put_autosuspend(&pdev->dev); |
| |
| return 0; |
| |
| err_destroy_mutex: |
| pm_runtime_disable(&pdev->dev); |
| pm_runtime_put_noidle(&pdev->dev); |
| pm_qos_remove_request(&qspi->pm_qos_req); |
| pm_qos_remove_request(&qspi->pm_ddr_qos); |
| |
| mutex_destroy(&qspi->lock); |
| iounmap(qspi->pmuap_addr); |
| |
| err_put_ctrl: |
| spi_controller_put(ctlr); |
| |
| dev_err(dev, "ASR QSPI probe failed\n"); |
| return ret; |
| } |
| |
| static int asr_qspi_remove(struct platform_device *pdev) |
| { |
| struct asr_qspi *qspi = platform_get_drvdata(pdev); |
| |
| pm_runtime_get_sync(&pdev->dev); |
| |
| /* set disable mode */ |
| qspi_writel(qspi, QSPI_MCR_MDIS_MASK, qspi->io_map + QSPI_MCR); |
| qspi_writel(qspi, 0x0, qspi->io_map + QSPI_RSER); |
| |
| pm_runtime_disable(&pdev->dev); |
| pm_runtime_put_noidle(&pdev->dev); |
| pm_qos_remove_request(&qspi->pm_qos_req); |
| pm_qos_remove_request(&qspi->pm_ddr_qos); |
| |
| if (qspi->tx_dma) |
| dma_release_channel(qspi->tx_dma); |
| if (qspi->rx_dma) |
| dma_release_channel(qspi->rx_dma); |
| |
| mutex_destroy(&qspi->lock); |
| iounmap(qspi->pmuap_addr); |
| |
| clk_disable_unprepare(qspi->clk); |
| clk_disable_unprepare(qspi->bus_clk); |
| |
| if (qspi->sram.pool) |
| gen_pool_free(qspi->sram.pool, |
| (unsigned long)qspi->sram.virt, SZ_4K); |
| return 0; |
| } |
| |
| static void asr_qspi_default_setup(struct asr_qspi *qspi) |
| { |
| struct spi_mem_op *op = qspi->ahb_op; |
| int i; |
| |
| asr_qspi_host_init(qspi); |
| |
| for (i = 0; i < QSPI_MAX_SEQ_NUM; i++) |
| qspi->seq_opcode[i] = 0; |
| |
| if (op) |
| asr_qspi_prepare_lut(qspi, op, SEQID_LUT_AHBREAD_ID); |
| |
| return; |
| } |
| |
| #ifdef CONFIG_PM_SLEEP |
| static int asr_qspi_suspend(struct device *dev) |
| { |
| int ret; |
| u32 sr; |
| struct asr_qspi *qspi = dev_get_drvdata(dev); |
| |
| pm_runtime_get_sync(qspi->dev); |
| |
| sr = qspi_readl(qspi, qspi->io_map + QSPI_SR); |
| if (sr & QSPI_SR_BUSY) { |
| dev_err(dev, "qspi busy with ongoing cmd\n"); |
| return -EBUSY; |
| } |
| |
| ret = pm_runtime_force_suspend(dev); |
| if (ret) { |
| dev_err(dev, "failed to suspend(ret:%d)\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int asr_qspi_resume(struct device *dev) |
| { |
| struct asr_qspi *qspi = dev_get_drvdata(dev); |
| int ret; |
| |
| ret = pm_runtime_force_resume(dev); |
| if (ret) { |
| dev_err(dev, "failed to resume(ret:%d)\n", ret); |
| return ret; |
| } |
| |
| /* reset qspi via bus reset */ |
| clk_disable_unprepare(qspi->bus_clk); |
| udelay(1000); |
| clk_prepare_enable(qspi->bus_clk); |
| asr_qspi_default_setup(qspi); |
| |
| pm_runtime_mark_last_busy(dev); |
| pm_runtime_put_autosuspend(dev); |
| |
| return 0; |
| } |
| #endif |
| |
| #ifdef CONFIG_PM |
| static int asr_qspi_runtime_suspend(struct device *dev) |
| { |
| u32 sr; |
| struct asr_qspi *qspi = dev_get_drvdata(dev); |
| |
| mutex_lock(&qspi->lock); |
| sr = qspi_readl(qspi, qspi->io_map + QSPI_SR); |
| if (sr & QSPI_SR_BUSY) { |
| dev_err(dev, "qspi busy with ongoing cmd\n"); |
| mutex_unlock(&qspi->lock); |
| return -EBUSY; |
| } |
| qspi_enter_mode(qspi, QSPI_DISABLE_MODE); |
| mutex_unlock(&qspi->lock); |
| |
| clk_disable_unprepare(qspi->clk); |
| |
| /* put qos */ |
| pm_qos_update_request(&qspi->pm_qos_req, PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); |
| |
| return 0; |
| } |
| |
| static int asr_qspi_runtime_resume(struct device *dev) |
| { |
| struct asr_qspi *qspi = dev_get_drvdata(dev); |
| |
| /* get qos */ |
| pm_qos_update_request(&qspi->pm_qos_req, qspi->lpm_qos); |
| |
| clk_prepare_enable(qspi->clk); |
| qspi_enter_mode(qspi, QSPI_NORMAL_MODE); |
| |
| return 0; |
| } |
| #endif |
| |
| static const struct dev_pm_ops asr_qspi_pmops = { |
| SET_SYSTEM_SLEEP_PM_OPS(asr_qspi_suspend, asr_qspi_resume) |
| SET_RUNTIME_PM_OPS(asr_qspi_runtime_suspend, |
| asr_qspi_runtime_resume, NULL) |
| }; |
| |
| static const struct of_device_id asr_qspi_dt_ids[] = { |
| { .compatible = "asr,qspi", }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, asr_qspi_dt_ids); |
| |
| static struct platform_driver asr_qspi_driver = { |
| .driver = { |
| .name = "asr-qspi", |
| .of_match_table = asr_qspi_dt_ids, |
| .pm = &asr_qspi_pmops, |
| }, |
| .probe = asr_qspi_probe, |
| .remove = asr_qspi_remove, |
| }; |
| module_platform_driver(asr_qspi_driver); |
| |
| MODULE_DESCRIPTION("ASR QSPI Host Controller Driver"); |
| MODULE_AUTHOR("ASR Micro"); |
| MODULE_LICENSE("GPL v2"); |