2022-05-12 10:05:32 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* (C) Copyright 2018 Xilinx
|
|
|
|
*
|
|
|
|
* Cadence QSPI controller DMA operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <clk.h>
|
|
|
|
#include <common.h>
|
|
|
|
#include <memalign.h>
|
|
|
|
#include <wait_bit.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/gpio.h>
|
|
|
|
#include <asm/cache.h>
|
|
|
|
#include <cpu_func.h>
|
|
|
|
#include <zynqmp_firmware.h>
|
|
|
|
#include <asm/arch/hardware.h>
|
|
|
|
#include "cadence_qspi.h"
|
|
|
|
#include <dt-bindings/power/xlnx-versal-power.h>
|
|
|
|
|
|
|
|
#define CMD_4BYTE_READ 0x13
|
|
|
|
#define CMD_4BYTE_FAST_READ 0x0C
|
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
|
2022-05-12 10:05:32 +00:00
|
|
|
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) {
|
2022-05-12 10:05:34 +00:00
|
|
|
cadence_qspi_apb_enable_linear_mode(false);
|
2022-08-24 11:38:47 +00:00
|
|
|
reg = readl(priv->regbase + CQSPI_REG_CONFIG);
|
2022-05-12 10:05:32 +00:00
|
|
|
reg |= CQSPI_REG_CONFIG_ENBL_DMA;
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(reg, priv->regbase + CQSPI_REG_CONFIG);
|
2022-05-12 10:05:32 +00:00
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(bytes_to_dma, priv->regbase + CQSPI_REG_INDIRECTRDBYTES);
|
2022-05-12 10:05:32 +00:00
|
|
|
|
|
|
|
writel(CQSPI_DFLT_INDIR_TRIG_ADDR_RANGE,
|
2022-08-24 11:38:47 +00:00
|
|
|
priv->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE);
|
2022-05-12 10:05:32 +00:00
|
|
|
writel(CQSPI_DFLT_DMA_PERIPH_CFG,
|
2022-08-24 11:38:47 +00:00
|
|
|
priv->regbase + CQSPI_REG_DMA_PERIPH_CFG);
|
|
|
|
writel((unsigned long)rxbuf, priv->regbase +
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_DMA_DST_ADDR_REG);
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(priv->trigger_address, priv->regbase +
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_DMA_SRC_RD_ADDR_REG);
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(bytes_to_dma, priv->regbase +
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_DMA_DST_SIZE_REG);
|
|
|
|
flush_dcache_range((unsigned long)rxbuf,
|
|
|
|
(unsigned long)rxbuf + bytes_to_dma);
|
|
|
|
writel(CQSPI_DFLT_DST_CTRL_REG_VAL,
|
2022-08-24 11:38:47 +00:00
|
|
|
priv->regbase + CQSPI_DMA_DST_CTRL_REG);
|
2022-05-12 10:05:32 +00:00
|
|
|
|
|
|
|
/* Start the indirect read transfer */
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(CQSPI_REG_INDIRECTRD_START, priv->regbase +
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_REG_INDIRECTRD);
|
|
|
|
/* Wait for dma to complete transfer */
|
2022-08-24 11:38:47 +00:00
|
|
|
ret = cadence_qspi_apb_wait_for_dma_cmplt(priv);
|
2022-05-12 10:05:32 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Clear indirect completion status */
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(CQSPI_REG_INDIRECTRD_DONE, priv->regbase +
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_REG_INDIRECTRD);
|
|
|
|
rxbuf += bytes_to_dma;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx_rem) {
|
2022-08-24 11:38:47 +00:00
|
|
|
reg = readl(priv->regbase + CQSPI_REG_CONFIG);
|
2022-05-12 10:05:32 +00:00
|
|
|
reg &= ~CQSPI_REG_CONFIG_ENBL_DMA;
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(reg, priv->regbase + CQSPI_REG_CONFIG);
|
2022-05-12 10:05:32 +00:00
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
reg = readl(priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
|
2022-05-12 10:05:32 +00:00
|
|
|
reg += bytes_to_dma;
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(reg, priv->regbase + CQSPI_REG_CMDADDRESS);
|
2022-05-12 10:05:32 +00:00
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
addr_bytes = readl(priv->regbase + CQSPI_REG_SIZE) &
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_REG_SIZE_ADDRESS_MASK;
|
|
|
|
|
|
|
|
opcode = CMD_4BYTE_FAST_READ;
|
|
|
|
dummy_cycles = 8;
|
|
|
|
writel((dummy_cycles << CQSPI_REG_RD_INSTR_DUMMY_LSB) | opcode,
|
2022-08-24 11:38:47 +00:00
|
|
|
priv->regbase + CQSPI_REG_RD_INSTR);
|
2022-05-12 10:05:32 +00:00
|
|
|
|
|
|
|
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);
|
2022-08-24 11:38:47 +00:00
|
|
|
dummy_cycles = (readl(priv->regbase + CQSPI_REG_RD_INSTR) >>
|
2022-05-12 10:05:32 +00:00
|
|
|
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);
|
2022-08-24 11:38:47 +00:00
|
|
|
ret = cadence_qspi_apb_exec_flash_cmd(priv->regbase, reg);
|
2022-05-12 10:05:32 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
data = readl(priv->regbase + CQSPI_REG_CMDREADDATALOWER);
|
2022-05-12 10:05:32 +00:00
|
|
|
memcpy(rxbuf, &data, rx_rem);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_priv *priv)
|
2022-05-12 10:05:32 +00:00
|
|
|
{
|
|
|
|
u32 timeout = CQSPI_DMA_TIMEOUT;
|
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
while (!(readl(priv->regbase + CQSPI_DMA_DST_I_STS_REG) &
|
2022-05-12 10:05:32 +00:00
|
|
|
CQSPI_DMA_DST_I_STS_DONE) && timeout--)
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
if (!timeout) {
|
|
|
|
printf("DMA timeout\n");
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2022-08-24 11:38:47 +00:00
|
|
|
writel(readl(priv->regbase + CQSPI_DMA_DST_I_STS_REG),
|
|
|
|
priv->regbase + CQSPI_DMA_DST_I_STS_REG);
|
2022-05-12 10:05:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2022-05-12 10:05:33 +00:00
|
|
|
|
|
|
|
#if defined(CONFIG_DM_GPIO)
|
2022-08-24 11:38:45 +00:00
|
|
|
int cadence_qspi_versal_flash_reset(struct udevice *dev)
|
2022-05-12 10:05:33 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
2022-08-24 11:38:45 +00:00
|
|
|
int cadence_qspi_versal_flash_reset(struct udevice *dev)
|
2022-05-12 10:05:33 +00:00
|
|
|
{
|
|
|
|
/* 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
|
2022-05-12 10:05:34 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|