blob: c756a854bce6a255b06faa539486ff790db2ab6d [file] [log] [blame]
// 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
int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat,
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(plat->regbase + CQSPI_REG_CONFIG);
reg |= CQSPI_REG_CONFIG_ENBL_DMA;
writel(reg, plat->regbase + CQSPI_REG_CONFIG);
writel(bytes_to_dma, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
writel(CQSPI_DFLT_INDIR_TRIG_ADDR_RANGE,
plat->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE);
writel(CQSPI_DFLT_DMA_PERIPH_CFG,
plat->regbase + CQSPI_REG_DMA_PERIPH_CFG);
writel((unsigned long)rxbuf, plat->regbase +
CQSPI_DMA_DST_ADDR_REG);
writel(plat->trigger_address, plat->regbase +
CQSPI_DMA_SRC_RD_ADDR_REG);
writel(bytes_to_dma, plat->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,
plat->regbase + CQSPI_DMA_DST_CTRL_REG);
/* Start the indirect read transfer */
writel(CQSPI_REG_INDIRECTRD_START, plat->regbase +
CQSPI_REG_INDIRECTRD);
/* Wait for dma to complete transfer */
ret = cadence_qspi_apb_wait_for_dma_cmplt(plat);
if (ret)
return ret;
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTRD_DONE, plat->regbase +
CQSPI_REG_INDIRECTRD);
rxbuf += bytes_to_dma;
}
if (rx_rem) {
reg = readl(plat->regbase + CQSPI_REG_CONFIG);
reg &= ~CQSPI_REG_CONFIG_ENBL_DMA;
writel(reg, plat->regbase + CQSPI_REG_CONFIG);
reg = readl(plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
reg += bytes_to_dma;
writel(reg, plat->regbase + CQSPI_REG_CMDADDRESS);
addr_bytes = readl(plat->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,
plat->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(plat->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(plat->regbase, reg);
if (ret)
return ret;
data = readl(plat->regbase + CQSPI_REG_CMDREADDATALOWER);
memcpy(rxbuf, &data, rx_rem);
}
return 0;
}
int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_plat *plat)
{
u32 timeout = CQSPI_DMA_TIMEOUT;
while (!(readl(plat->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(plat->regbase + CQSPI_DMA_DST_I_STS_REG),
plat->regbase + CQSPI_DMA_DST_I_STS_REG);
return 0;
}
#if defined(CONFIG_DM_GPIO)
int cadence_qspi_versal_flash_reset(struct udevice *dev)
{
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
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);
}
}