// SPDX-License-Identifier: GPL-2.0 /* * (C) Copyright 2018 Xilinx * * Cadence QSPI controller DMA operations */ #include #include #include #include #include #include #include #include #include #include #include "cadence_qspi.h" #include #define CMD_4BYTE_READ 0x13 #define CMD_4BYTE_FAST_READ 0x0C int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, const struct spi_mem_op *op) { u32 reg, ret, rx_rem, n_rx, bytes_to_dma, data; u8 opcode, addr_bytes, *rxbuf, dummy_cycles; n_rx = op->data.nbytes; rxbuf = op->data.buf.in; rx_rem = n_rx % 4; bytes_to_dma = n_rx - rx_rem; if (bytes_to_dma) { cadence_qspi_apb_enable_linear_mode(false); reg = readl(priv->regbase + CQSPI_REG_CONFIG); reg |= CQSPI_REG_CONFIG_ENBL_DMA; writel(reg, priv->regbase + CQSPI_REG_CONFIG); writel(bytes_to_dma, priv->regbase + CQSPI_REG_INDIRECTRDBYTES); writel(CQSPI_DFLT_INDIR_TRIG_ADDR_RANGE, priv->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE); writel(CQSPI_DFLT_DMA_PERIPH_CFG, priv->regbase + CQSPI_REG_DMA_PERIPH_CFG); writel((unsigned long)rxbuf, priv->regbase + CQSPI_DMA_DST_ADDR_REG); writel(priv->trigger_address, priv->regbase + CQSPI_DMA_SRC_RD_ADDR_REG); writel(bytes_to_dma, priv->regbase + CQSPI_DMA_DST_SIZE_REG); flush_dcache_range((unsigned long)rxbuf, (unsigned long)rxbuf + bytes_to_dma); writel(CQSPI_DFLT_DST_CTRL_REG_VAL, priv->regbase + CQSPI_DMA_DST_CTRL_REG); /* Start the indirect read transfer */ writel(CQSPI_REG_INDIRECTRD_START, priv->regbase + CQSPI_REG_INDIRECTRD); /* Wait for dma to complete transfer */ ret = cadence_qspi_apb_wait_for_dma_cmplt(priv); if (ret) return ret; /* Clear indirect completion status */ writel(CQSPI_REG_INDIRECTRD_DONE, priv->regbase + CQSPI_REG_INDIRECTRD); rxbuf += bytes_to_dma; } if (rx_rem) { reg = readl(priv->regbase + CQSPI_REG_CONFIG); reg &= ~CQSPI_REG_CONFIG_ENBL_DMA; writel(reg, priv->regbase + CQSPI_REG_CONFIG); reg = readl(priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR); reg += bytes_to_dma; writel(reg, priv->regbase + CQSPI_REG_CMDADDRESS); addr_bytes = readl(priv->regbase + CQSPI_REG_SIZE) & CQSPI_REG_SIZE_ADDRESS_MASK; opcode = CMD_4BYTE_FAST_READ; dummy_cycles = 8; writel((dummy_cycles << CQSPI_REG_RD_INSTR_DUMMY_LSB) | opcode, priv->regbase + CQSPI_REG_RD_INSTR); reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); reg |= (addr_bytes & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); dummy_cycles = (readl(priv->regbase + CQSPI_REG_RD_INSTR) >> CQSPI_REG_RD_INSTR_DUMMY_LSB) & CQSPI_REG_RD_INSTR_DUMMY_MASK; reg |= (dummy_cycles & CQSPI_REG_CMDCTRL_DUMMY_MASK) << CQSPI_REG_CMDCTRL_DUMMY_LSB; reg |= (((rx_rem - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) << CQSPI_REG_CMDCTRL_RD_BYTES_LSB); ret = cadence_qspi_apb_exec_flash_cmd(priv->regbase, reg); if (ret) return ret; data = readl(priv->regbase + CQSPI_REG_CMDREADDATALOWER); memcpy(rxbuf, &data, rx_rem); } return 0; } int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_priv *priv) { u32 timeout = CQSPI_DMA_TIMEOUT; while (!(readl(priv->regbase + CQSPI_DMA_DST_I_STS_REG) & CQSPI_DMA_DST_I_STS_DONE) && timeout--) udelay(1); if (!timeout) { printf("DMA timeout\n"); return -ETIMEDOUT; } writel(readl(priv->regbase + CQSPI_DMA_DST_I_STS_REG), priv->regbase + CQSPI_DMA_DST_I_STS_REG); return 0; } #if defined(CONFIG_DM_GPIO) int cadence_qspi_versal_flash_reset(struct udevice *dev) { #ifndef CONFIG_ARCH_VERSAL_NET struct gpio_desc gpio; u32 reset_gpio; int ret; /* request gpio and set direction as output set to 1 */ ret = gpio_request_by_name(dev, "reset-gpios", 0, &gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); if (ret) { printf("%s: unable to reset ospi flash device", __func__); return ret; } reset_gpio = PMIO_NODE_ID_BASE + gpio.offset; /* Request for pin */ xilinx_pm_request(PM_PINCTRL_REQUEST, reset_gpio, 0, 0, 0, NULL); /* Enable hysteresis in cmos receiver */ xilinx_pm_request(PM_PINCTRL_CONFIG_PARAM_SET, reset_gpio, PM_PINCTRL_CONFIG_SCHMITT_CMOS, PM_PINCTRL_INPUT_TYPE_SCHMITT, 0, NULL); /* Disable Tri-state */ xilinx_pm_request(PM_PINCTRL_CONFIG_PARAM_SET, reset_gpio, PM_PINCTRL_CONFIG_TRI_STATE, PM_PINCTRL_TRI_STATE_DISABLE, 0, NULL); udelay(1); /* Set value 0 to pin */ dm_gpio_set_value(&gpio, 0); udelay(1); /* Set value 1 to pin */ dm_gpio_set_value(&gpio, 1); udelay(1); #endif return 0; } #else int cadence_qspi_versal_flash_reset(struct udevice *dev) { /* CRP WPROT */ writel(0, WPROT_CRP); /* GPIO Reset */ writel(0, RST_GPIO); /* disable IOU write protection */ writel(0, WPROT_LPD_MIO); /* set direction as output */ writel((readl(BOOT_MODE_DIR) | BIT(FLASH_RESET_GPIO)), BOOT_MODE_POR_0); /* Data output enable */ writel((readl(BOOT_MODE_OUT) | BIT(FLASH_RESET_GPIO)), BOOT_MODE_POR_1); /* IOU SLCR write enable */ writel(0, WPROT_PMC_MIO); /* set MIO as GPIO */ writel(0x60, MIO_PIN_12); /* Set value 1 to pin */ writel((readl(BANK0_OUTPUT) | BIT(FLASH_RESET_GPIO)), BANK0_OUTPUT); udelay(10); /* Disable Tri-state */ writel((readl(BANK0_TRI) & ~BIT(FLASH_RESET_GPIO)), BANK0_TRI); udelay(1); /* Set value 0 to pin */ writel((readl(BANK0_OUTPUT) & ~BIT(FLASH_RESET_GPIO)), BANK0_OUTPUT); udelay(10); /* Set value 1 to pin */ writel((readl(BANK0_OUTPUT) | BIT(FLASH_RESET_GPIO)), BANK0_OUTPUT); udelay(10); return 0; } #endif void cadence_qspi_apb_enable_linear_mode(bool enable) { if (CONFIG_IS_ENABLED(ZYNQMP_FIRMWARE)) { if (enable) /* ahb read mode */ xilinx_pm_request(PM_IOCTL, PM_DEV_OSPI, IOCTL_OSPI_MUX_SELECT, PM_OSPI_MUX_SEL_LINEAR, 0, NULL); else /* DMA mode */ xilinx_pm_request(PM_IOCTL, PM_DEV_OSPI, IOCTL_OSPI_MUX_SELECT, PM_OSPI_MUX_SEL_DMA, 0, NULL); } else { if (enable) writel(readl(VERSAL_AXI_MUX_SEL) | VERSAL_OSPI_LINEAR_MODE, VERSAL_AXI_MUX_SEL); else writel(readl(VERSAL_AXI_MUX_SEL) & ~VERSAL_OSPI_LINEAR_MODE, VERSAL_AXI_MUX_SEL); } }